elementReady: a jQuery plugin

Tuesday, 14 August 2007

Please see waitFor, the updated version of elementReady.

I have written a simple but useful jQuery plugin. elementReady calls a function during page load as soon as a specific element is available — even before the full DOM is loaded. It’s useful if you have unobtrusive JavaScript that you want to apply to particular page elements immediately, without having to wait for the whole DOM to load in a large page.

Using the elementReady plugin is very similar to the existing jQuery.ready() function, except that you must also pass in a string representing the ID of the element that should have the function call attached.

This version of the plugin works well for me in testing (and on this page), but it has not been exhaustively tested. I would appreciate your comments on the idea and the implementation.

Download

Get jquery.elementReady.js from the Github project.

Get the latest version from the elementReady jQuery plugin page. Get jquery.elementReady.js here. Then read on for instructions and details.

Documentation for jQuery.elementReady()

$.elementReady( String id, Function fn ) returns jQuery

While a page is loading, call a given callback function as soon as a specific
element is loaded into the DOM, even before the full DOM has been loaded.
Executes the function within the context of the element. This means that when
the passed-in function is executed, the ‘this’ keyword points to the specific
DOM element.

The function returns ‘this’, so you can chain multiple calls to
elementReady(). (Not that there’s much benefit in doing that.)

One argument is passed to the callback: a reference to the jQuery function.
You can name this argument $ and therefore use the $ alias even in
noConflict mode.

If the element has not been found by the time the DOM is fully loaded, then
the function will not be called.

Example:

Change the source of a specific image as soon as it is loaded into the
DOM (before the whole DOM is loaded).

$.elementReady('powerpic', function(){
    this.src = 'powered-by-jquery.png';
});

Example:

If you want to have the jQuery object instead of the regular DOM
element, use the $(this) function.

$.elementReady('header', function(){
    $(this).addClass('fancy');
});

Example:

Chain multiple calls to $.elementReady().

$.elementReady('first',  function(){ $(this).fancify(); })
 .elementReady('second', function(){ $(this).fancify(); });

Example:

Use the ‘$’ alias within your callback, even in noConflict mode.

jQuery.noConflict();
jQuery.elementReady('header', function($){
    $(this).addClass('fancy');
});

Example:

Change the polling interval to 100ms. This only works if $.elementReady() has not yet been called.

$.elementReady.interval_ms = 100;

Why?

I asked about this feature on the jQuery development list, and to my surprise nobody had made much of an effort to do this. jQuery author John Resig has some magic code that makes this happen automatically(!), but including this “auto” feature in the core would complicate things unnecessarily. He suggested that a plugin should be written. So here it is.

Implementation notes

The jQuery.elementReady() function is interesting because it’s quite unlike most other jQuery methods. It’s not a method applied to a normal jQuery object; it can’t be, because a jQuery object is normally constructed from a DOM element (or list of DOM elements). But in this case, all we have is the ID of an element: the element itself does not yet exist.

When the function is called, the matched element is passed to the callback function as a raw DOM element rather than a jQuery object. This is consistent with other event handlers, and is also the most efficient approach.

The function returns the global jQuery object, so you can chain calls to elementReady().

My implementation is inspired by the Yahoo UI library’s onAvailable(), but is simpler (and, to be sure, less well-tested). YUI also includes a related method called onContentReady(), which is supposed to fire when the element and all its content is available. I have not implemented that feature yet, but I think I may end up doing so in elementReady(). This will make it more robust yet still dead simple to use.

I thought about somehow overloading the original ready() function but thought that would just add complexity for no real benefit.

Things to do

This is an early version of the plugin. I’m afraid I have tested it only on Windows browsers, but I have had no problems with it yet. The main thing to do is to test it more thoroughly. As I mentioned above, I also might decide to modify the function to check that the entire contents of the target element are loaded, not just the element itself.

Have your say

Apparently blog posts get x% more comments if the last heading says “Have your say”. So have your say! Is this useful? How can it be improved?

Tags: , ,

231 comments

You can leave a comment, or trackback from your own site.

  1. Wow

    I just realised today that I need this functionality. So I did a google search and found this.

    Then I wonder why there’s no comments for such a cool thing – I check the date and it was posted today !! What great timing : )

    I’ll give it a go now …

    weepy

    *…(

  2. Very Nice function, surprised that this was not included in jquery to begin with — brilliant :).

  3. If you think this should be part of jQuery, you can rate it on the jQuery site:
    http://jquery.com/plugins/project/elementReady

    If enough people use the plugin and rate it highly then it may eventually be included in the jQuery core. Spread the word!

  4. What is difference between your plugin and jQuery .ready() function. I think that each methods doing the same result. I can write:
    $j(‘element’).ready(function);

  5. Zysio, $('id').ready(function) doesn’t do what you think it does. Actually the ‘id’ is pretty much ignored — it’s equivalent to $(document).ready(function). In other words, the function is not called until the entire document has been loaded. That’s why you need to use $.elementReady('id', function): then the function gets called as soon as the element is loaded, even if the document is not completely loaded.

    This makes sense when you think about it: if the element whose id is ‘id’ has not yet been loaded, then $('id') contains no elements. Therefore, if ready applied to individual elements then $('id').ready(function) would have no effect.

  6. Note there was a bug in the original version of elementReady that I posted here. It could cause some elementReady events not to fire if you called elementReady more than once on a page. I have fixed the bug and updated to version 0.5.

  7. Maybe a dumb question: how can I check to see if two ID’s have been loaded? I need to run a function after two iFrames have been loaded and I don’t think it’s the same as chaining multiple calls…or maybe it is.

    Any help is appreciated.

  8. elementReady works on individual elements only. To do what you want you’ll need to do a little extra. Something like this:

    var isBothLoaded = false;
    var check12 = function() {
      if (!isBothLoaded && $('#id1') && $('#id2')) {
        isBothLoaded = true;
        // do your processing here
      }
    };
    $.elementReady('id1', check12);
    $.elementReady('id2', check12);
    

    This is just off the top of my head so please don’t use it to launch a space ship or control a pacemaker.

    However, maybe you really want to wait until the contents of the iframes are loaded. To be honest, I don’t know how the DOM works with iframes so all I can do in that case is wish you good luck.

  9. Thank you – I’ll try it out…even if this specific code doesn’t work, I get the idea. And, no, this isn’t for NASA.

  10. this plugin can be described with one word.
    PERFECT…

    I used it on struts 2 platform to get my fields masked and my table sorted which is created by displaytag and it worked perfect..
    My problem was ajax callbacks with s:div tags. Returned scripts were running before dom loaded.. Even dojoAddOnLoad, jQuery ready was not worked.. other events such as loaded event in dojo could not catch the correct time. I was nearly giving up using sorting or masking…

    Thank you very much.

Leave a comment