The dojo parser.

Dojo has a HTML parsing function that reads the custom dojoType=”" attribute, and turns the node in some class instance. It’s really cool, and really fast, and briefly, I am going to explain some of it, and it’s uses.

First, when is it loaded? Parser is loaded sometimes without you knowing. If you have specified djConfig="parseOnLoad:true" in your dojo.js script tag, Dojo includes the parser code auto-magically. If you set parseOnLoad:false, you must explicitly require() it:

<script type="text/javascript" djConfig="parseOnLoad:false" src="dojo/dojo.js"></script>
<script type="text/javascript">
    dojo.require("dojo.parser");
    dojo.addOnLoad(function(){
        dojo.parser.parse();
    });
</script>

What does it do? It scans the dom (or a portion of it) turning nodes with dojoType=”" attributes into declared classes, for example:

<div dojoType="dijit.Dialog" title="My Title">
   <p>Some content</p>
</div>

If parseOnLoad:true, a Dialog will be created and started with the title “My Title”. Its essentially the same as doing:

dojo.addOnLoad(function(){
     var d = new dijit.Dialog({
           title:"My Title"
     });
     d.startup();
     d.setContent("Some content");
});

The parser is just an uber-easy automagic conversion thinger to turn “invalid markup” into useful widgets. If you don’t like the invalid markup, you can still use the above programmatic method.

One reason to use parseOnLoad: false (aside from any cases where are you using widgets at all) would be to delay the parsing until you are ready. Dojo’s addOnLoad fires _after_ parsing is done when parseOnLoad:true. Turning it off will result in parser never running, and addOnLoad firing after all the require()’s have been made.

The dijit ThemeTester is an example of harnessing parseOnLoad:false. It basically has to load every piece of widget code, and when uncompressed its a substantial piece of download. By not parsing the dom first thing, we’re able to create an “overlay” for our content, hiding it in it’s unstyled form, then in addOnLoad calling parser, and finally hiding the overlay. The effect is a dojo-preloader:

<html>
<head>
    <title>PreLoad test</title>
    <style type="text/css">
          @import "dojo/dijit/themes/tundra/tundra.css";
          body, html { width:100%; height:100%; }
          #overlay { background:#fff; width:100%; height:100%; 
                 position:absolute; top:0; left:0; 
          }
    </style>
    <script src="dojo/dojo.js" djConfig="parseOnLoad:false"></script>
    <script type="text/javascript">
          dojo.require("dojo.parser");
          dojo.require("dijit.layout.BorderContainer");
          dojo.addOnLoad(function(){
                // run the parser. defaults to dojo.body(), but you can pass any node
                // it runs async, so fadeout when it's done. 
                dojo.parser.parse();   
                dojo.fadeOut({ 
                      node:"overlay",
                      onEnd: function(){ 
                             // hide it completely after fadeout
                             dojo.style("overlay","display","none");
                      }
                }).play();
          });
    </script>
</head>
<body class="tundra">
     <div id="overlay"><div class="innerOverlay">Loading</div></div>
     <div dojoType="dijit.layout.BorderContainer">
     <!-- lots of dijit.Layout.BorderContainer's and ContentPane's and TabContainers -->
     </div>
</body>
</html>

You can run parser on parts of the dom by passing a “starting point” node as the first argument to parse(). Say you just used someNode.innerHTML = “a bunch of html text with dojoTypes”; The nodes with dojoTypes aren’t widgets yet, and if parsing has already run on the page, you will get duplicate id warnings for the widgets that have already been parsed. Simple only re-parse the new content:

someNode.innerHTML = "<span dojoType='dijit.somebox'></span>";
dojo.parser.parse(someNode);

Which is especially useful if you pull in large blocks of HTML from an Ajax request:

dojo.xhrGet({
     url:"sample.html", 
     load:function(data){
         var n = dojo.byId("someNode");
         n.innerHTML = data;
         dojo.parser.parse(n);
     }
});

And that’s the dojo parser. (and a quick howto on the preloader thing).

EDIT: Parser isn’t automatically included simply by the presence of a parseOnLoad:true flag. It just seems that way. The parser is included as part of dijit._Templated, so it can parse the template Dom, resulting in you not needing to dojo.require(”dojo.parser”) any time you are using a _Templated widget (which is most of them). It is still and always will be safe to call dojo.require() many times, as when a resource is loaded, require() become a no-op. So don’t worry about duplicate requires. That’s the beauty of the package system in Dojo.

Tags: