skip to content

CSS: Fading slideshow with a touch of JavaScript

 Tweet0 Tweets

Thanks mainly to WordPress, every other website now has some kind of carousel or slideshow on the homepage. That is despite all advice to the contrary.

Regardless of the merits, these features also tend to use bloated JavaScript and jQuery plug-ins adding hundreds of extra kilobytes to the homepage - slowing things down and damaging SEO on the most important page of your website.

Here we present a simple slideshow which by contrast requires less than 1kb of CSS and JavaScript.

Setting up the HTML

The starting point is to set up a list of images on the page, wrapped in a DIV. Something like the following:

<div id="stage"> <a href="1.jpg"><img src="1.jpg" width="360" height="270"></a> <a href="2.jpg"><img src="2.jpg" width="360" height="270"></a> <a href="3.jpg"><img src="3.jpg" width="360" height="270"></a> <a href="4.jpg"><img src="4.jpg" width="360" height="270"></a> <a href="5.jpg"><img src="5.jpg" width="360" height="270"></a> <a href="6.jpg"><img src="6.jpg" width="360" height="270"></a> <a href="7.jpg"><img src="7.jpg" width="360" height="270"></a> <a href="8.jpg"><img src="8.jpg" width="360" height="270"></a> </div>

In this example all the images have links, but that's not necessary. If you remove the links, you'll just need to change some of the CSS and JavaScript to reference 'img' instead of 'a'. Later we'll look at placing text over the images for a caption, call to action (CTA) or similar.

An improvement would be to have only two images on the page load time, and then post-load and insert the others into the DOM as required. That saves having to load them all at once, making the page faster and boosting SEO.

If we stop here, our images are just going to wrap across the page. What we need is some styles to prevent that from happening.

CSS to stack the images

Here is the CSS we are using for the demonstration below:

<style type="text/css"> #stage { margin: 1em auto; width: 382px; height: 292px; } #stage a { position: absolute; } #stage a img { padding: 10px; border: 1px solid #ccc; background: #fff; } #stage a:nth-of-type(1) { animation-name: fader; animation-delay: 4s; animation-duration: 1s; z-index: 20; } #stage a:nth-of-type(2) { z-index: 10; } #stage a:nth-of-type(n+3) { display: none; } @keyframes fader { from { opacity: 1.0; } to { opacity: 0.0; } } </style>

By setting the links to position: absolute we're taking all the images out of the document flow and stacking them on top of one another. We then need to assign a width and height to #stage to reserve space on the page for the slideshow. This equals the image dimensions plus padding (10px on each side) and borders (1px on each side).

We then use some nth-of-type selectors to place the first image on top of the stack, the second image just behind, and the rest hidden from display.

Finally, we assign an animation keyframe to the top image telling it to wait four seconds before fading out to opacity: 0. All that's missing now is a touch of JavaScript to move the faced image to the bottom of stack so the next image can be displayed and fade out in turn.

A touch of JavaScript

All that is needed here is to assign an event handler to our images that is triggered when the keyframe animation ends. It takes the foremost photo, and moves it to the back. Much as you would do with a pack of playing cards:

<script type="text/javascript"> // Original JavaScript code by Chirp Internet: www.chirp.com.au // Please acknowledge use of this code by including this header. window.addEventListener("DOMContentLoaded", function(e) { var stage = document.getElementById("stage"); var fadeComplete = function(e) { stage.appendChild(arr[0]); }; var arr = stage.getElementsByTagName("a"); for(var i=0; i < arr.length; i++) { arr[i].addEventListener("animationend", fadeComplete, false); } }, false); </script>

The new uppermost image now assumes the nth-of-type(1) properties, including the fader keyframe, and so on through the other images.

And that's it! No bloated code, no plugins, no libraries, just a few lines of vanilla JavaScript which works in all modern browsers.

To support older browsers, there are browser prefixes for WebKit, Mozilla and Microsoft and you can find details in our earlier article on constructing an Infinite Animated Photo Wheel.

Working demonstration

Putting it all together you get a simple fading slideshow:

If you want the slideshow to move slower, or faster, or use a different transition, it's just a matter of adjusting the Transition Timing Function (or animation-timing-function in this case). The borders can also be easily re-styled or removed.

Displaying text over the slideshow

There are many ways to go about this, but perhaps the simplest is to add some title attributes to our links and have them displayed over the image using CSS:

#stage a::after { position: absolute; left: 11px; bottom: 11px; padding: 2px 0; width: calc(100% - 22px); background: rgba(0,0,0,0.5); text-align: center; content: attr(title); font-size: 1.1em; color: #fff; }

