The dojo parser.
by Peter Higgins

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: , ,

This entry was posted on Saturday, March 8th, 2008 at 3:50 pm and is filed under Dojo Cookies. You can follow any responses to this entry through the RSS 2.0 feed. You can leave a response, or trackback from your own site.

10 Responses to “The dojo parser.”

  1. gtducati Says:

    I really like this cookie; however, there are two conflicting statements made concerning the call: dojo.parser.parse();

    You first say that the document is scanned for all types of dojoType=”". However, in the Dojo-Preloader example their is a comment that says “// run the parser. defaults to dojo.body()”, which indicates that the call of “dojo.parser.parse();” is essentially scanning everything for rendering. Which is it?

    I’ve tested this myself, and it seems that the latter is true.

  2. dante Says:

    dojo.body() is everything? You can’t have markup outside of body, so when you call dojo.parser.parse() or dojo.parser.parse(dojo.body()) it’s identical. or perhaps I’m missing your question.

  3. gtducati Says:

    Hey dante, thanks for the quick response. To clarify, when I said everything, I meant everything in the document body.

    In any case, I was really just having problems with the statement “It scans the dom (or a portion of it) turning nodes with dojoType=”" attributes into declared classes”, I was confused about the use of dojoType=”" in said quote. I was assuming that the statement implied the parser was looking for ‘empty’ dojoType attributes, since the phrase dojoType=”" is used. In fact, all it’s really saying is that it scans the body for all nodes with a dojoType attribute with a qualified.class.name for rendering. So, yes, you did answer my question.

    I do have another question; this one is in regards to the themeTester and its use of dojo.parser.parse(’container’) in the current 1.1.0 release. What is this exactly looking for? How is dojo.parser.parse(’container’) rendering the entire themeTester? Does ‘container’ imply the document body too? Am I missing something here? Thanks in advance.

  4. dante Says:

    Actually, it looks like there is no node id=”container” in the themeTester anymore … which means dojo.byId(”container”) is returning null || undefined, which means dojo.parser.parse() is being called without any arguments, thus parsing the entire body. Sorry for the confusion. I swear it did have and id=”container” at one point ;)

  5. gtducati Says:

    Ah … good, so I wasn’t going crazy :) Thanks!

  6. ribob2 Says:

    Passing information from the original HTML to the widget methods ?? I want to allow the original HTML that specifies a widget to be able to pass information to widget methods or be saved in instance variables. I tried adding some attributes of my own to the initial div, but by the time is is parsed and the widget is loaded, those attributes have disappeared. How do I pass info from the HTML to the widget object? Also, is there more documentation on the details of the parsing process?

    var catWidget = dijit.byId(”cat”);
    var catDiv = catWidget.domNode;
    var species = catDiv.getAttribute(”species”); // Doesn’t work, no species attr in the div

  7. ribob2 Says:

    Passing atttributes resolved? I think I figured it out: Dojo may replace the original you create with its own, losing all attributes (except those it creates) It only looks for attributes matching the names of the ‘instance variables’ defined in widget declaration. So you can pass things as long as they are named as instance variables.

  8. ribob2 Says:

    Dojo object and element creation timing? When do things get parsed and created with parseOnLoad? I did this:

    lpThumb = new dijit.layout.ContentPane();
    lpThumb.domNode.id = “leftThumbtray”;
    lpThumb.attr(”title”, “Thumb”);
    lp.addChild(lpThumb);

    (where lp is an existing TabContainer) There are two problems: 1) at the time postCreate is called on lbThumb, it seems that the widget domNode has not be inserted into the DOM tree (ie createElement was done, but it wasn’t appendChild()ed into the tree) 2) it seems that immediately after the lp.addChild(lpThumb) executes, the lpThumb.addChild is not defined in that object

    I’d like to understand what is going on here, or more generally, what is the flow of operations and timing when things are created (so you can know when you can start using things that are being created.

  9. ricardochois Says:

    Hi I’m new with dojo.

    I want to dinamically create a widget, so i create an HTML element (TextBox) and then try to parse it, but it just looks like an basic html textbox and not like a dojoType textBox.

    Here is the function i’m using, what is wrong???

    function agregarFilaAsignatura(argumentos){
    totalAgregadas++;
    tabla = document.getElementById(”tablaDatos”);
    var elmTR;
    var elmTD;
    var elmText;
    elmTR = tabla.insertRow(fila+1);

    actualizarIdsFila();
    elmTR.id = fila+1;

    for (var i=0; i<5; i++) {

    elmTD = elmTR.insertCell(i);

    elmText = document.createTextNode(argumentos.rbAsignatura.split(”-”)[1]);

    elmInput = document.createElement(’input’);
    elmInput.type = ‘hidden’;
    elmInput.value = argumentos.rbAsignatura.split(”-”)[0];
    elmInput.id = “asignaturas” + idDocente;
    elmInput.name = “asignaturas” + idDocente;

    elmInputHoras = document.createElement(’input’);
    elmInputHoras.type = ‘text’;
    elmInputHoras.dojoType = ‘dijit.form.TextBox’;
    elmInputHoras.style.width = ‘40px’;
    elmInputHoras.value = argumentos.rbAsignatura.split(”-”)[2];
    elmInputHoras.id = “horasAsignaturas” + idDocente;
    elmInputHoras.name = “horasAsignaturas” + idDocente;

    dojo.parser.parse(dojo.byId(”horasAsignaturas” + idDocente));

    if(i==1) elmTD.appendChild(elmInput);
    if(i==3) elmTD.appendChild(elmText);
    if(i==4) elmTD.appendChild(elmInputHoras);

    }

    }

  10. dante Says:

    @ricardochois - You might want to read this newer article: http://dojocampus.org/content/2009/04/15/declarative-vs-programatic/ … most of the work being done in your paste above is unnecessary, especially with the base Dojo API’s already available to you.

Leave a Reply

You must be logged in to post a comment.