More than one way to skin a jQuery
Joseph McCullough, a frequent and welcomed commenter here, is doing a redesign of his website (which you can view here.). At the moment he has a construction page up that consists of one div, which contains an img, an h1, a p tag, and a link. Those three tags fade in one after another in sequential order. Curious, I checked his source code and looked at the jQuery he wrote to get it to happen. We obviously think differently but both of our approaches got the job done.
UPDATE: Joseph has created a plugin for a sequential fadin, that you can get here.
One of the reasons I love jQuery is the fact that there are many ways to do one thing. You can take ten people, tell them to make a certain thing happen, and you will most likely get ten different pieces of code.
Let me show you exactly what I mean here. So with the above example we have a wrapper div holding three elements.
Joseph’s html simplified:
<div id="wrapper">
<img></img>
<h1></h1>
<p></p>
<a></a>
</div>And here is my simplified html after I decided to do this little trial:
<div id="container">
<div></div>
<div>
<ul>
<li></li><li></li><li></li><li></li>
</ul>
</div>
<div></div>
</div>With that out of the way, Let’s take a look at the jQuery. Here is what Joseph wrote to get the effect to work:
$(function(){ var wrapper = $("#wrapper > *"); $.fn.blockHide = function() { return $(this).animate({'opacity' : 0},0).css({'display' : 'block'}); } $(wrapper).blockHide(); fadeInAll($(wrapper), 500, 500); function fadeInAll(elem, fadeInTime, timeBetween) { timeBetween = typeof(timeBetween) == 'undefined' ? 0 : timeBetween; fadeInTime = typeof(fadeInTime) == 'undefined' ? 1000 : fadeInTime; for(i=0; i<elem.size(); i++) { $(elem[i]).delay(i*(timeBetween+fadeInTime)).animate({'opacity' : 1}, fadeInTime); } } });
And here is what I wrote:
$(document).ready(function() { var faders = $('#container').children().hide(); i = 0; function awesomeFaders() { $(faders[i++]).delay(500).fadeIn(2000, arguments.callee); }; awesomeFaders(); });
I am going to go out on a limb here and say that Joseph might be thinking of making this into a plugin by the way he has coded it. He has timeBetween and fadeInTime if equal to undefined, set to a default number, otherwise = to, well itself. I’m guessing that somewhere along the line you would be able to set that value.
Either way, both sets of code are doing the same thing. I hide the children of #container, while Joseph sets the opacity to 0. Joeseph uses a for loop with animate to set the opacity to 1, while I fade them in.
Two different ways to do the exact same thing. Neither right or wrong. My point is, there are so many options in jQuery, it can make your head spin. But that is what makes it awesome. It is powerful, and extremely flexible.
Want to see this done in 4 other ways? Then take a look here at Paul Irish’s page.
jQuery junkBox
.insertBefore(selector);
This method places the matched elements before each of the elements matched by selector. In example below, it inserts the p tag before #moreContent.
$('<p>Some content goes here</p>').insertBefore('#moreContent');
Tags: jQuery






