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.

        
$("div").bind('preLoad', function(e) {});
$("div").bind('displayImage', function(e){});
$("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.

//AjaxLoadImages is a http-get that retrieves
//an xml file, and adds the links in it to the .data()
//attribute of the div

//I trigger the event on the div, which is handed upward
//to this function, with the div as 'target' 

$("body").bind('AjaxLoadImages', function(e, fname) {
	var target = $(e.target);

	$.get(fname, {}, function(xml)  {
		var counter = 0;
		$('entry', xml).each(function(i) {
		     counter++;
                     //key = (x + counter)
$(e.target).data(('x'+counter).toString(), $(this).find("link").text());
		});

//some general data
		$(e.target).data("preload", "0");
		$(e.target).data("currentimage", "1");
		$(e.target).data("countimage", counter.toString());
	});
});


//displayImage retrieves the 'currentimage' value 
//which, with an added X, 
//is the key of the imageurl in the .data() element
//(which is key=>value array) 

//Once I have it, I can replace the background image url
//in the css

$("body").bind('displayImage', function(e) {
	var target = $(e.target);
        $(e.target).css('background-image', 'url(' + $(e.target).data(('x'+ target.data("currentimage") ).toString()) +  ')');
});		

//preload does the same trick, with the source attribute
//of an image I add to force the browser to preload
$("body").bind('preLoad', function(e) {
    $('').attr({
    src: $(e.target).data(('x' + $(e.target).data("currentimage")).toString()) });
});

the xml file is plain :
<?xml version=”1.0″ encoding=”UTF-8″?>
<xmlslides>
<entry>
<link>https://www.juust.org/slides/one.png</link>
</entry>
<entry>
<link>https://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.

//binding the event with void handler
$("div").bind('ScheduleSlides', function(e){});

///...then binding the actual event handler to body,
//param holds the interval
$('body').bind("ScheduleSlides", function(e, param) {

	var target = $(e.target);

//runs a function on set intervals for the target element
	target.everyTime(param, function(i) {

//display the current image by triggering displayImage,
//which bubbles up to the body event handler
		var imgcount = target.data("currentimage");
		target.trigger('displayImage');

//increase the functions internal counter
		imgcount++;

//set currentimage to the increased counter
                target.data("currentimage", (imgcount).toString());

//if at the end of the image list,
//reset currentimage
//and end preloading
		if( target.data("currentimage") == target.data("countimage") ) {
			target.data("preload", "1");
			target.data("currentimage", "1");
		};

//preload the new image if necessary
		if(target.data("preload") == "0") {
			target.trigger('preLoad');
		};
	});
});

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

$("#dia_show").trigger(
	"AjaxLoadImages",
	"https://www.juust.org/slides/a.xml"
);
$("#dia_show").trigger('ScheduleSlides', 1000);

and another one…

$("#dia_show_2").trigger(
	"AjaxLoadImages",
	"https://www.juust.org/slides/b.xml"
    );
$("#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.

faking links & dblclick search

Being a noob at jquery, I was very satisfied with these two : the first is a one liner to search the blog when someone doubleclicks on a word, the second is an easy fake anchor.

double click search

If you double click (in some browsers?) you select the word under the cursor. Bind the double click event of a paragraph element to a function that uses the standard worpdress search, using bloginfo(‘home’) to get the home url, with the selected text as ?s= search parameter, and relocates to the search result page.

jQuery(document).ready(function() {
	jQuery('p').bind('dblclick', function(e){
		window.location = '<?php bloginfo('home'); ?>/?s='+getSelectedText();
    });

});

function getSelectedText(){
    if(window.getSelection){
        return window.getSelection().toString();
    }
    else if(document.getSelection){
        return document.getSelection();
    }
    else if(document.selection){
        return document.selection.createRange().text;
    }
}

fake a link

You can also fake a hyperlink, makes seo life easier.

Any surplus inline element will do, in this case the dfn tag, as most have the title attribute that I use to store the url  :

<dfn title="http://elgoog.rb-hosting.de/index.cgi?dir=/Top/News/&amp;page=Satire/">hyperlink</dfn>

Style the dfn element with a bit of css :

dfn {
 	color: blue;
	text-decoration: underline;
	font-size: 14px;
}

…and it looks like a hyperlink, add some jQuery to bind a click event on the dfn element to a function that jumps to the title attribute.

jQuery(document).ready(function() {
	
	jQuery('dfn').bind('click', function(e){
		window.location = jQuery(this).attr('title');
    });
});

…and we have the link that search engines do not index as a link, but it does work.

 

masked slideshow – jquery & wordpress

I agreed to make a wordpress template for a friends’ site. As extra feature they wanted a slide show in the sidebar with a half transparent mask.

I trolloped around the net a little and this seems the most basic sidebar slideshow :

 
 

I make a new 240×240 image in photoshop and use a radial gradient from transparent to white, making the center of the picture transparent, and export it as ‘mask.png’, 24bit.

In my sidebar_left.php I make three divs, one is the container dia_frame,

the other two are the overlay and the slideshow div.

<div id="dia_frame">
      <div id="dia_show"></div>
       <div id="dia_overlay"></div>
</div>

I give he frame the z-order 90, the mask the z-order 92, the slideshow is z-order 91. That makes sure the overlay is rendered on top of the slideshow.

Both the overlay and show div are relative positioned. 

 

#sidebar_left #dia_frame {
  width: 240px;
  min-height : 240px;
  _height : 240px;
  clear: both;
  z-index: 90;
}

#sidebar_left #dia_frame #dia_show {
	width: 240px;
	min-height: 240px;
	_height: 240px;
	position: relative;
	float: right;
        z-index: 91;
}

#sidebar_left #dia_frame #dia_overlay {
	width: 240px;
	position : relative;
	background: url(images/mask.png) no-repeat; 
  	min-height : 240px;
 	_height : 240px;
	z-index: 92;
}

That sets up the xhtml and css frame in the side bar. Then I go make the slideshow. I grab some graphics of flowers and store them in the themes images folder.

With jQuery in header.php I make an array containing the image urls, and with a timer, cycle the slideshow background graphics :

jQuery(document).ready(function() {
var allImgs = [];
	
	allImgs[0] = "/images/one.png";
	allImgs[1] = "/images/two.png";
	allImgs[2] = "/images/three.png";
	allImgs[3] = "/images/four.png";
	allImgs[4] = "/images/five.png";

    var currentSlide = 0;
 
 	jQuery.mynamespace = {};
	jQuery.mynamespace.counter = currentSlide;
	jQuery.mynamespace.imgs= allImgs; 
 
    function imgRotate() {
	
        jQuery('#dia_show').css('background', 'url(' + jQuery.mynamespace.imgs[jQuery.mynamespace.counter] +  ') no-repeat right');
		jQuery.mynamespace.counter++;
		if(jQuery.mynamespace.counter > 4) { 
			jQuery.mynamespace.counter = 0;
		};
    }
 
    setInterval(imgRotate, 2000);

});

 

That’s the basic setup

IE5 and IE6 dont support PNG transparency, so for these there are a number of workarounds. supersleight by drew mclellan is reprogrammed to work with jQuery, for wordpress that seems the best option.