Carousel Q

My javascript >
A carousel is an animated image slideshow that plays by itself and is used to show off news features or products. On this page I will take a basic model and add some improvements, to give it better looks and more function.


The basic slideshow

The basic carousel structure found at w3schools.com is very similar to the javascript lightbox (see my Lightbox Q page), but without any thumbnails to click on (although you could have thumbs underneath, instead of dots) and no fullscreen user immersion, so it fits into the page design.

Here are the parts, the html layout first...

View code
 


And the js...

View code
 


The method here is ultra simple, using js to add the 'display: block' value inline to the new slide, and just before that, all slides are killed (the line: slides[i].style.display = "none"; ) or set with no display.

An index is created at the first line, beginning at 0, and the function increments the index no. each time, by 'slideIndex++'. So on te initial run, the index no. becomes 1, An array is built of the slide divs 'mySlides' and as arrays start at 0, each slide in the array is called with the index no. minus 1 to call the correct slide (slide 1 is array 0).

In the CSS, the class 'mySlides' needs 'display: none', and the 'fade-in' class is like so...

View code
 


You can also use a cubic-bezier instead of 'ease-out'

View code
 


So what you get is the new slide fades in over a blank div area, the carousel kind of blinking as it does so (previous slide is killed without any transition out). There's no cross-fade of old and new slides.



Basic slideshow


1 / 3
Caption One
2 / 3
Caption Two
3 / 3
Caption Three





To create a slide-in effect, you need to add a transform of some kind to the 'fade' transition, so that on obtaining 'display: block' it will move into position from off-right.

View code
 


To make it look nice by not showing slides outside the frame, you need to add a wrapper div to the html, and in the css, give .wrapper a value 'overflow: hidden;'. Otherwise you'll see the slide divs move across the container div into position each time.

Also, use flex to keep the contents centered...

View code
 
View code
 




Slide-in slideshow


1 / 3
Caption One
2 / 3
Caption Two
3 / 3
Caption Three



How's it look? I suppose it's fairly reasonable for a quick picture show, perhaps one that people won't stay on very long.

But what about cross-fading the slides, how can that be done using js and CSS? To begin with, you can't keep killing the active slide before each new one, as you need one to stay displayed underneath as it animates in. That means you'd have to reset 'display: none' to the old slide only after the new slide is safely displayed.



Cross-fade slideshow


1.

1 / 3
Caption One
2 / 3
Caption Two
3 / 3
Caption Three


2.

1 / 3
Caption One
2 / 3
Caption Two
3 / 3
Caption Three


3.

1 / 3
Caption One
2 / 3
Caption Two
3 / 3
Caption Three


4.

1 / 3
Caption One
2 / 3
Caption Two
3 / 3
Caption Three


In the set of slideshows above, I achieve a cross-fade by preserving the current slide with display as a new slide moves in on top.

▷ With 1 and 2, the displayed slide is removed when a second slide comes in (so when it is at one-before-previous position, two back from new). This is because the new slide takes 1sec to fully animate in and we want the previous slide to remain in the background for this short moment.

▷ With 3 and 4, I remove display from the previous slide (one back from new) and use a timer, so that the new slide can animate fully before the previous slide vanishes.

▷ With 1, 2 and 3, I use js to set 'display: block' and 'display: none', inline, with the 'fade-in' class on the 'mySlides' div, so when a slide gets 'display: block' it triggers the fade-in/slide-in animation set to the 'fade-in' class.

▷ With 4, I do it differently, setting/removing the 'fade-in' class name, with 'mySlides' class set with 'display: none'. Practically the same as 3. but 3. has a glitch at the start, with no animation until the next loop.

▷ With all of them I set new slide 'z-index: 50' (drawn after other elements) which changes to 'z-index: 0' once the underlying slide has gone. This is to prevent slide no.1 being covered by the last slide (drawn later in the DOM) when the index wraps from end to start.


And the javascript...

For no.1 above, the js uses two functions to calculate the index no.s of the two slides behind the current slide:

View code
 


The two functions 'removeZ' and 'removeOld' run some math to find out where the previous slides are on the slide index, like if before the start it will wrap the count to the end.


No.2 above is done slightly differently, by setting variables with the fn 'getSlideHistory', of the current, previous and before-previous slide index no.s, which I think is a tidier approach:

View code
 



In both 3 and 4, the current slide index no. is saved to a variable, 'oldCurrent', and it is used to set the properties of this slide when it becomes the 'previous' slide, after another slide comes in.

And because it is the index no. saved to variable, and not a calculation one back or two back from current, it will also be safe to use if the slideshow jumps to a random slide - the previous index no. will always be available.


The js for no.3 above:

View code
 


The js for no.4 above:

View code
 





Interactive slideshow



The next step is to integrate some basic controls to allow flicking through the slides using prev/next buttons. and random jumps by clicking on the dots under the slideshow.

Going backwards, the active slide needs to peel off the top showing the new slide underneath - needing a 'slide-out' animation.

Here is what I managed to develop with some help from AI. The slideshow now auto plays only after scrolling down into view, it pauses on mouse-over, and the next/previous and dots select slide.







The html structure has received some elements from my lightbox, like the next/previous area and buttons, as well as the dot onClick fn.

View code
 


The function calls 'onmousemove' and 'onmouseleave' in the 'carousel' div call the js fn when the mouse is detected over the slideshow and when it moves off. Thus when a user wants to interact with it the thing pauses to be helpful.

'onmousemove' is slightly better than 'onmouseover' as if the mouse is already there at page load, a slight movement will trigger pause, whereas the latter needs the mouse to enter the slideshow to trigger it.

The 'plusSlides' fn, called by prev/next, for travelling up or down the slide index, carry a second argument, 'Dn' or 'Up' which is read by the js fn as 'Next' (as the slide progress will be different in each case).

The captions are extracted from the Exif comment tag of the images (see exiftag plugin).

The 'currentSlide' fn calls the slide no. with a -1 to align the 1..n index with the 0..n array of the html divs (as I changed to a 0-based slide index). This means that slide no.1 will call div no. 0 in the array of divs created by the js.

I include it on the page with..

View code
 


Where the file 'mycarousel.html' is the html content above this, and N is the no. of slides in the image dir or no. of images to include.

And the style sheets...

View code
 


The secret to the crossfade here is with these classes:


And the css animation...

View code
 



And my modified javascript looks like this, which displays the carousel on the page, but as yet does not start the slideshow..

 


Notes:


Important! Updated Jan 20th, 2025! When slides move FWD, both 'setTimeout's must use a closure, i.e. with both 'prevIndex' and 'slideIndex', as otherwise, with fast clicking, slides will be missed and left with display properties that will cause problems and a bad experience!



The z-index values are still necessary, so that newer slides are given drawing priority over older ones - especially so that slide 0 will display over slide n (last in the index).

The timeout is to ensure that the old slide remains displayed until the new slide transitions completely.

And here is the js to start the slideshow when the user scrolls the carourel into view..

 


The last 'isInViewport' at the bottom, which calls the fn that starts the carousel, is needed in case the page is refreshed when already scrolled to the carousel, so that it will start without any scroll event to trigger it.


The js to pause the slideshow when the mouse hovers the carourel..

 




Final tweaks



Well, I took the js a little further, amalgamating the 'plusSlides' and 'currentSlide' fn's with their 'if/else if' tests into 'showSlides' to create 'getSlide'. What a task!!

It works the same and might look tidier, hopefully not more complex?

The html structure changed very slightly, as you can see (note 'getSlide' and the second arg 'Rm' with the dots fn call)...

View code
 


The new js....