Home > javascript, jquery > Markup-based unobtrusive comprehensive DOM-ready execution

Markup-based unobtrusive comprehensive DOM-ready execution

March 11th, 2009

On a recent project I took my previous approach to automating firing of onload events to a new level.

For instance if your code was architected in an object literal such as:

 
FOO = {
  common : {
    init     : function(){ ... },
    finalize : function(){ ... }
  },
  shopping : {
    init     : function(){ ... },
    cart     : function(){ ... },
    category : function(){ ... }
  }
}

A page with this body tag:

<body id="cart" class="shopping">

would load these functions sequentially:

UTIL.fire is calling: FOO.common.init()
UTIL.fire is calling: FOO.shopping.init()
UTIL.fire is calling: FOO.shopping.cart()
UTIL.fire is calling: FOO.common.finalize()

In addition, using these classes and IDs on the body tag provides some excellent specific hooks for your CSS.

The javascript:

UTIL = {
 
  fire : function(func,funcname, args){
 
    var namespace = FOO;  // indicate your obj literal namespace here
 
    funcname = (funcname === undefined) ? 'init' : funcname;
    if (func !== '' && namespace[func] && typeof namespace[func][funcname] == 'function'){
      namespace[func][funcname](args);
    } 
 
  }, 
 
  loadEvents : function(){
 
    var bodyId = document.body.id;
 
    // hit up common first.
    UTIL.fire('common');
 
    // do all the classes too.
    $.each(document.body.className.split(/\s+/),function(i,classnm){
      UTIL.fire(classnm);
      UTIL.fire(classnm,bodyId);
    });
 
    UTIL.fire('common','finalize');
 
  } 
 
}; 
 
// kick it all off here 
$(document).ready(UTIL.loadEvents);

This system worked very well and keeps you in serious control of the execution order.

In the end, I used this plus a custom event to bind super low priority script.
For example:

$(document).bind('finalized',function(){ ... }); // placed within a FOO.shopping.category()

And I'd trigger that

$(document).trigger('finalized');

at the very end of UTIL.loadEvents(). This allows you to keep similar code together, but delay portions responsibly without any setTimeout ugliness.

Paul Irish javascript, jquery

  1. redsquare
    #1

    Nice Paul, Just starting a greenfield app and have used a variation of this, rather than using body id and class I have used the rest url peices
    e.g appName/Controller/Action - this provides me with common (appName), then more specific implementations using controller and finally the action. Seems to be working good although only a few hours into it. I hope this will spare the developers having to remember to specify body id and class. Any thoughts / issues with this?

  2. #2

    @redsquare,
    Ah that's pretty nice. I would only go that route if you're confident the URL structure doesn't need to change and you don't have any SEO needs out of the site.

  3. #3

    This is an excellent solution. Just tested out on one current project and the application feels much smoother

  4. #4

    Great idea! I can't believe I haven't thought of this previously. :)

  5. #5

    I believe I got here from html5boilerplate.com so sorry if I'm more than a year late.

    Well, I like this but you can do the same without the whole UTIL object if you're already using jQuery and if performance aren't really a problem.
    I'm using this at the moment:

    $(function() {
    	//common.init
    	if($("#gallery").length)
    	{
    		//gallery page
    	}
    	else if($("#contacts").length)
    	{
    		//contacts page
    	}
     
    	if($(".classname").length)
    	{
    		// do all the *needed* classes too.
    	}
    	//common.finalize
    });
  6. #6

    @Bfred
    The classes part is where your code breaks down.. Not to mention this anti-DRY code is gonna get really hairy as this file grows..
    But sure.. it's possible to do it this way.. sorta. kinda. :/

  7. #7

    Hi, a simple question Paul
    Should be any difference between this:

    funcname = (funcname === undefined) ? 'init' : funcname;

    and this way I often see:

    funcname = funcname || 'init';

    Thanks

  8. #8

    @David
    The only different is the latter will go to 'init' if funcname is also false, 0, and most importantly, '' (empty string).
    Your call if that's okay or not.

  1. No trackbacks yet.

For code blocks, use <pre lang="javascript" escaped="true">. css and html4strict are also accepted.

i left this space here for you to play. <3