Simplest jQuery Slideshow

A friend was looking at doing a simple slideshow. The requirements were very straightforward:

  • No animation controls. eg: play, stop.
  • Images should cross-fade.

Her instinct was to find an existing jQuery plug-in and revise it to work to her needs. That would seem simple enough but if you do a search for jQuery slideshows, you'll find that there are plenty of them and they are filled with plenty of functionality.

Using an existing plug-in wasn't very practical and hard to work with. I had even recommended a script that I, myself, had written but she quickly realized it didn't meet the requirements.

I put my thinking cap on and decided to write a script from scratch. "Under 20 lines," I exclaimed! In the end, it turned out much simpler than even I predicted.

(If you're a TL;DR kinda person, check out the demo.)

HTML and CSS

The HTML was very straightforward: a DIV with some IMGs in it.

<div class="fadein">
<img src="https://farm3.static.flickr.com/2610/4148988872_990b6da667.jpg">
<img src="https://farm3.static.flickr.com/2597/4121218611_040cd7b3f2.jpg">
<img src="https://farm3.static.flickr.com/2531/4121218751_ac8bf49d5d.jpg">
</div>

In thinking about the CSS, I decided to just lock all the images into the same place using absolute positioning.

.fadein { position:relative; width:500px; height:332px; }
.fadein img { position:absolute; left:0; top:0; }

jQuery Slideshow

Now to think about the slideshow. First, I knew that I'd want to hide all the images except the first one.

$('.fadein img:gt(0)').hide();

You have to remember that the image index starts at 0. That means that we want all images after the first one to be hidden.

Next, I need a setInterval to iterate through the images every few seconds.

setInterval(function(){ },3000);

From here, I started writing this out piece by piece to get what I wanted. I needed the first image to fade out.

$('.fadein :first-child').fadeOut()

After that, I need the next image to fade in.

.next('img').fadeIn()

Then I needed to take the first image and throw it onto the end of the stack.

.end().appendTo('.fadein')

The end() resets jQuery's internal reference back to the original selection. That way, it's the :first-child that I'm appending to the end of the .fadein element and not the next('img').

That's it?

Well, yes. That's it.

$(function(){
    $('.fadein img:gt(0)').hide();
    setInterval(function(){
      $('.fadein :first-child').fadeOut()
         .next('img').fadeIn()
         .end().appendTo('.fadein');}, 
      3000);
});

Check out the demonstration page to this this little script in action.

Is there an even simpler way to do this? (Some ideas that I haven't tried: Is specifying img in next() necessary? Could I have used eq(0) instead of :first-child to save a couple bytes?)

For more Snook.ca slideshows:

Published December 11, 2009
Categorized as JavaScript
Short URL: https://snook.ca/s/965

Conversation

83 Comments · RSS feed
grace said on December 11, 2009

:) Even the post is concise, loving it! Thank you again sweetness.

Sean Gaffney said on December 11, 2009

Looks really similar to what we did on http://pocketyoursite.com, but yours is more elegantly coded. :) Mine is additionally ugly because I had to sync two "shows" and ended up just doubling up the code...

Mathias Bynens said on December 11, 2009

Impressive!

I guess you could indeed use eq(0) instead of :first-child in most cases. (Depending on how many of these slideshows you would want on one page, and how you’d specify the different element sets on which to invoke the effect, :first-child and eq(0) could return different results.)

Specifying img in next() is something I personally wouldn’t do, since I’d be using an unordered list containing IMG elements. Fade effects work on any HTML element, so there’s no problem there. Dropping the img in next() wouldn’t just make your script smaller, it would increase its flexibility by accepting different markup scenarios.

Steve Rydz said on December 11, 2009

Great work Jonathan, however when Javascript is disabled you can only see one image. Would it not be better to perhaps set the images to overflow:auto so at least the user could scroll through the images if they didn't have Javascript?

Justen Doherty said on December 11, 2009

good job - i got the perfect use for this!

Eugenio Grigolon said on December 11, 2009

Wow, what a nice slideshow. This can help lots of people with no jQuery knowledge. No need to add anything else. Nice job.

Andy Ford said on December 11, 2009

I was prepared with a "why not use Cycle lite" comment, but then I saw how little code you used to pull this off. Very cool!

Jake said on December 11, 2009

Sweet Jonathan. I love when code is nice and small. Will definitely check this out next time I need slideshow functionality. Thanks for sharing :)

