This blog is far from finished, but feel free to enjoy the content.

jQuery: Simulating a delay function between fade in/out effects

Posted: April 1st, 2007 | Author: Panagiotis Karageorgakis | Filed under: jQuery |

jQuery is a very popular JavaScript library among web developers, gaining more and more fans as it evolves. It’s fast, lightweight and does many things while being unobtrusive, meaning that no code has to be incorporated in an (X)HTML document’s body. This way, the designer can write the code, and then go to the document’s head and add a few lines of code to do some fancy stuff with the document. Between the several built-in effects are the popular fade-in and fade-out effects that, due to the chainablity of jQuery can be used sequentially, for a fade-in-then-fade-out effect.

Chainability, is the ability to “chain” different methods on an object, like this:

Chainability in all its glory

$("#foo").show().hide();

which will just show and then hide the element with the id “foo”.

Fade-in-fade-out

Using the concept of chainability, we can achieve a fade-in-fade-out effect easily:

A simple fade-in-fade-out


$(document).ready(function() {
	$("#foo").fadeIn(2000).fadeOut(2000);
});

The above fragment of codefades in the #foo element with a duration of 2 seconds (2000 milliseconds — the argument is in ms), then it fades out the element. The duration of the fade-out effect is another 2 seconds. But what happens if we wanted to have a delay, between the fading-in and fading-out of the element? Say, for example, that we want to fade in the element, then keep it that way for 5 seconds, and then fade it out again.

Intuitively, the “I-dont-read-documentation-but-guess-my-way-around” kind of developer would try to chain a delay effect, like this:

We wish we could do this…


$(document).ready(function() {
	$("#foo").fadeIn(2000).delay(5000).fadeOut(2000);
});

However, this won’t work, simply because there’s not a delay() function. The outcome would be for the element to fade in, and then do just nothing, since the script encounters a function that is non-existent and just stops there. So how can we achieve that?

The trick

Let’s think of what happens during the fadeIn() function: the JavaScript alters the opacity property of an element, from zero to 1, which corresponds to a transition between 0% and 100% of opacity. The fadeOut() function does the reverse: changes the opacity property gradually from 1 to zero. There’s a third function named fadeTo(), which fades the given element to a specific opacity that is passed to it as an argument. For example, the following line of code would make the #foo element go from total invisibility to 50% opacity (the effect will last 3 seconds):

Fade from completely opaque to 50% visible

$("#foo").fadeTo(3000, 0.5);

The trick is to simulate the delay by applying a fade-in (or fade-out) effect that does not change the opacity of an element, but rather spends it’s time doing nothing. The concept is as follows:

  1. Fade-in the element from 0 opacity to 1, for 2 seconds
  2. Fade-in the element again from 1 opacity to 1, for 5 seconds
  3. Fade-out the element from 1 opacity to 0, for 2 seconds

This way, we just insert a transition that last 5 seconds but does not change the opacity of the element, since the target opacity has the same value as in the current condition. The code would just be as simple as this:

The trick is to use the fadeTo() effect!


$(document).ready(function() {
	$("#foo").fadeIn(2000).fadeTo(5000, 1).fadeOut(2000);
});

As Karl Swedberg correctly stated in a comment below, the same effect can be achieved by using the animate() effect, i.e.: .animate({opacity: 1.0}, 3000)

Delaying an effect before it begins

Now, let’s alter the desired effect a little bit. Let’s say that, our foo element is rather shy; it doesn’t want to appear just as the page loads, but would rather give the reader some time to settle in, before it intrudes in the page. A few seconds would be enough for our element to feel comfortably, so we want to give it 3 seconds time until it appears. How do we do this? Intuitively we would expect something like the following code to work:

(This is not the right way to code what we want)


$(document).ready(function() {
	$("#foo").fadeTo(3000, 0).fadeIn(2000)
	.fadeTo(5000, 1).fadeOut(2000);
});

Previewing this in our browser, we see that it’s not what we expected. Foo shows momentarily, then disappears, waits, and then begin to fade in. But why did foo make a quick appearance and then disappear, before it does it’s part? Maybe it wanted to peek and see who the viewer is, but I bet there’s a better explanation, and there is one: the original opacity of it was 1, so fading to 0 creates a transition (a short one) to the target opacity.

[Edit: The following part of this section of the document has been updated to add support for internet Explorer 7]

Here’s a way to achieve this effect. This method has been tested in Safari and Firefox 2 for the Mac, as well as Firefox 2 and IE 7 for Windows. The trick here is to initially hide the element, not by altering it’s opacity, but rather by taking it out of the document flow. This can be done the same way designers choose to hide accessibility text, by creating a class like the following:

Create a .hidden class


.hidden {
	position: absolute;
	top: -10000px;
}

Then all we have to do is this: apply the class to our element, create a fadeTo transition that takes the desired time, remove the class from the element then go on with our normal effects. To make a long story short, in order to have all the aforementioned browsers support this, we must keep in mind two things:

  1. Use fadeTo() effects to substitute fadeIn() and fadeOut() effects (e.g. fadeTo(”normal”, 1) is the same as fadeIn(”normal”) )
  2. Use the remove class attribute as the delaying transition’s callback function

In a nutshell, if we want to have an element delay 3 seconds, fade in, wait another 3 seconds and then fade out, here’s the proper code:

The right way to do it


$("#foo").addClass("hidden").fadeTo(3000, 0, function() {
	$(this).removeClass("hidden")
}).fadeTo("slow", 1).fadeTo(3000, 1).fadeTo("slow", 0);

Now, if anyone’s kind enough to try this with Internet Explorer 6 or below, plase let us know by leaving a comment.

You can see a real world live example of this behaviour in a simple intro page (that has inspired me to write this article). Three seconds after the page loads, an image containing some text appears above the graphic, waits for 2 secs and fades out.


