jQuery JavaScript LibraryThe jQuery JavaScript library is a seriously beautiful thing. With a code base that is much smaller than Prototype + Scriptaculous, jQuery can be used on a web site do a whole lot of fancy AJAX stuff with syntax that is surprisingly easy.

At first, jQuery doesn’t look easy. In fact, it looks downright scary. But once you actually dive in, you find that the water is warm and inviting indeed!

If you’ve used jQuery, there is undoubtedly one problem you will encounter: How do you insert a delay between special effects like fadeIn() and fadeOut()??

I found several answers to this question on the web, but I decided not to use any of them. Instead, I decided to leverage jQuery’s built-in callback functionality to get a “delay” in one clean, simple, and super easy step. But, as always, there is a catch…

So, you’ve got jQuery on your site. You decided to add some functionality so that when a user clicks an image, it fades out and another image fades in. You are feeling super cool because it’s so easy to do. You may have done something like this:

$(".thumbs a").live('click', function(event) {
	if (event.button != 0) { return true; }
	var largePath = $(this).attr("href");
	$("#largeImg").fadeOut(100);
	$("#largeImg").attr({ src: largePath });
	$("#largeImg").fadeIn(100);
	return false;
});

So what’s going on here? Well, say you have an image in a container with an ID of #largeImg. There are thumbnails of other images below it. When the user clicks a thumbnail, you want the large image to fade out, the new image should load in its place, and then fade in the new large image corresponding to the thumbnail you just clicked. There is actually a handy example of this here: WebDesignerWall.com jQuery Image Replacement Demo

Now, back to the code…

First of all, you’re using the live('click', function()) method because you have a super-AJAXified page, and you need your thumbnail click function to continue working even after the user has clicked it once and the DOM has been updated. Groovy!

The second thing you’ve done is add if (event.button != 0) { return true; } because unfortunately, the live() function pays attention to ALL mouse clicks, not just left-clicks like the standard click() function. This line makes it only listen to left clicks.

Finally, we get to the nitty gritty details:

  • $("#largeImg").fadeOut(100) grabs the largeImg container, and fades it out in 100 ms
  • $("#largeImg").attr({ src: largePath }) loads the new image into the largeImg container
  • $("#largeImg").fadeIn(100) fades in the now hidden largeImg container, which just happens to contain the new image

So, that’s great, and it works, yes? But there’s one problem. Individual function calls in jQuery are not processed sequentially. While you might think that calling one function at a time will make the image container fade out, and then load the new image, and then fade it back in, that’s not what happens. What happens is that all the functions are run as fast as possible, and you end up with something that resembles the following sequence:

  1. largeImg starts to fade out
  2. new image briefly flashes onto the screen
  3. largeImg finishes fading out with new image in it
  4. largeImg then fades back in with new image in it

That’s not what you want at all.

Now, one of the nice things about jQuery is the ability to chain a bunch of functions together. For the sake of readability and explainability (that’s a word now), I’ve separated the functions in our example onto individual lines.

Anyway, many people on the net recommend getting clever and doing something like this:

$("#foo").fadeIn(2000).fadeTo(5000, 1).fadeOut(2000);

The idea is to use fadeTo() to slowly fade to the current opacity, which to the end user just seems like a 5 second delay with nothing happening visually. Well, that still won’t work the way you’d expect in all cases, and it’s kind of lame coding practice. Besides, it turns out that a there is a very simple way of accomplishing the same thing by using built-in jQuery callback functionality – at least on fast connections (that’s the catch). This is how you might do it:

$(".thumbs a").live('click', function(event) {
	if (event.button != 0) { return true; }
	var largePath = $(this).attr("href");
	$("#largeImg").fadeOut(100, function() {
		$("#largeImg").attr({ src: largePath }).fadeIn(100);
	});
	return false;
});

That’s it. What we’re doing above is defining the “load new image” and fadeIn() parts of the process as a function that will be called only after the fadeOut() part has completed. On a speedy connection, this will make the fading effect look like it should. The old image will fade out, then the new image will load and more or less fade in at the same time.

Of course, on slower connections, this technique may not be terribly useful. You could, however, simply modify the function further to wait for the loading of the image (with, say, a “loading…” indicator), and then fade in the new image – all with a sequence of callbacks. Alternatively, you can use my technique if you preload all the large images and store them in hidden DIVs. Then just fade various DIVs in/out when the thumbnails are clicked.

The point here is not to give you exactly the code you need. Besides, I’ve never had any problems with slow connections and jQuery! Intranet sites are really nice in that respect…

The point is rather that quite often, programming something the “hard way” is a lot better than using a fading function as a delay. That may be clever, but you’re still screwed if you think about it: How long should the delay be?? If I’m on a fast connection, I’m waiting for nothing. If I’m on a really slow connection, the delay won’t be long enough!

So, do things the “hard way” and you’ll be forced to get even more clever. And in the process, you’ll learn all kinds of new techniques and functions that will serve you better in the long run.

Need help? Hire me!
Get Scottie Stuff!