Thomas Fuchs said on December 11, 2009

Hey Jonathan, nice to have met you in Amsterdam!

If you rearrange the images to be in reverse-order, you can fade with basically just one line of code. For a cross-fade it's just necessary to fade out the overlaying object (you don't need to fade two elements at the same time).

Here's my implementation with Prototype and scriptaculous, with three lines of code. :)

setInterval(function(){ $$('.fadein img').last().fade({
  duration: .3, afterFinish: function(e){ e.element.remove(); }
}); }, 3000);

By the way, I love the comment preview functionality on your site.

Eli Dupuis said on December 11, 2009

Sweet! I use Alsup's cycle all the time and it works great. but this takes the cake from a simplicity standpoint. Thanks!

Mathias Bynens said on December 11, 2009

Jonathan, your code can easily be made into a jQuery plugin:

  $.fn.slideShow = function(timeOut) {
   var $elem = this;
   this.children(':gt(0)').hide();
   setInterval(function() {
    $elem.children().eq(0).fadeOut().next().fadeIn().end().appendTo($elem);
   }, timeOut || 3000);
  };

  $(function() {
   $('.fadein').slideShow();
  });

Now you can use something like this to invoke the slideshow on an element:

$('.fadein').slideShow();

As you can see, I’ve added a timeOut parameter. The default value is 3000 (I just took this from your code) but this can easily be specified:

$('.fadein').slideShow(300);

I uploaded an example of your script in this plugin format.

Mathias Bynens said on December 11, 2009

I also did the changes I mentioned in my first comment, allowing for more flexibility in the markup. Here’s an example of your script cycling through an unordered list.

Thomas Fuchs said on December 11, 2009

I had a pretty big bug in my code, but have corrected it now (unfortunately, requires some extra hoops). Anyway, here it goes:

setInterval(function(){
  var imgs = $$('.fadein img'),
   visible = imgs.findAll(function(img){ return img.visible(); });
  if(visible.length>1) visible.last().fade({ duration: .3 });
    else imgs.last().appear({ duration: .3,
      afterFinish: function(){ imgs.slice(0,imgs.length-1).invoke('show');  } });
}, 3000);

I personally don't like either the jQuery (too "magic", not readable) nor my code (too complicated). Will think about it and hopefully come up with something nicer.

Thanks for the food for thought!

Derek Pennycuff said on December 11, 2009

If you ignore the opening and closing function call and don't bother to specify 'img' for next(), the minified version of this is exactly 140 characters. Any jQuery function that both does something useful and fits inside a single tweet is something to be proud of.

Daniel Marino said on December 11, 2009

@Steve Rydz - I would think that you'd want to hide the other images, otherwise it could break the layout of the page (depending on how the images fall into place).

