I make slideshow width 4 photos that appear with opacity: 1 and z-index: 2, and I could make it run automatically, but to control it, not yet and this is my js code with some of jquery:
$(document).ready(function() {
var i = 0
function next() {
move(i++);
if (i === 4) {
i = 0
}
console.log("first i = " + i)
};
setInterval(next, 2000);
function move(n) {
var images = document.querySelectorAll('img')
var img = images[n]
$(img).addClass('showSlide')
$(img).removeClass('hideSlide')
$(img).siblings(".img").addClass('hideSlide')
}
$('.next').click(
() => {
if (i === 3) {
i = 0
};
move(i++);
console.log("next i = " + i)
}
)
$('.previous').click(
() => {
if (i === 0) {
i = 3
};
move(i--);
console.log("previous i = " + i)
}
)})
my automatic slide work but when I click the next or the previous button the slide do not continue from the last position ,and my HTML code is :
<div class="container">
<button class="next">next</button>
<button class="previous">previous</button>
<img class="img" src="gallery-img7.jpg" alt="">
<img class="img" src="gallery-img2.jpg" alt="">
<img class="img" src="gallery-img8.jpg" alt="">
<img class="img" src="gallery-img3.jpg" alt="">
</div>
I think the way you are handling increment and decriment might be the issue? This is a good use case for modulo %. I also cleared and reset the interval after button click to get the same interval on the newly shown image. Here's an example that seems to work as your intending:
$(document).ready(function() {
var i = 0
function next() {
move(i++);
};
let nextInterval = setInterval(next, 2000);
function move(n) {
clearInterval(nextInterval)
nextInterval = setInterval(next, 2000);
n = i%4;
var images = document.querySelectorAll('img')
var img = images[n]
$(img).addClass('showSlide')
$(img).removeClass('hideSlide')
$(img).siblings(".img").addClass('hideSlide')
}
$('.next').click(
() => {
move(i++);
}
)
$('.previous').click(
() => {
move(i--);
}
)})
.img{
display:block;
width:100px;
height:100px;
}
.showSlide{
display:block;
}
.hideSlide{
display:none;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="container">
<button class="next">next</button>
<button class="previous">previous</button>
<img class="img" src="https://via.placeholder.com/100x100.png?text=1" alt="">
<img class="img hideSlide" src="https://via.placeholder.com/100x100.png?text=2" alt="">
<img class="img hideSlide" src="https://via.placeholder.com/100x100.png?text=3" alt="">
<img class="img hideSlide" src="https://via.placeholder.com/100x100.png?text=4" alt="">
</div>
I think your code to trigger the next and previous clicks is basically working, the Interval function you are running is never interrupted by your button clicks, so the slideshow continues to cycle around.
Your classes to show and hide may not be attaching to the DOM properly either. I find it's a good practice to attach less specific classes before I attach a specific one, i.e. blanket hide all slides and then show the selected slide.
Another technique that I think is helpful is to try and figure out the manual user interaction first and then base my automation on it. I've worked up an alteration of the code you posted, where the slides 'slide' themselves by triggering the next action, similarly to how a user would.
So the slideshow should start itself on page load by use of a setInterval being declared. That setInterval is interrupted when the user moves the mouse into the slideshow area - this way the button will control the active/shown slide. If you move the mouse off or away from the slideshow container the setInterval is allowed to kick in again, and the slides should cycle around automatically.
$(document).ready(function() {
var i = 0;
function move(n) {
var images = document.querySelectorAll('img');
var img = images[n];
$(img).siblings(".img").addClass('hideSlide');
$(img).siblings(".img").removeClass('showSlide');
$(img).addClass('showSlide');
$(img).removeClass('hideSlide');
}
var next = setInterval(autoRotate, 2000);
function autoRotate() {
$('.next').trigger('click');
}
$('.container').on('mouseenter', function() {
clearInterval(next);
});
$('.container').on('mouseleave', function() {
next = setInterval(autoRotate, 2000);
});
$('.next').click(() => {
if (i === 3) {
i = 0;
} else {
i++;
}
move(i);
});
$('.previous').click(() => {
if (i === 0) {
i = 3;
} else {
i--;
}
move(i);
});
})
Related
Goal:
Photo slider where you can toggle between different subreddits and view recent photos which have been posted.
Issue:
When you select one subreddit (via dedicated button) and scroll through a few photos (forward and/or back) all works fine, however if you then choose a different subreddit (via dedicated button) and scroll through a few photos (forward and/or back) the photos from the prior/first subreddit you previously selected flicker in and out at seemingly random intervals. I have watched console while this happens and src url for whatever photo that flickers in/out never enters the img element (unless its happening too fast to catch), instead the src url goes directly to the expected image. Also, I am console logging the array of image urls as the sub is selected via click and there is no abnormalities (all the photo links in the array are from the correct subreddit and in the correct order).
What I have tried:Moving the empty array var into the loop itself and the if statement, moving the counter variable into the functions themselves, as well as wrapping the entire click function in a separate function. None of these attempts made any difference.
JS:
function reply_click(clicked_id) {
var trys = [];
var title = document.getElementById("red-sub");
title.innerHTML = clicked_id;
$.getJSON("https://www.reddit.com/r/"+ clicked_id +"/.json", function(result){
for (var i = 0; i < result.data.children.length; i++) {
var imagesOnly = result.data.children[i].data;
if(imagesOnly.thumbnail !== 'self' && imagesOnly.post_hint === 'image'){
var items = result.data.children[i].data.url;
trys.push(items);
console.log(items);
var s = 0; // Start Point
function setImage(){
document.slide.src = trys[s];
}
setImage();
function changeImg(){
// Check If Index Is Under Max
if(s < trys.length - 1){
// Add 1 to Index
s++;
} else {
// Reset Back To O
s = 0;
}
setImage();
}
function changeBack(){
if(s < trys.length - trys.length + 1){
s = trys.length -1;
} else {
s--;
}
setImage();
}
}
}
console.log(trys);
document.getElementById ("btngo").addEventListener ("click", changeImg, true);
document.getElementById ("btnback").addEventListener ("click", changeBack, true);
});
}
document.getElementById("cats").addEventListener ("click", function(event)
{reply_click(event.target.id);;
});
document.getElementById("architecture").addEventListener ("click", function(event)
{reply_click(event.target.id);
});
HTML:
<body>
<div class="container">
<div class="entry-header">
<h1>subreddit slide shows</h1>
<p>Click the buttons below to browse through the latest photos of each noted subreddit.</p>
</div>
<div class="red-buttons">
<button id="cats">cats</button>
<button id="architecture">architecture</button>
</div>
<div class="main">
<div class="main_cat">
<div class="main_section">
<h2>r/<span id="red-sub">?????</span></h2>
<div class="img_container">
<img name="slide" class="cat_img" style=""
src="https://cdn.worldvectorlogo.com/logos/reddit-2.svg" />
</div>
<div class="button_base">
<button id="btnback"><</button>
<button id="btngo">></button>
</div>
</div>
</div>
</div>
</div>
</body>
I have three images in my HTML code, and I want them to change every five seconds. Why does my code not work?
var images = [];
images[0] = ['photoFromInternet'];
images[1] = ['photoFromInternet2'];
images[2] = ['photoFromInternet3'];
var index = 0;
function change() {
document.mainPhoto.src = images[index];
if (index == 2) {
index = 0;
} else {
index++;
}
setInterval(change(), 1000);
}
window.onload = change();
<div class="lastMain">
<a href="www.comingsoon.com" id="slider">
<img id="mainPhoto">
<div class="mainSlider">
<img src="photoFromInternet1" style="display: none">
<img src="photoFromInternet2*" style="display: none">
<img src="photoFromInternet3" style="display: none">
</div>
</a>
</div>
P.S. If you can help please don't use jquery because I haven't learned that yet.
you should run 'change' function outside of the func and pass the function name to the setInterval func as below
let images = ['photoFromInternet', 'photoFromInternet2', 'photoFromInternet3'];
let index = 0;
const imgElement = document.querySelector('#mainPhoto');
function change() {
imgElement.src = images[index];
index > 1 ? index = 0 : index++;
}
window.onload = function () {
setInterval(change, 5000);
};
Look at your console, it's telling you why. Uncaught TypeError: Cannot set property 'src' of undefined, meaning document.mainPhoto is undefined. That's not how you select an element in JS (document.getElementById("mainPhoto") works better :)
Also, you should pass a function to setInterval, not call the function directly inside of it, otherwise you are infinitely calling change() which leads to an infinite call stack error.
Also, if you want 5 seconds, you want to pass 5000, not 1000 (milliseconds).
Finally, you want to set a timeout, not an interval, every time you call the function. Timeouts are executed once. If you set a new interval every time, you'll be piling up function calls exponentially, quickly making your page unresponsive by overwhelming the CPU.
var images = [];
images[0] = ['photoFromInternet'];
images[1] = ['photoFromInternet2'];
images[2] = ['photoFromInternet3'];
var index = 0;
function change() {
document.getElementById("mainPhoto").src = images[index];
if (index == 2) {
index = 0;
} else {
index++;
}
setTimeout(change, 5000);
}
window.onload = change();
<div class="lastMain">
<a href="www.comingsoon.com" id="slider">
<img id="mainPhoto">
<div class="mainSlider">
<img src="photoFromInternet1" style="display: none">
<img src="photoFromInternet2*" style="display: none">
<img src="photoFromInternet3" style="display: none">
</div>
</a>
</div>
My slideshow works well if I stay in the same browser tab which slideshow exists in, but If I change the Browser tab to another and then back again to the slideshow tab I find that the slideshow moves randomly And in a wrong way. I don't know what happened to the slideshow.
HTML:
<div id="slideshow">
<img src="images/img.jpg" alt="" title="" />
<img src="images/img2.jpg" alt="" title="" />
</div>
Javascript:
var time = 0;
var slideshow = function() {
var slideshowElem = document.getElementById('slideshow');
var slideshowLen = slideshowElem.children.length;
var slideshowScrollheight = slideshowElem.scrollHeight - 300;
if (slideshowElem.scrollTop < slideshowScrollheight) {
time = setInterval(function() {
slideshowElem.scrollTop += 5;
if (slideshowElem.scrollTop == slideshowScrollheight)
clearInterval(time);
}, 10);
} else {
time = setInterval(function() {
slideshowElem.scrollTop -= 5;
if (slideshowElem.scrollTop == 0)
clearInterval(time);
}, 10);
}
setTimeout(slideshow, 3000);
}
// implementation
slideshow();
Can anybody know why that happened, although I think my code is correct?
I have one question about image grid system.
I created this DEMO from codepen.io
In this demo you can see :
<div class="photo-row">
<div class="photo-item">
<!--Posted image here <img src="image/abc.jpg"/>-->
</div>
</div>
This DEMO is working fine but. My question is how can I use my grid system like in this css:
<div class="photo">
<div class="photo-row">
<img src="abc.jpg"/>
</div>
<div class="photo-row">
<img src="abc.jpg"/>
</div>
</div>
I created second demo for this: second DEMO. In the second demo you can see the grid system not working like first DEMO.
Also my jQuery code:
(function($,sr){
var debounce = function (func, threshold, execAsap) {
var timeout;
return function debounced () {
var obj = this, args = arguments;
function delayed () {
if (!execAsap)
func.apply(obj, args);
timeout = null;
};
if (timeout)
clearTimeout(timeout);
else if (execAsap)
func.apply(obj, args);
timeout = setTimeout(delayed, threshold || 100);
};
}
// smartresize
jQuery.fn[sr] = function(fn){ return fn ? this.bind('resize', debounce(fn)) : this.trigger(sr); };
})(jQuery,'smartresize');
/* Wait for DOM to be ready */
$(function() {
// Detect resize event
$(window).smartresize(function () {
// Set photo image size
$('.photo-row').each(function () {
var $pi = $(this).find('.photo-item'),
cWidth = $(this).parent('.photo').width();
// Generate array containing all image aspect ratios
var ratios = $pi.map(function () {
return $(this).find('img').data('org-width') / $(this).find('img').data('org-height');
}).get();
// Get sum of widths
var sumRatios = 0, sumMargins = 0,
minRatio = Math.min.apply(Math, ratios);
for (var i = 0; i < $pi.length; i++) {
sumRatios += ratios[i]/minRatio;
};
$pi.each(function (){
sumMargins += parseInt($(this).css('margin-left')) + parseInt($(this).css('margin-right'));
});
// Calculate dimensions
$pi.each(function (i) {
var minWidth = (cWidth - sumMargins)/sumRatios;
$(this).find('img')
.height(Math.floor(minWidth/minRatio))
.width(Math.floor(minWidth/minRatio) * ratios[i]);
});
});
});
});
/* Wait for images to be loaded */
$(window).load(function () {
// Store original image dimensions
$('.photo-item img').each(function () {
$(this)
.data('org-width', $(this)[0].naturalWidth)
.data('org-height', $(this)[0].naturalHeight);
});
$(window).resize();
});
Anyone can help me in this regard ? Thank you in advance for your answer.
Since you'll be creating the HTML dynamically you should remove the .photo-row container but keep .photo-item like so:
<div class="photo-item">
<img src="..." />
</div>
<div class="photo-item">
<img src="..." />
</div>
<div class="photo-item">
<img src="..." />
</div>
...
Then what you can do is wrap your elements with .photo-row on page load. First starting with various sets of two:
var imgGrab = $('.photo-item'); //photos
var imgLength = imgGrab.length; //number of photos
for ( i=0; i<imgLength; i=i+3 ) {
imgGrab.eq(i+1).add( imgGrab.eq(i+1) ).add( imgGrab.eq(i+2) ).wrapAll('<div class="photo-row"></div>'); //wrap photos
}
Then find the remanding ones and wrap those with .photo-row as well:
$(".photo-item").each(function(){
if($(this).parent().is(":not(.photo-row)")){
$(this).wrap('<div class="photo-row"></div>');
}
});
This will wrap your images dynamically and let the CSS do its job regardless of the number of them:
CODEPEN
I'm building a webpage which shows products. When hovering over the product image, a slideshow-like event should start. You'll see photo's of the product in several states.
I've coded this with jQuery and I'm facing a kinda annoying bug.
When you hover over several products very fast and then leave your mouse on one product, it will slide through the images very fast.
The HTML structure looks like this:
<div class="products">
<div class="productContainer" data-product-id="1">
<img src="http://detestdivisie.nl/upload/images/hover-tests/inside/bmw-inside.jpg" class="mainProductImage" />
<div class="hoverImages">
<img src="" data-lazy-src="http://detestdivisie.nl/upload/images/hover-tests/inside/bmw-inside-1.jpg" />
<img src="" data-lazy-src="http://detestdivisie.nl/upload/images/hover-tests/inside/bmw-inside-2.jpg" />
<img src="" data-lazy-src="http://detestdivisie.nl/upload/images/hover-tests/inside/bmw-inside-3.jpg" />
<img src="" data-lazy-src="http://detestdivisie.nl/upload/images/hover-tests/inside/bmw-inside-4.jpg" />
<img src="" data-lazy-src="http://detestdivisie.nl/upload/images/hover-tests/inside/bmw-inside-5.jpg" />
</div>
</div>
<div class="productContainer" data-product-id="1">
<img src="http://detestdivisie.nl/upload/images/hover-tests/outside/bmw-outside.jpg" class="mainProductImage" />
<div class="hoverImages">
<img src="" data-lazy-src="http://detestdivisie.nl/upload/images/hover-tests/outside/bmw-outside-1.jpg" />
<img src="" data-lazy-src="http://detestdivisie.nl/upload/images/hover-tests/outside/bmw-outside-2.jpg" />
<img src="" data-lazy-src="http://detestdivisie.nl/upload/images/hover-tests/outside/bmw-outside-3.jpg" />
<img src="" data-lazy-src="http://detestdivisie.nl/upload/images/hover-tests/outside/bmw-outside-4.jpg" />
<img src="" data-lazy-src="http://detestdivisie.nl/upload/images/hover-tests/outside/bmw-outside-5.jpg" />
</div>
</div>
</div>
The Javscript class that is responsible for handling the events looks like this:
CategoryProductImages = function () {
this.className = 'CategoryProductImages';
this.version = '0.1.0';
this.mouseOver = false;
this.currentElement = null;
this.currentCategoryId = 0;
this.currentImageList = [];
this.currentImageKey = 0;
/**
* CategoryProductImages.init()
*
* Initializes the class that is reponsible for handling the
* mouse over and outs for the products.
*
* This method will call the createObserver method.
*/
this.init = function () {
console.log('Called ' + this.className + '.init()');
this.createObservers();
};
/**
* CategoryProductImages.createObservers()
*
* Will handle the mouse over and out events.
*/
this.createObservers = function () {
console.log('Called ' + this.className + '.createObservers()');
var thisObj = this;
jQuery('.productContainer').hover(
/** Called when mouse of the user moves over the element. **/
function () {
console.log('Mouse over .productContainer');
thisObj.mouseOver = true;
thisObj.currentElement = jQuery(this);
thisObj.currentCategoryId = thisObj.currentElement.data('category-id');
console.log('Lets\'s work for category id ' + thisObj.currentCategoryId);
thisObj.currentElement.find('.hoverImages img').each(function() {
thisObj.currentImageList.push(jQuery(this).data('lazy-src'));
});
thisObj.iterate();
},
/** Called immediatly after the mouse of the user leaves the element **/
function () {
console.log('Mouse out .productContainer');
thisObj.currentElement = null;
thisObj.mouseOver = false;
thisObj.currentImageList = new Array();
thisObj.currentImageKey = 0
}
);
};
this.iterate = function () {
console.log('Called ' + this.className + '.iterate()');
if (this.mouseOver && this.currentImageList.length > 0) {
console.log('Will now start the iteration process');
this.currentElement.find('img.mainProductImage').prop('src', this.currentImageList[0]);
thisObj = this;
setTimeout(function () {
console.log('First image shown, will now show next image.');
thisObj.nextImage(thisObj.currentCategoryId);
}, 3000)
} else {
console.log('Won\'t iterate, because the mouse of the user has left the element, of there are no images to show.');
}
};
this.nextImage = function (currentCategoryId) {
console.log('Called ' + this.className + '.nextImage()');
if (this.mouseOver && this.currentImageList.length > 0 && currentCategoryId == this.currentCategoryId) {
console.log('MouseOver still active, and images are found, show next image.');
this.currentImageKey += 1;
if (typeof this.currentImageList[this.currentImageKey] == 'undefined') {
console.log('OH NO! We\'ve reached the end of the list. Letst start all over again.');
this.currentImageKey = 0;
}
this.currentElement.find('img.mainProductImage').prop('src', this.currentImageList[this.currentImageKey]);
thisObj = this;
setTimeout(function () {
console.log('Okay, we\'ve waited for three seconds, NEXT! :)');
thisObj.nextImage(currentCategoryId)
}, 3000);
} else {
console.log('Iteration for ' + currentCategoryId + ' stopped');
console.log('Mouse over: ');
console.log(this.mouseOver);
console.log('Image list length: ');
console.log(this.currentImageList.length);
console.log('nextImage category id: ');
console.log(currentCategoryId);
console.log('Current category id:');
console.log(this.currentCategoryId);
console.log('#########################');
}
}
};
var categoryProductImagesObject = new CategoryProductImages();
categoryProductImagesObject.init();
The make it all more clear I've created a CodePen example. Please mouse over product 1 and product 2 very fast and then leave your mouse on product 1. You'll see it will loop through the product images way to fast.
http://codepen.io/wdivo/pen/lamsq
The time it should take before and image is replaced by another one is 3 seconds.
I am overlooking something, because obviously several setTimeouts are working near eachother, while there should only be 1 active setTimeout.
What should I do to make sure only 1 "active" setTimeout is running?
Simply said, I'd want to stop all the previous setTimeouts if a new one gets activated.
I'm now looking into clearInterval, but can't think of a way to implement it...