jQuery delay() not working for you?

jQuery’s delay() function causes more than its fair share of problems over on Stack Overflow. You often see people trying to use delay() to delay something like* setting a CSS property (via css()), and then wondering why their CSS property updated immediately; irrespective of their use of delay().

$('#some-element').delay(5000).css("backgroundColor", "red"); // this won't work

*: Other common problems originate from trying to perform DOM manipulation (via the likes of append(), after() etc, or the setting of HTML/ values (via html(),text() or val()).

The short answer is to basically use setTimeout() instead. The long answer involves looking more into how jQuery queues these requests, and finding out exactly what delay() does…

Why it doesn’t work…

When you call delay(5000), what you’re actually doing is calling delay(5000, "fx") as the function has an optional second parameter (the queue name you wish to delay operation of) which defaults to fx. The various effects methods (fadeOut(), animate() etc.) all use this queue which is why delay() has an effect on them.

However nothing else in jQuery does (by default at least, but we’ll get to that part shortly). That is to say that calls to any manipulation functions (html(), append(), css() etc.) will completely ignore your delay() call, because they’re are all executed immediately, and not queued at all.

How you can fix it…

The recommendation here is to use setTimeout() instead; your code will end up looking like this:

setTimeout(function () {
    $("#some-element").css("backgroundColor", "red");
}, 5000);

However, if you wish to use delay(), you can use the queue() function to queue something. The signature of the queue() function is as follows;

queue([queueName,] functionToBeExecuted(next));

That is to say, the first argument is the name of the queue you wish to add to (which defaults to fx when not specified), and the second argument is the function you wish to queue. That function gets passed one parameter, which you must call when your function has finished doing whatever it needs to do; this is to ensure the next elements in the queue get executed as well.

Therefore we end up doing something like this;

$('#some-element').delay(5000, "my-queue").queue("my-queue", function (next) {
    $(this).css("backgroundColor", "red");
    next();
});

Note that in the above example, we’ve specifically avoided the fx queue; the changing of the background-color is not related to fx at all, so we shouldn’t abuse the use of that queue. Having said that, there are times when queuing to the fx queue is useful; such as when you want to perform an action after a list of effects have completed;

$("#some-element").fadeIn(200).animate({
    padding: "+=50"
}, 1500).delay(1000).queue(function (next) {
    $(this).css("backgroundColor", "red");
    next();
});

Of course, if we wanted to be very explicit about which queues we were using, it’d look something like this:

$("#some-element").fadeIn(200).animate({
    padding: "+=50"
}, 1500).delay(1000, "fx").queue("fx", function (next) {
    $(this).css("backgroundColor", "red");
    next();
});

For further reading after finishing the article, consider reading the delay() documentation or the Stack Overflow question that triggered me writing this article!