Other way's I've seen this done (though not using Snook's methodology) is that in the HTML you only specify one image and use JS to append the other images to use in the slideshow. If the user has JS disabled, they only see the one, placeholder image. This is kind of the same, except that the JS isn't adding the other images... they're specified in the HTML.

Jeff said on December 11, 2009

Bravo snook! I'm really liking this! Is there some way to control how fast the image fades out? Would you do that by extending the interval time?

Adam Detrick said on December 11, 2009

Thomas - I agree in a way regarding the jQuery syntax...

The enthusiasm for code like this makes me anxious. Don't get me wrong, it's beautiful... It's just that I've had one to many experiences working with really cool and terse jQuery "sream of consciousness" style code. Also, I hate the misconception that this is just a few lines of code. It's a few lines of code plus the jQuery library. Readability and performance gains seem to be canceled out here in a way.

It just upsets me when I imagine the complex web app that's written entirely like this because a developer gets too excited about how few lines of code something might take instead of making something understandable and scalable.

With that said, this is a really cool post! I look forward to more, and I'm really loving the redesign here...

Jonathan Snook said on December 11, 2009

@Jeff: to change the speed at which the fade occurs, you need to pass in a parameter to the fadeIn/fadeOut methods.

$('.fadein :first-child').fadeOut(1000)
  .next('img').fadeIn(1000)
adam j. sontag said on December 11, 2009

"Too magic to understand" strikes me as a bizarre criticism of what is a relatively simple code snippet that a majority of jQuery developers could grok in a matter of seconds. Jon's example is clearly only a few lines of code, there is no "misconception" there. LOC is not calculated by adding up all the lines of code that are used in the library methods that you employ in the original function.

There is a lot of energy invested on large jQuery sites on organising and structuring code (or at least there should be), so the idea that someone would write a "complex web app" as a random bunch of jQuery snippets seems like an unfounded fear. It does happen, but unsavvy developers writing crappy code is not a problem that is unique to jQuery or even JavaScript. I have frequently encountered this perception that jQuery is "too easy" from a lot of people who tend to conflate complexity with worthiness, and I just don't buy it. That the potential exists for something to be misused does not mean it should be eschewed altogether.

I shudder at the thought of seeing this code written out with native DOM methods, and I shudder again at the thought that someone that would be preferable.

Jonathan Coombs said on December 11, 2009

I love the simple slideshow concept here. But I notice that all the images are the same size in the slideshow. Does anyone have an example of showing both landscape and portrait photos in the same slideshow with the portrait photos centered? All photos would be the same height, but of course the portrait photos would have a smaller width.
Thanks!

Steve Rydz said on December 11, 2009

@Daniel Marino That is one way.

Surely however the images must be relevant to the content and therefore should be accessible. Having them hidden or positioned absolutely on top of each other means that in the situation where JS isn't available the user cannot access the images at all except whichever one is on top.

The only way I can see this being acceptable is if the images are there purely for decoration but then they wouldn't be in the content, they would be specified in the stylesheet.

Graham Bradley said on December 11, 2009

I'm glad someone else pointed out that this "tiny" piece of code requires the entire jQuery library behind it. I know there's a judgement call to be made - when to stop writing custom code and use a library - but a novice doesn't know about that.

I'm not arguing against jQuery or Jon's code - I just think that, in general, its a good idea to mention an alternative to using a framework, in case a reader only needs the functionality you're describing. Otherwise a novice is going to follow a Jon's article and end up using a sledgehammer to crack a walnut.

@Adam Sontag

"I shudder at the thought of seeing this code written out with native DOM methods, and I shudder again at the thought that someone that would be preferable.

Writing a custom script to do the same isn't hard - I recently wrote something very similar for a client that comes in well under 1k uncompressed. There are plenty of occasions where going native is preferable.

Michael Eichelsdörfer said on December 11, 2009

I have written a "fully-fledged" (e.g. chainable) jQuery plugin based on the ideas above. It can be called with timeout and speed parameter values.


(function($){
  $.fn.simplestSlideShow = function(settings){
    var config = {
      'timeOut': 3000,
      'speed': 'normal'
    };
    if (settings) $.extend(config, settings);
    this.each(function(){
      var $elem = $(this);
      $elem.children(':gt(0)').hide();
      setInterval(function(){
        $elem.children().eq(0).fadeOut(config['speed'])
        .next().fadeIn(config['speed'])
        .end().appendTo($elem);
      }, config['timeOut']);
    });
    return this;
  };
})(jQuery);

You may use it like this:


$(".fadein").simplestSlideShow({'timeOut': 5000, 'speed': 1000});
Michael Eichelsdörfer said on December 11, 2009

BTW: It's still less than 20 lines!

Chris Sullins said on December 11, 2009

Graceful degradation: When the slideshow is initialized, add a second class to .fadein, and reference the js-dependent css using that class. Also, might as well just get all direct children of .fadein. The simple slideshow is inconstent between img and :first-child, so that's basically a requirement anyway. While we're at it, why not store $('.fadein') via closure?

.fadein-js > * { position: absolute; top: 0; left: 0; }

$(function(){
	var fadein = $('.fadein').addClass('fadein-js');
	fadein.children().slice(1).hide();
	setInterval(function(){
		fadein.children().eq(0).fadeOut()
		.next().fadeIn()
		.end().appendTo(fadein);
	}, 3000);
});

This would be incredibly easy to turn into a function to allow multiple slideshows on a page.

Mathias Bynens said on December 11, 2009

Thanks Michaël, I was about to update my ‘plugin’ example with that functionality (speed setting + chainability).

I see you wrapped the code inside this.each(function() { … }, so it works for several elements at the same time. Awesome!

I took the liberty of adding this to GitHub, so it’s easier to reference future improvements of the script. I hope that’s okay.

Andrew Cornett said on December 11, 2009

Dude that is so clean. Two lines??? That's why you're the man, Snook!

Trevor said on December 11, 2009

Mathias, How do you change the fadeIn, fadeOut animation with your code?

peter said on December 11, 2009

I like the fact that you show us how to do it instead of just providing a plug in. Awesome tutorial.

Jonathan Snook said on December 11, 2009

@Steve Rydz: If you're looking for a non-JS alternative, I'd look at attaching a "with JS" class at runtime. With that said, when talking about accessibility, those with screenreaders and other assistive software usually browse with JavaScript on. Which still begs the question of how the interaction should work when there's no real interaction at play.

What I'd recommend is having the images fade in/out but set visibility to hidden and display set to block (do not set it to none). Make sure the images have alt text. That should still allow screenreaders to access the content regardless of the animation.

Steve Rydz said on December 11, 2009

I think you misunderstand me Jonathan. I mean more of a situation where someone is viewing the site without Javascript but not assistive technology.

For example I used to work in an office and we had free reign on the web in quiet times but were not able to use JS, so in a situation like this I would only be able to see one image and not even have the option to scroll through the other images.

This might work from a presentation point of view however if the images are in the content then surely they should be available without JS?

This is more of a CSS thing than JS really. I hope you get what I mean?

Jonathan Snook said on December 11, 2009

Right, I understand now. In which case, I'd do as mentioned in previous comments and as I mentioned in the first sentence of my comment: just add a class to the body or to the surrounding element via javascript and use that to set the absolute positioning.

Steve Rydz said on December 11, 2009

Ah I get what you mean now. Sorry I was on a completely different wave length. You live and you learn ;-)

Michael Eichelsdörfer said on December 12, 2009

@MATHIAS BYNENS: Thanks for putting it on GitHub. Just one more thing: According to the jQuery plugin guidelines the name of the (plugin) file should be "jquery.simplestslideshow.js" (or "jquery.slideshow.js", but I prefer the first).

Mathias Bynens said on December 12, 2009

Mathias, How do you change the fadeIn, fadeOut animation with your code?

Just to be clear: it’s not 'my code', I just wrapped Jonathan’s code into a plugin with some enhancements from Michael. Credit to these fella's!

To answer your question: sorry, that’s not what this script does. As Jonathan explained, this is just a simple slideshow where images cross-fade. If you want more fade effects, use a (heavier) plugin, like Cycle or Cycle Lite.

According to the jQuery plugin guidelines the name of the (plugin) file should be "jquery.simplestslideshow.js" (or "jquery.slideshow.js", but I prefer the first).

Thanks for the tip, Michael, I renamed it.

Mathias Bynens said on December 12, 2009

I love how, after some optimizations, the minified plugin version of this script is only 291 characters long.

Chris said on December 12, 2009

This is great! Thanks for explaining how it all comes together Jonathan.

Is there anyway to make this work with the same class for multiple slideshows on the same page?

Chris said on December 12, 2009

Thank you Mathias :D

Christian Fleschhut said on December 12, 2009

Great post!

Instead of cross-fading two images you can also just get the first one, hide it, append it to the end of the image stack
and fade it in from there so that the (in this case white) page background won’t shine through.

Like this:
http://jsbin.com/isegi

Charles said on December 12, 2009

It is a very simple slideshow. Useful for some projects.
I like the Christian Fleschlut solution.

Mathias Bynens said on December 13, 2009

Nice solution, Christian! Unfortunately your scripts starts off with the wrong image.

Mathias Bynens said on December 13, 2009

Christian: My latest comment was phrased incorrectly. Your example flashes the last image in the stack upon loading, then quickly switches to the first image. I found this to be pretty annoying. Also, note that Jonathan’s demo doesn’t have this tiny issue. This can be fixed through CSS (in combination with a ‘with JS’ class), and by adding some tweaks to your code. Currently, you have the following:

$(function() {
 $('.fadein :first-child').appendTo('.fadein');
 setInterval(function() {
  $('.fadein :first-child').hide().appendTo('.fadein').fadeIn(2000);
 }, 4000);
});

Try this instead:

$(function() {
 $('.fadein :first-child').appendTo('.fadein').show();
 setInterval(function() {
  $('.fadein :first-child').hide().appendTo('.fadein').fadeIn(2000);
 }, 4000);
});

As you can see, .show() explicitly makes the first image visible.

Combined with the ‘with JS’ class, you can use CSS to hide all the images inside .fadein until the script makes them visible again:

 <head>
  <!-- Other stuff here… -->
  <style>
   .js .fadein img { display: none; }
   .js .fadein img:first-child { display: block; }
  </style>
  <script>
   document.documentElement.className += 'js';
  </script>
 </head>

If you omit .js .fadein img:first-child { display: block; }, this will also cause a small ‘flash’, going from the background color of the page to the first image. This is better already – at least it’s not a ‘flash’ between two images. But by adding that line, we don’t get a flash at all :)

