Sunday, September 19, 2010

jQuery Slideshow

Presented below is a jQuery slideshow, with the code discussed below that. Originally, this was based on Rodrigo Silveira's Javascript Prototype Slideshow, re-written for jQuery. But in Chrome I was witnessing a disturbing flicker (that not all Chrome users were seeing) and alternative code was suggested by Peter Ajtai in my StackOverflow post, Why is this slideshow flickering? This post now uses and presents Peter's adaptation of my re-write of Rodrigo's code. And they say there is nothing new in the world?

Here is a standalone version without the chaff from before the write, and here is a standalone version without the chaff showing the current code.


Here is the code.

First up, you need to import the jQuery API.

<script type="text/javascript" src="http://www.google.com/jsapi?key=ABQIAAAAoDEIY_vXge_LQOEVgHyheBSvIISGg2D4cAMKlpvPZkPgQSL0sRRGyBerqkXeyllTDvkGdlqzeYPWKA" ></script>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js"></script>

What's with the jsapi?key and the imports from ajax.googleapis.com? Google (being the thoroughly non-evil company that they are) offer several well known Javascript APIs for public use on websites other than their own i.e. they host them so you don't have to. They ask that you register for (free) and use an API key specific to your site so they know ... whatever it is they want to know. Read more about this on the Google Libraries API - Developer's Guide. This is perfect for things like Blogger i.e. services for which you cannot upload your own scripts or other arbitrary files.

Next is the Javascript, written against jQuery.

<script type="text/javascript">
   // Contain all your functionality in a self calling anonymous
   // function, so that you don't clutter the global namespace.
   (function() {
      // ------
      // ###### Edit these.
      // Assumes you have images in path named 1.jpg, 2.jpg etc.
      var imagePath = "images";
      var lastImage = 5;         // How many images do you have?
      var fadeTime = 4000;       // Time between image fadeouts.

      // ------
      // ###### Don't edit beyond this point.
      // No need for outer index var
      function slideShow(index) {
         var url = imagePath + "/" + index + ".jpg";
         // Add new image behind current image
         $("#slideShow").prepend($("<img/>").attr("src",url));
         // Fade the current image, then in the call back
         // remove the image and call the next image.
         $("#slideShow img:last").fadeOut("slow", function() {
            $(this).remove();
            setTimeout(function() {
               slideShow((index % lastImage) + 1)
            }, fadeTime);
         });
      }
      $(document).ready(function() {
         // Img 1 is already showing, so we call 2
         setTimeout(function() { slideShow(2); }, fadeTime);
      });
   })();
</script>

Note that we are creating the second image within this Javascript ($("#slideShow").prepend($("").attr("src",url));), so the HTML only needs to define one image at the start.

Here is the CSS needed to render two images on top of each other.

<style>
   #slideShow {
      position:relative;
      width: 500px;
      height: 500px;
   }
   #slideShow IMG {
      position:absolute;
   }
</style>

And lastly, the HTML - you need to fill in the first image yourself.

<div id="slideShow">
   <img id="slideShowBack"  src="images/1.jpg" />
</div>

One last important note (at least to this post). Part of the reason for the flickering I witnessed in Chrome was attributed to the re-rendering of some fancy border related CSS on the IMG elements. The fix was to remove the CSS from the IMG and put it on a surrounding DIV instead. Since the CSS is coming from my Blogger template, I had to first "reverse" the template's CSS, and then add it to the surrounding DIV. Below is the CSS I used to do just that.

/* Overriding Blog styles to put IMG border on the surrounding DIV. */
.post-body IMG {
   padding: 0px;
   background: white;
   border: none;
   -moz-box-shadow: none;
   -webkit-box-shadow: none;
   box-shadow: none;
   -moz-border-radius: 0;
   -webkit-border-radius: 0;
   border-radius: 0;
}
.post-body #slideShow {
   padding: 8px;
   background: #ffffff;
   border: 1px solid #cccccc;
   -moz-box-shadow: 0 0 20px rgba(0, 0, 0, .2);
   -webkit-box-shadow: 0 0 20px rgba(0, 0, 0, .2);
   box-shadow: 0 0 20px rgba(0, 0, 0, .2);
   -moz-border-radius: 0;
   -webkit-border-radius: 0;
   border-radius: 0;
}