With no other changes aside from adding titles to our links, we now have the startings of a basic CTA slideshow, which could even be turned into a carousel with a few tweaks to the keyframe.

So if you absolutely have to have a homepage slideshow to please the powers that be, think about ditching jQuery for something like the above. And let us know if you have any feedback or questions using the button below.

Converting to a carousel

A few simple changes to the CSS makes our slideshow behave more like a carousel:

CSS:

<style type="text/css"> #stage { margin: 1em auto; width: 360px; height: 270px; border: 10px solid #000; overflow: hidden; } #stage a { position: relative; display: inline-block; } #stage a::after { position: absolute; top: 50%; left: 0; transform: translateY(-50%); width: 100%; text-align: center; content: attr(title); font-weight: bold; font-size: 2em; color: #fff; text-shadow: -1px -1px 0 #333, 1px -1px 0 #333, -1px 1px 0 #333, 2px 2px 0 #333; } #stage a:nth-of-type(2) { left: 360px; top: -50%; animation-name: slider; animation-delay: 4s; animation-duration: 1s; animation-timing-function: cubic-bezier(0,1.5,0.5,1); } #stage a:nth-of-type(n+3) { display: none; } @keyframes slider { from { transform: translateY(-50%) rotate(30deg); left: 360px; } to { transform: translateY(-50%); left: 0px; } } </style>

JavaScript:

<script type="text/javascript"> // Original JavaScript code by Chirp Internet: www.chirp.com.au // Please acknowledge use of this code by including this header. window.addEventListener("DOMContentLoaded", function(e) { var stage = document.getElementById("stage"); var slideComplete = function(e) { stage.appendChild(arr[0]); }; var arr = stage.getElementsByTagName("a"); for(var i=0; i < arr.length; i++) { arr[i].addEventListener("animationend", slideComplete, false); } }, false); </script>

And of course your first question is going to be how to add navigation spots or arrows over the image...

Working with different size images

If your images differ in size or shape you will see parts of the larger ones protruding from behind the smaller ones. One way around this is to use JavaScript to adjust the padding to make every slide the same dimension:

<script type="text/javascript"> // Original JavaScript code by Chirp Internet: www.chirp.com.au // Please acknowledge use of this code by including this header. window.addEventListener("DOMContentLoaded", function(e) { var maxW = 0; var maxH = 0; var stage = document.getElementById("stage"); var fadeComplete = function(e) { stage.appendChild(arr[0]); }; var arr = stage.getElementsByTagName("img"); for(var i=0; i < arr.length; i++) { if(arr[i].width > maxW) maxW = arr[i].width; if(arr[i].height > maxH) maxH = arr[i].height; } for(var i=0; i < arr.length; i++) { if(arr[i].width < maxW) { arr[i].style.paddingLeft = 10 + (maxW - arr[i].width)/2 + "px"; arr[i].style.paddingRight = 10 + (maxW - arr[i].width)/2 + "px"; } if(arr[i].height < maxH) { arr[i].style.paddingTop = 10 + (maxH - arr[i].height)/2 + "px"; arr[i].style.paddingBottom = 10 + (maxH - arr[i].height)/2 + "px"; } arr[i].addEventListener("animationend", fadeComplete, false); } }, false); </script>

Please note that the above code is for the case where the slideshow contains only images without links around them. If there are links, replace img with a and arr[i] with arr[i].firstChild throughout, with the exception of the last line.

References

< CSS

Send a message to The Art of Web:


used only for us to reply, and to display your gravatar.

<- copy the digits from the image into this box

press <Esc> or click outside this box to close

User Comments

Post your comment or question

24 January, 2019

Your slider is very cool, the only thing is not clear how to add slide switching points

17 December, 2018

I assume you have to have images that have the same exact dimensions. What if you have images with different heights but the same width - or vice versa? Can you have a slideshow in this case where, for example if you keep the width constant but have different heights in some cases, the image centers itself vertically in the slideshow?

The problem is in the way the fade-out works. Both images have to be visible at the same time, with the top one then fading out. When the queued image is larger, it pokes out from behind the current image.

You could try a CSS solution to fore them to the same height:
e.g.

#stage img {
max-height: 800px;
width: auto;
}

But even then you'd want them to be the same shape.

Otherwise you can set larger borders (or margin) on the smaller images to make them cover the same area, and the image centered...

24 February, 2018

Thank you so mutch, i now have a greate website. Do you hate W3schools, i do. This is so mutch easyer! Tanks you again! �™�

top