The Parser

The Dojo parser is a Core resource that is the standard means of instantiating a widget defined in markup and ensuring that its visible representation, linked via its domNode, gets inserted into the page. Once the domNode is assigned into the page, the browser renders it on the page. So, while a widget's DOM node is the vital part of the dijit that makes it visible, the totality of the dijit is considerably more. This section provides an introduction to the parser, as well as play-by-play coverage on exactly how it works.

Parsing a Widget When the Page Loads

Aside from seeing some references in the introductory material in Chapter 1 and some exposure in the drag-and-drop examples from Chapter 7, the parser hasn't been formally introduced because its most common use case is instantiating widgets in a page. Without further ado, here's an official example of the parser instantiating a widget from markup. Note the emphasized lines in Example 11-2, which highlight where the parser-related action is happening.

Example 11-2. Automatically parsing a widget

<html>
    <head>
        <title>Fun With the Parser!</title>

        <!-- pull in the standard CSS that styles the stock dijits -->

        <link rel="stylesheet" type="text/css"
href="http://o.aolcdn.com/dojo/1.1/dojo/resources/dojo.css" />
        <link rel="stylesheet" type="text/css"
          href="http://o.aolcdn.com/dojo/1.1/dijit/themes/tundra/tundra.css" />

        <script
            type="text/javascript"
            src="http://o.aolcdn.com/dojo/1.1/dojo/dojo.xd.js"
            djConfig="parseOnLoad:true"
        ></script>

        <script type="text/javascript">
            dojo.require("dojo.parser");
            dojo.require("dijit.form.Button");
        </script>
    </head>

    <body class="tundra">
      <button dojoType="dijit.form.Button"
 >Sign Up!</button>
    </body>
</html>

To summarize what's happening, there's just a simple page that includes an off-the-shelf button from Dijit that does absolutely nothing except look pretty—but for the purposes of introducing the parser without diving into Dijit specifics, this is just fine. The only thing you need to know about the Button dijit at this time is that it is fetched via a call to dojo.require and inserted into the page via the dojoType tag.

Tip

Any custom addOnLoad logic you could include is executed after the widgets are parsed—making it safe for you to reference them.

You won't see any direct invocations of the parser in Example 11-2; that's by design. The vast majority of the time, you simply dojo.require dijits into your page, set the parseOnLoad flag in djConfig, and let the rest happen behind the scenes. In fact, that's all that occurs in this example. It's worth taking a moment to ponder just how absolutely elegant it is that you can take a dijit off the shelf and, in just a few keystrokes, insert it into the page. No additional headache, hassle, or fuss is required.

Manually Parsing a Widget

There are bound to be times when you will need to manually parse a page or some node in the DOM. Fortunately, it's just one function call away. Consider Example 11-3, a variation that manually parses the widget in the page.

Example 11-3. Manually parsing a page

<html>
    <head>
        <title>Hello Parser</title>

        <!-- pull in the standard CSS that styles the stock dijits -->

        <link rel="stylesheet" type="text/css"
          href="http://o.aolcdn.com/dojo/1.1/dojo/resources/dojo.css" />
        <link rel="stylesheet" type="text/css"
          href="http://o.aolcdn.com/dojo/1.1/dijit/themes/tundra/tundra.css" />

        <script
            type="text/javascript"
            src="http://o.aolcdn.com/dojo/1.1/dojo/dojo.xd.js"
            djConfig="parseOnLoad: false"
        ></script>

        <script type="text/javascript">
            dojo.require("dojo.parser");
            dojo.require("dijit.form.Button");
            dojo.addOnLoad(function(  ) {
              dojo.parser.parse(  ); //manually parse after the page loads
            });
        </script>
    <head/>
    <body class="tundra" >
      <button dojoType="dijit.form.Button" >Sign Up!</button>
    </body>
</html>

