Implementing the blink tag using jQuery

8 February 2011 · 5 minute read · jquery · doing-it-wrong · web · javascript

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.


Published: 2017-07-30 20:47:37 +1000 +1000, Version: bfd8bbd