16 Comments on “jQuery: Simulating a delay function between fade in/out effects”

  1. #1 Theo Skye said at 11:06 pm on April 2nd, 2007:

    This delay approach generally does work in IE 6, but there are some (relatively common) instances of opacity changes in IE 6 causing text rendering issues. For example, in IE 6, the text inside a block that you are changing the opacity of may inexplicably turn bold and lose its anti-aliasing, even if you are just changing the opacity from 1 to 1. So it can make text look really bad in some cases. But making sure the block containing the text has a background-color specified in the CSS will usually prevent the problem from occuring. Gotta love IE6! Cheers.

  2. #2 Karl Swedberg said at 3:29 am on April 3rd, 2007:

    Hi there,
    Very nice tutorial. You may be interested in seeing my similar tutorial, Effect Delay Trick. The only difference is that instead of repeating .fadeTo(), for the delay, I use .animate({opacity: 1.0}, 3000)

  3. #3 Panagiotis Karageorgakis said at 10:05 am on April 3rd, 2007:

    Theo, thank you for the IE 6 feedback, I’m glad it works there too.

    Karl, I just saw your tutorial, basically we’re using the same concept; either it’s fadeTo or animate doesn’t matter, so users can incorporate whatever they like, as long as it does nothing that’s visible and takes some time!

    I’d love to see a real delay function though, as it would be quite useful in creating interfaces that alter through time. I haven’t yet researched the potential limitations of JavaScript for such a function, but if there is not one, I hope future versions of jQuery will incorporate one.

  4. #4 Karl Swedberg said at 4:34 pm on April 3rd, 2007:

    Yes, you’re right about them being basically the same. I just thought it was interesting that we came up with the same trick. :)

    As for a real delay function, JavaScript has setTimeout(), which is really no harder than what we’ve come up with, but it doesn’t have the same “chainability” that makes jQuery so convenient.

    I think you might also find the Pause plugin helpful:
    http://www.mythin.net/projects/jquery.php

  5. #5 Brenton said at 11:39 pm on July 10th, 2007:

    Has anybody been able to use the Pause plugin from mythin.net in a chain rather than doing this opacity trick? I may be missing something obvious, but the Pause function never seems to work for me. Examples of it in use would be great. There is no documentation or examples on the developer’s site.

  6. #6 Rob McInnes said at 8:01 am on October 8th, 2007:

    Maybe someone can correct me, but I’ve been testing using setTimeOut today and any fading effects used in the new thread are choppy and inconsistent.

    Also the Pause plugin does seem broken due to a change in the way .dequeue() is used? Or is it because the dequeue command is in a setTimeOut within the plugin code?.. It seems to be a quite widely recognised issue as of October 2007

  7. #7 Webrocker said at 1:19 am on October 27th, 2007:

    @Rob
    changing the line 29 with the dequeue.thingie from
    $.dequeue(self);
    to
    $(self).dequeue();
    “repaired” the pause-plugin for me.
    Props to http://www.nabble.com/jQuery.dequeue-is-not-a-function-t4429225s27240.html

  8. #8 Tom said at 6:04 am on November 10th, 2007:

    Interesting that this works in IE6 but not IE7? The living example posted above doesn’t work either. Anyone have any tips?

  9. #9 Tom said at 6:06 am on November 10th, 2007:

    Meant to say ‘live example’ above, not ‘living example’. Apologies for the double post.

  10. #10 Panagiotis Karageorgakis said at 7:07 am on November 10th, 2007:

    I just did a quick test, the live example does indeed work in IE7, but not when it takes some time to load the images. I tested it over the LAN and it works fine. However, when testing over the Internet (especially today that my internet connection is really slow), it looks like the .ready() function fires up the rest of the script without actually waiting for the images to finish (especially the image that is supposed to be delayed).

    I’ll have to make a plain text-only example so there is no such problem. But the thing with .ready() not functioning the way it’s supposed to be, needs some research too.

  11. #11 links for 2008-01-17 at doug nelson: DISENGAGE! said at 5:40 pm on January 16th, 2008:

    [...] jQuery: Simulating a delay function between fade in/out effects Don’t know if this is the best way or not, but it worked well for my purposes. (tags: jquery javascript) « Learned Today: Incredibly Obvious jQuery Trick. Categories [...]

  12. #12 links for 2008-01-30 « Richard@Home said at 10:17 pm on January 29th, 2008:

    [...] jQuery: Simulating a delay function between fade in/out effects A very useful technique. In essense, between the fades you do something that makes no visible change but takes some time (such as changing the opacity to 1 over 2 seconds) (tags: jquery animation fade delay) Posted by Richard@Home Filed in 15 [...]

  13. #13 Big Burch said at 5:30 am on April 23rd, 2008:

    To make this code work without the flickering of the targeted element, set the element’s css property to (display:none).

    JQuery
    $(document).ready(function() {
    $(”#foo”).fadeTo(3000, 0).fadeIn(2000)
    .fadeTo(5000, 1).fadeOut(2000);
    });

    CSS
    #foo { display:none }

    Works like a charm!

  14. #14 mvpetrovich said at 11:14 pm on July 13th, 2008:

    I do not understand. This works.

    $(document).ready(function() {
    setTimeout( ‘$(”#foo”).fadeOut(2000)’, 3000);
    });

  15. #15 jQuery-Based Popout Ad: Part 2 - CSSnewbie said at 4:54 am on September 17th, 2008:

    [...] I found a solution on a blog titled “Panagiotis Karageorgakis,” and modified it to fit my needs. The basic concept is this: we animate the ad in some way that [...]

  16. #16 David said at 4:29 pm on September 25th, 2008:

    @mvpetrovich: Thank you! That seems to work great for me.


Leave a Reply