Mathias Bynens said on December 13, 2009

Whoops, that last link should be http://jsbin.com/alada3. Jonathan, could you please edit my previous post and remove this one? Thanks!

"Cowboy" Ben Alman said on December 13, 2009

Jonathan, I wrote a tiny plugin that does just this to help someone in the irc.freenode.net #jquery IRC channel earlier this year. What I did was very similar, except that it did allow stopping and restarting, as well as a "one-off" fade without any kind of loop.

I'm not a big fan of setInterval, for reasons explained on this doTimeout example page, so I've used setTimeout instead. The plugin code is available in the source of the 'fadeQueue ' example page.

This is one of this little plugins that got totally lost in the shuffle.. thanks for reminding me of it!

Cesar said on December 13, 2009

Amazing trick with such tiny amount of code, thanks for sharing.

Matthew Simmons said on December 13, 2009

It's funny that you posted this. I've been looking for a plugin to do exactly this. I'm still in the beginning stages of learning jQuery so I couldn't do it myself.

again, thanks!

Rio said on December 13, 2009

I'm use this code with jquery tools slider. It seems conflict mixing with jquery tools because it only fade one time and then stop.

What solution for this?

Babar O'Cap said on December 14, 2009

Check out Slidedown.
It Generates syntax-highlighted slides from Markdown.