Great post Jeremy. There was an exact reason why I avoided the Fade’s (fadein, fadeto, etc)though, and why I made that blockHide method.
For obvious reasons, I wanted the CSS for the elements to be at display: none in the beginning.
Since images are inline elements, you have to change them to display: block to make them line up correctly when you’re positioning with margins.
However, the moment you make it display: block, it appears, because it’s obviously overriding the display: none.
I was originally using fadeTo, but the final position of the elements after using fadeTo was about 10px off from where things were positioned without the animation taking place.
Oddly enough, the Fade’s are supposed to turn an element into display:block. Firebug agreed with me though, and showed that the img was coming in as display: inline.
When you .hide(), it seems as jQuery is resetting it back to its default display.
You can verify this by commenting out my $(wrapper).blockHide(), and you’ll note the position shift.
So what .blockHide does is loads the element to an opacity of 0, which is DIFFERENT than it being set to display: none. This is because interestingly enough, I found that opacity 0 means that the element is loaded and has all its styles attached, but is 100% see through. While display: none means the element is not displayed at all.
Therefore, while it is impossible to have .hide() and display: block for an inline element, it IS possible to have an opacity of 0 and a display: block for an inline element.
That aside, I’d like to understand this arguments.callee business and what exactly i++ is doing there, haha. Your solution is much more elegant, and the only thing I’d change would be incorporating my .blockhide.
Note: For whatever reason, the animation gets jerky at the end on IE8.
The arguments.callee is a callback equal to the function itself. If you take that out of the code, it will only show the first div, since it only runs through the function once. After the fadeIn occurs on the first div, the arguments.callee pretty much says run through the function again.
Without the counter, the whole damn thing will show everytime (meaning ALL the children div at once). The i = 0 you obviously know sets the counter at 0. The faders variable is basically returning an array of whatever matched my selection. In this case all the children of the container div. So I basically have an array of [div, div, div]. The first time the function runs through, it does the whole fading thing on the index of 0 (first div), hits the arguments.callee, which says run the damn thing again, this time ‘i’ gets incremented to 1 (2nd div) and the process repeats until it gets through the array.
Whew….thank you for actually making me explain that, it solidified what I was thinking. Does that make sense now?
Yup! Makes sense. However, I’m kind of confused on something.
Whenever I was dealing with looping and delays, I could not get delays to be treated as relative to one another, but only in absolutes in terms of when javascript executes the code.
So putting delays in a for loop or recursive function caused everything to show up at once, just because javascript executes the code so fast. That’s why in my loop I had to multiply eveything by i.
Ok, you have used words here that make me feel stupid, but I think what you want to use is the queue() method. So you would have the fadeIn, add the queue() with a function in it calling the delay, then dequeue() method directly after. The queue method will add non-effect methods into the chain, and adding dequeue() will allow the animation or chain to continue where it left off.
So for example, you would have fadeIn(‘slow’).queue(function() {$(whatever).delay(’2000′).dequeue();}).fadeOut(‘slow’);
I think I’ve figured it out: Callback functions put delay times in relation to one another because the callback function does not occur until the script has finished. That sounds like a “no duh”, but hear me out.
For example:
Let’s say we have
var wrapper = $(“#wrapper”)
And we want to fade in each element of the wrapper one after another.
This LOOKS like it would work
for (var i=0; i<$(wrapper).length; i++)
{
$(wrapper[i]).delay(1000).fadeTo(500, 1)
}
It looks like that this code will make each successive element appear after 1000ms. But that's not how javascript works. This code makes everything come up at once. Why? The delay is in terms of the time of script execution. JavaScript executes these scripts lightning fast: It can execute that entire for loop in a tenth of a second. Consequently, JavaScript sees it like this
wait one second and fade in element at index 0
wait one second and fade in element at index 1
wait one second and fade in element at index 2
Which means that the animations are going to happen at the same time, because the for loop was executed so fast.
Since we obviously can't (or shouldn't) slow down how fast JavaScript executes code, there are 2 ways to accommodate this fundamental of JavaScript, and this is where our examples differ.
My way) Have the delay time multiplied by an incremental amount so it increases every loop.
JavaScript will see it this way
wait 1000ms and fade in element at index 0
wait (1000*2)ms and fade in element at index 1
wait (1000*3)ms and fade in element at index 2
Assuming you wanted to wait one second in between each animation.
This looks like
for (var i=0; i<$(wrapper).length; i++)
{
$(wrapper[i]).delay(i*1000).fadeTo(500, 1);
}
However, to have an initial delay, you would have to place in an offsetting value. This offset value has to be applied to every single subsequent execution, otherwise the intervals between the animations won't be even.
That looks like
for (var i=0; i<$(wrapper).length; i++)
{
$(wrapper[i]).delay(i*1000+3000).fadeTo(500, 1);
}
That means that the animations will come in one second after each other with an initial 3 second delay.
Now here is your way, which is MUCH better. This goes along the lines of what we were talking about: If I don't know the methods to get the job done, I can break it down into math concepts.
Your Way)
It seems that javaScript adds callback functions to a queue, while statements in a for loop are executed immediately. My for loop gave the impression of a delay, but in fact all statements generated by the for loop were executed at just about the same time.
But the callback function will not execute until its caller function is completely finished. So while my forloop statements were executed at just about the same time, JavaScript holds off on callback function execution until the calling function is complete. This way, you can use a set a CONSTANT value for delay, because javaScript does not even execute that code until after a period of time.
Hope that makes sense!
Well Geez, I just made a comment that could have been a blog post itself! lol
In short….yes. Mine works because it is NOT a loop. Mine goes through the function, at the end of the function, do the function again, but this time ‘i’ is incremented. So I’m not looping, I’m just calling the function over and over, until I’m through the array of elements (divs in my case). You are saying. Do this to each, which does it all at the same time. But you wrote in the code to get around that.
[...] be interested in seeing a working example of a Sequential fade-in effect. If you are, head over to The Web Machine and view Jeremy Carlson's solution. Filed under: JavaScript/jQuery Leave a comment Comments [...]
[...] it be cool if I animated it a little? In fact, why not do something that I did a few posts ago (can view post here), and move each cog left while fading in. Whatever I come up with for the design, won’t look [...]
[...] be interested in seeing a working example of a Sequential fade-in effect. If you are, head over to The Web Machine and view Jeremy Carlson’s solution. Subscribe via [...]
Hi,
Is there anyway to make this fading effect loop? I want the fade effect to happen until the user has left the page. Also, my first item in the list is fading in and out twice before the function moves on to the next item. Any help would be greatly appreciated.
Thanks!
Erika