slides and bubbles (jquery event delegation)

I wanted to improve my slideshow with image preloading. What I wanted was something that resembles a queue for preloading.

On a blog I found a piece by Rebecca Murphey, she seems a very smart programmer. I picked up on the idea of triggering events, then read some remarks on binding data to an element, and on event delegation.

Being a noob, I concocted thus :

Each element I use to run the ‘slideshow’ has three basic functions,

  • AjaxLoadImages : loads the xml data ( image urls) into $(elem).data()
  • displayImage : replaces the css background-image url
  • preLoad : creates an img element in the document with the image url as source, to force the browser to download the picture so once I put it in the css background the image file is already downloaded, that stops the flickering

The latter two run on a timer, more about that later on in the post.

With bind(), I tie the event handler function to an element, and can use my own events. I can delegate the handling of events up the dom tree by leaving the handler of the lower elements void and binding a handler with the same name to a higher level element, like body.

  1. $("div").bind('preLoad', function(e) {});
  2. $("div").bind('displayImage', function(e){});
  3. $("div").bind('AjaxLoadImages', function(e){});

In ‘bubbling up’ the element data of the element where the event was triggered is sent along. I can access that elements .data() (with my image list etc.) through the generic $(event.target) object.

  1. //AjaxLoadImages is a http-get that retrieves
  2. //an xml file, and adds the links in it to the .data()
  3. //attribute of the div
  4.  
  5. //I trigger the event on the div, which is handed upward
  6. //to this function, with the div as 'target'
  7.  
  8. $("body").bind('AjaxLoadImages', function(e, fname) {
  9.  var target = $(e.target);
  10.  
  11.  $.get(fname, {}, function(xml)  {
  12.   var counter = 0;
  13.   $('entry', xml).each(function(i) {
  14.        counter++;
  15.                      //key = (x + counter)
  16. $(e.target).data(('x'+counter).toString(), $(this).find("link").text());
  17.   });
  18.  
  19. //some general data
  20.   $(e.target).data("preload", "0");
  21.   $(e.target).data("currentimage", "1");
  22.   $(e.target).data("countimage", counter.toString());
  23.  });
  24. });
  25.  
  26.  
  27. //displayImage retrieves the 'currentimage' value
  28. //which, with an added X,
  29. //is the key of the imageurl in the .data() element
  30. //(which is key=>value array)
  31.  
  32. //Once I have it, I can replace the background image url
  33. //in the css
  34.  
  35. $("body").bind('displayImage', function(e) {
  36.  var target = $(e.target);
  37.         $(e.target).css('background-image', 'url(' + $(e.target).data(('x'+ target.data("currentimage") ).toString()) +  ')');
  38. });  
  39.  
  40. //preload does the same trick, with the source attribute
  41. //of an image I add to force the browser to preload
  42. $("body").bind('preLoad', function(e) {
  43.     $('<img />').attr({
  44.     src: $(e.target).data(('x' + $(e.target).data("currentimage")).toString()) });
  45. });

the xml file is plain :
<?xml version=”1.0″ encoding=”UTF-8″?>
<xmlslides>
<entry>
<link>http://www.juust.org/slides/one.png</link>
</entry>
<entry>
<link>http://www.juust.org/slides/two.png</link>
</entry>

</xmlslides>

That sets up the basic data and functions for the div elements.

Now to make it run.

Timing, even with the SetInterval method, was gonna be a pain as it seems I can not tie SetInterval  to an element. Luckily (blair mitchelmore?) was so kind as to write a jQuery plugin to handle multiple timers.

With that, I can write one event handler on body level that assigns each element that comes bubbling up a function on a timer interval to handle the display and preloading, as if it were a loop.

  1. //binding the event with void handler
  2. $("div").bind('ScheduleSlides', function(e){});
  3.  
  4. ///…then binding the actual event handler to body,
  5. //param holds the interval
  6. $('body').bind("ScheduleSlides", function(e, param) {
  7.  
  8.  var target = $(e.target);
  9.  
  10. //runs a function on set intervals for the target element
  11.  target.everyTime(param, function(i) {
  12.  
  13. //display the current image by triggering displayImage,
  14. //which bubbles up to the body event handler
  15.   var imgcount = target.data("currentimage");
  16.   target.trigger('displayImage');
  17.  
  18. //increase the functions internal counter
  19.   imgcount++;
  20.  
  21. //set currentimage to the increased counter
  22.                 target.data("currentimage", (imgcount).toString());
  23.  
  24. //if at the end of the image list,
  25. //reset currentimage
  26. //and end preloading
  27.   if( target.data("currentimage") == target.data("countimage") ) {
  28.    target.data("preload", "1");
  29.    target.data("currentimage", "1");
  30.   };
  31.  
  32. //preload the new image if necessary
  33.   if(target.data("preload") == "0") {
  34.    target.trigger('preLoad');
  35.   };
  36.  });
  37. });

All that remains is to load an xml file and trigger the scheduling…

  1. $("#dia_show").trigger(
  2.  "AjaxLoadImages",
  3.  "http://www.juust.org/slides/a.xml"
  4. );
  5. $("#dia_show").trigger('ScheduleSlides', 1000);

and another one…

  1. $("#dia_show_2").trigger(
  2.  "AjaxLoadImages",
  3.  "http://www.juust.org/slides/b.xml"
  4.     );
  5. $("#dia_show_2").trigger('ScheduleSlides', 2000);

I really wonder why I wrote it that way, but it seems to work without the flickering bit and without having to code the links in css or in the source.

I put the test page and the source of it on the server.

I read some remarks on other blogs that after adding 14 img elements IE throws a stack overflow, I haven’t looked into that.

Posted in juust and tagged .

Leave a Reply

Your email address will not be published. Required fields are marked *