http://github.com/nakajima/slidedown

Joe Kirkson said on December 14, 2009

Good stuff Jonathan, you make it seem so effortlessly easy!

Rio said on December 14, 2009

Forget my previous comment, I found the problem :]

Your code is running smooth right now, It's a miracle!

Hey said on December 14, 2009

What

Ben Tortora said on December 14, 2009

You would not believe how much time I have spent looking for something like this to solve my problem.

This should be the default setup for anyone looking for a photogallery solution

johan said on December 16, 2009

For a port to mootools, would be great to have that...

nate said on December 17, 2009

I was trying to make it work when each image is a link. Any suggestions? Thanks!

Frank said on December 20, 2009

Wow. That's what I call lightweight. Thank you!

e-sushi said on December 20, 2009

Just an idea... I tend to do jquery stuff completely internal; meaning, I also handle all CSS stuff using jquery, making the modding of yet-another-file (the css file) void.

I'm not sure if everyone will agree, but I am convinced that it strips some bytes from the main css and it only uses the jquery-needed css rules if the user has javascript enabled.

You might want to try it... it downgrades more beautifull and surely is a bit more minimal when those robots come haunting your site for more search-engine food.

Nice site btw. - and I sure like that "preview" while writing my comment. It simply rocks!

Cara Mico said on February 03, 2011

Love this script, use it on my own site! Thanks, if only all code was this concise!

Pete said on February 04, 2011

Anyone know how to add links to the images or the whole thing?? Its exactly what i'm looking for if it had that!!

Pete said on February 04, 2011

found a way to link the whole thing, not ideal but basically adding onclick to the surrounding div. If anyone has a cleaner way let me know: http://www.shore.co.uk/snow/snow-jackets/ladies-snow-jackets.html

Dan said on March 07, 2011

It's quite easy to do. Just add a link around each image have the links fading back and forth instead of the images. The css would look something like this:


.fadein a {display:block;}


And the Javascript would look like this:

