Do you miss the good old days of of the web where you had the <blink>
tag, oh it was wonderful.
Well today I decided that I wanted to bring it back, damnit I want my text to blink!
Thanks to the wonders of jQuery this is a snap to build, in fact here it is:
(function($) {
$.fn.blinky = function() {
var that = this;
function go() {
$(that).fadeOut().fadeIn();
setTimeout(go, 1e3);
};
go();
};
})(jQuery);
Now you can use it just like this:
$('h1').blinky();
Woo, all your h1
elements are going to blink :D.
Breaking it down
To make this work you need to run the code periodically, in my case I’m running it every 1000 milliseconds (1e3
is just a lazy way of doing it, exponents are fun!). You could do this with the setInterval
method, but setInterval
isn’t great, if your code is going to take longer than the allocated time it’ll start again, before the previous has finished!
Instead I’m using the recursive setTimeout pattern, let’s look at that.
Recursive setTimeout pattern
This pattern is cropping up with some of the more popular frameworks, and the idea is that rather executing on a particular interval you execute when the code you want to run is completed.
Here’s a better example:
function doStuff() {
$.get('/foo', function(result) {
//do something with the result
setTimeout(doStuff, 1e4);
};
}
As you can see we’re doing an AJAX get
which may take a while to complete, and once it does complete we’ll do it again in 1000 milliseconds. If I’d been using a setInterval
then there is the possibility of having two running at the same time, since the first hadn’t finished before the second started.
Sexing blink up
So now that we have a working <blink>
simulator, let’s sex it up a bit. Why not make it so we can specify the speed at which it blinks at:
(function($) {
$.fn.blinky = function(frequency) {
frequency = frequency || 1e3;
var that = this;
function go() {
$(that).fadeOut().fadeIn();
setTimeout(go, frequency);
};
go();
};
})(jQuery);
Now you can optionally pass in a frequency you want to blink at:
$('h1').blinky(2e3);
Woo-hoo, delayed blink!
How about we extend it so that you can specify the number of times you want to blink:
(function($) {
$.fn.blinky = function(args) {
var opts = { frequency: 1e3, count: -1 };
args = $.extend(true, opts, args);
var i = 0;
var that = this;
function go() {
if(i == args.count) return;
i++;
$(that).fadeOut().fadeIn();
setTimeout(go, args.frequency);
};
go();
};
})(jQuery);
I’ve also refactored to use an object literal as the arguments, using jQuery.extend, meaning you can do it like this:
$('h1').blinky({ frequency : 2e3, count: 3 });
This will cause the h1
to blink 3 times over the course of 6 seconds, how pretty.
Making it REALLY sexy!
I’m sure you’ve heard by now that jQuery 1.5 is out, and one of the new features of jQuery 1.5 is the Deferred
object. Full API doco is here, but the short of it is that Deferred
is how the new AJAX API works (I suggest you check out the jQuery doco for the best explanation of it), but one of the really cool things is that you can use Deferred
in your own API, so that when your operations finish it can raise a done
or fail
method, depending on what is happening.
Since we have the ability to specify a number of times to execute our blink will occur why not use Deferred
to call a method when we’re done, seems like a good idea right.
There’s plenty of examples on the web of how to use Deferred
, but here’s a basic example:
function doStuff() {
//create an instance of deferred
var dfd = $.Deferred();
$.get('/foo', function(result) {
//do stuff with result
//success, so we tell the deferred object to resolve itself
return dfd.resolve();
});
//return a promise to be deferred
return dfd.promise();
}
The high level workflow is:
- Create a deferred object
- return a promise, to indicate that the deferred will complete at some point
- in the
get
method when it completes call the resolve method so anyone listening to the deferred will be run
Now let’s add it to our blink method:
(function($) {
$.fn.blinky = function(args) {
var opts = { frequency: 1e3, count: -1 };
args = $.extend(true, opts, args);
var i = 0;
var that = this;
var dfd = $.Deferred();
function go() {
if(that.length == 0) {
return dfd.reject();
}
if(i == args.count) {
return dfd.resolve();
}
i++;
$(that).fadeOut().fadeIn();
setTimeout(go, args.frequency);
};
go();
return dfd.promise();
};
})(jQuery);
With deferred we can now do this:
$('h1')
.blink({ count: 2 })
.done(function() {
$('h1').css('color', '#f00');
});
So once we’ve finished executing the two iterations the blink the h1
will turn red.
We’ve also got code in for a failure, if there was nothing found in our selector it’ll raise a failure method:
$('foo')
.blink({ count: 2 })
.fail(function() {
console.log('aww snap!');
});
Conclusion
Just like this we’ve come to the end of our post. This isn’t really a useful jQuery plugin, in fact <bink>
was a terrible idea, it’s more a way that we can investigate a few interesting points, such as:
- recursive setTimeout pattern for better control over delayed execution
- using Deferred to execute code once we’re done without passing the callback as an argument
If anyone is interested the full code is available as a gist and I’ve created a playground on jsfiddle.