Although manually parsing the entire page is useful, you'll more often need to manually parse a DOM node. The parser accepts an optional argument that provides the root of a node in the DOM tree that it scans for dojoType tags and instantiates. Thus, you provide the parent of the node you wish to parse to the parse function. Here's one possible modification of the previous code block that illustrates:

<script type="text/javascript">
  dojo.require("dojo.parser");
  dojo.require("dijit.form.Button");
  dojo.addOnLoad(function(  ) {
    //The parser traverses the DOM tree passed in and instantiates widgets.
    //In this case, the button is the only leaf in the tree, so it is all that
    //gets parsed
    dojo.parser.parse(document.getElementsByTagName("button")[0].parentNode);
  });
</script>

Warning

Trying to manually parse a widget on the page by passing the widget's DOM node into the parse method will fail, and you may not receive any visible indication that parsing failed. Fortunately, if you can locate a reference to a node, referencing its parent through the parentNode is just a few keystrokes away.

Demystifying the Parser

Although what the parser accomplishes really does seem like magic, it really just boils down to rigorous, well-designed automation. As you now know, the parser has two primary use cases: parsing the page on load via djConfig="parseOnLoad:true" or manually parsing a widget. This section elaborates on the details that go into making those two things happen.

Parsing a widget when the page loads entails three basic requirements:

  • Include parseOnLoad:true as a key/value pair to djConfig, which the parser will detect when it is loaded and use to trigger automatic parsing.

  • Require the parser via dojo.require("dojo.parser") so that the parser is available and can register an automatic call to dojo.parser.parse( ) when the page loads. Because no arguments are passed to the call, the entire body of the page provides the basis for parsing.

  • Provide dojoType tags as needed in the markup for widgets that should be parsed.

Manually parsing a widget that has already been defined in markup after the page loads is similar:

  • Require the parser via dojo.require("dojo.parser"). Because parseOnLoad is not detected to be true, no automatic call to dojo.parser.parse( ) occurs.

  • Provide the corresponding dojoType tag in the markup for a widget—maybe even dynamically after the page has already loaded.

  • Manually call dojo.parser.parse( ), optionally providing a specific DOM node as an argument as the starting point for the parsing operation.

But what about the actual parsing process? You know—the part about finding all of the dojoType tags and instantiating them into widgets? Again, it's all simple automation when you get right down to it. Here's exactly what happens:

  • dojo.query("[dojoType]") is called to deterministically fetch the nodes in the page that need to be parsed.

  • Class information (as in dojo.declare type classes) is distilled from each node; attributes are iterated over and lightweight type conversion is performed. Recall that attributes may provide information to a class's constructor.

  • Any dojo/method or dojo/connect script tags internal to the node are detected and scheduled for processing. (More on these in the upcoming section "Defining Methods in Markup.")

  • An instance of the class is created by using its constructor unless a markupFactory method is defined, in which case it is used. markupFactory is a special method that allows you to define a custom constructor function for widgets that need different initialization in markup than they do via programmatic creation. All dijits inherit from a base class, _Widget, which fires off a standard series of lifecycle methods. One of these lifecycle methods inserts the dijit's domNode into the page, which makes it visible. Lifecycle methods are discussed in detail in the next chapter.

  • If a jsId attribute is present, then the class instance is mapped to the global JavaScript namespace. (Common for data stores and widgets that you have a reason to make global.)

  • Any connections provided via dojo/connect or dojo/method SCRIPT tags in markup are processed (more on this later in the chapter) and each widget's startup lifecycle method is called. startup is another standard lifecycle method inherited from _Widget (coming up in the next chapter) which allows you to manipulate any widgets that are contained in the one being instantiated.

Hopefully, that didn't make you feel the same way that you did when you learned that Santa Claus wasn't real, but you had to learn sometime. The next chapter focuses exclusively on dijit lifecycle methods where dedicated coverage of these concepts is provided.

Get Dojo: The Definitive Guide now with the O’Reilly learning platform.

O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.