$(function(){
    $('.fadein a:gt(0)').hide();
    setInterval(function(){
      $('.fadein a:first-child').fadeOut()
         .next('a').fadeIn()
         .end().appendTo('.fadein');},
      3000);
});

Cat said on March 12, 2011

I am new to jquery and was wondering can anyone explain what the gt(0) in this line of code $('.fadein img:gt(0)').hide();
stands for?

anthony said on March 12, 2011

How to do this with img's of different sizes?

Jonathan Snook said on March 13, 2011

@Cat: it means 'greater than' zero. That means get every image except the first one and hide it.

@Anthony: You can do all sorts of different sizes, you just need to make sure that you've reserved room for the largest image. If you wanted it to resize the space for each image, that's a little more complicated. You'd have to get the dimensions of the image and then resize the container around it.

Richard said on March 15, 2011

Thanks for this although I'm having trouble with jquery 1.4.2 here: <http://www.hakomimallorca.com/about-mallorca>

It seems to skip through the images once then stops at the final one instead of looping.

Richard said on March 15, 2011

Please disregard my previous comment...thanks

Sam Rogal said on March 23, 2011

Sir, you are gentleman and a scholar.
I've been wrestling with slideshows of one kind or another for the last six ours trying to meet a deadline for a client. Everything else was breaking the layout with the styles being dynamically applied or the use of static positioning.
This was simple, clean, and elegant. Perfect.
A million thanks to you.
-Sam

anitha said on March 24, 2011

I need to add a play/pause, forward and backward to this jquery. Could you please help me with this. Is it possible to do that? Can I use this code to my website. Is it legal?

Daniel said on March 24, 2011

Hi! I've been looking for a slideshow like this, but, at the same tiem i've downloaded a lot of them, by the way this one is the most practic one, congrats!!!, i would like to ask you a thing, why all of them are constructed with PA Divs and not with simple Divs??? If you could answer me i'll be very grateful.
THNKS!!!

Jonathan Snook said on March 24, 2011

@Daniel: What's a PA div and how does it differ from a simple div?

Adham Dannaway said on March 24, 2011

Nice one, very simple. Sometimes a whole library just isn't needed.

Dodgem Rider said on March 28, 2011

Great solution. Was trying to work out the neatest way to advance the slides with a mouse click (instead of via "set Interval"). Similar to the query from Anitha (above). Any ideas on this would be appreciated. I now there are many other slideshows out there, but having the images in a single div with a class is potentially great for use with a CMS.

Geoff said on April 26, 2011

I think Daniel is trying to say AP div. Absolute positioned div.

Thanks for the easy script.

smita said on April 28, 2011

I am unable to see this slideshow when i add jquerymobile css

smita said on April 28, 2011

Please help

Muhammad Adnan said on May 05, 2011

thx for so simple & easy slideshow script.......plz help me in tab switching and drop down menu......thx in advance.

Robert Cox said on May 07, 2011

Just wanted to thank the creator for this simple slideshow, and for helping me learn how to make slideshows in general (web student for almost year and a half now).

Ian said on May 10, 2011

Hi, thanks for the code. If I wanted to add a pause to the slideshow, so when a user hovers on an image the show pauses and on mouseout the show resumes how would I go about doing this?

I've tried adding onMouseOver and onMouseOut attributes but I'm not really a coder and haven't got very far!

Giselli M. said on May 10, 2011

AMAZING! Thank you so much :)

Jess said on May 12, 2011

This is absolutely amazing, and exactly what I needed. I'm a newbie at js and jQuery, so please forgive the intro answer. Is there any way to randomize the order that the images are displayed in, while keeping the multiple slideshows on the same page? TIA!

Jess said on May 12, 2011

One more question - yes, I'm still a newbie. Is there any way to adjust the timing on the slideshows so that they look like they are daisy chaining - so a second slideshow on the page starts a few miliseconds later than the first? TIA!

jcnapw said on May 18, 2011

I thought it would be easy to find a clean-coded fade slideshow... boy was I wrong. I'm a novice at programming to begin with but after hours of searching I was really feeling like an idiot. Thanks so much for this script!!

Sarah said on May 19, 2011

Is there a way to randomize the images ??

Sorry, comments are closed for this post. If you have any further questions or comments, feel free to send them to me directly.