Behavior

Core contains a lightweight extension that builds on top of query to provide a great way for decoupling events and DOM manipulations from an HTML placeholder via the behavior module. It may not be intuitively obvious at first, but the ability to define behavior for nodes irrespective of the markup itself can lend an immense of flexibility to a design. For example, it allows you to concisely accomplish tasks such as assigning click handlers to all anchor elements without knowing where or how many anchor elements there will be. You use the same CSS selectors you learned about in Table 5-1 to find the nodes for attaching behavior to, so the possibilities are almost endless.

The behavior module currently provides two API calls; the add method allows you to queue up a collection of behaviors, and the apply method actually triggers those behaviors:

dojo.behavior.add(/*Object*/ behaviorObject)
dojo.behavior.apply(  )

Basically, you use add to assign a new behavior to a collection of DOM nodes, but the behavior isn't actually reflected until you call apply. One of the reasons that it's a two-step process is because the pattern of performing multiple add operations before a final apply occurs lends itself to a lot of asynchronous communication patterns, described in Chapter 4.

Tip

Chapter 4 introduced a data structure called Deferred that is a staple in Dojo's IO subsystem. Deferred s provide the façade of having a thread available to operate on and lend themselves to successively applying multiple callback and error handling functions. After reading about Deferred patterns, the utility in providing separate functions for add and apply should be apparent.

The Object that you pass into add and apply is quite flexible and can accept a number of variations. In short, the behavior Object contains key/value pairs that map CSS selectors to Object s that supply DOM event handlers. The DOM event handlers themselves come as key/value pairs. Before the example, though, skim over Table 5-4, which provides a summary of the possibilities.

Table 5-4. Behavior Object possibilities

Key

Value

Comment

Selector (String)

Object

The Object should contain key/value pairs that map either DOM event names or the special "found" identifier to event handlers or topic names.

For example:

{
    onclick : function(evt) {/*...*/},
    onmouseover : "/dtdg/foo/moveover",
    found : function(node) {/*...*/},
    found : "/dtdg/bar/found"
}

In the case of a topic being published, the standardized event object is passed along for the subscribe handler to receive.

In the case of an event handler, the standardized event object is passed into the function.

In the case of the special "found" identifier, the matching node itself is either passed into the handler or passed along with the topic that is published.

Selector (String)

Function

For each node matching the selector, the handler is executed with each node passed in as the parameter.

Selector (String)

String

For each node matching the selector, the topic name is published. The node itself is passed along for the subscribe handler to receive.

Tip

Remember to provide the keys to the behavior Objects as actual String values whenever the CSS selector requires it. For example, a behavior object of {div : function(evt) {/*...*/} is fine whereas {#foo : "/dtdg/foo/topic"} would not be valid because #foo is not a valid identifier.

Take a moment to read through Example 5-1, which illustrates some of the possibilities as a working example.

Example 5-1. Example of dojo.behavior at work

<html>
     <head>
         <title>Fun with Behavior!</title>

         <link rel="stylesheet" type="text/css"
             href="http://o.aolcdn.com/dojo/1.1/dojo/resources/dojo.css" />
         <script
             type="text/javascript"
             src="http://o.aolcdn.com/dojo/1.1/dojo/dojo.xd.js"
             djConfig="isDebug:true"
         ></script>

         <script type="text/javascript">
             dojo.require("dojo.behavior");

             dojo.addOnLoad(function(  ) {
                     /* Pass a behavior Object into dojo.behavior.
                    This object is automatically added once the page loads*/
                     dojo.behavior.add({

                         /* The behavior Object is keyed by any combination of CSS
                        selectors, which can map to a single behavior or a
collection of
                        behaviors */

                         /* Mapping a key to a function is equivalent to mapping
to {found
                         : function(node) { ... } } */
                         ".container" : function(node) {
                            //apply some generic styling

                            dojo.style(node, {
                                border : "solid 1px",
                                background : "#eee"
                            });
                         },

                        /* Map the key to a collection of behaviors */
                        "#list > li" : {
                            /* DOM events work just like dojo.connect, allowing
you to act
                            on the event */
                            onmouseover : function(evt) {dojo.style(evt.target,
                            "background", "yellow");},
                            onmouseout : function(evt) {dojo.style(evt.target,
                            "background", "");},

                            /* String values are published as topics */
                            onclick : "/dtdg/behavior/example/click",

                            /* "found" is a general purpose handler that allows
                             manipulation of the node*/
                            found : function(node) {dojo.style(node, "cursor",
"pointer")}
                        }
                    });

                    /* Somewhere, out there...a subscription is set up... */
                    dojo.subscribe("/dtdg/behavior/example/click", function(evt) {
                        console.log(evt.target.innerHTML, "was clicked");
                    });
            });
         </script>
     <head>

         <body>
             <div class="container" style="width:300px">
                 Grocery List:
                 <ul id="list">
                    <li>Bananas</li>
                    <li>Milk</li>
                    <li>Eggs</li>
                    <li>Orange Juice</li>
                    <li>Frozen Pizzas</li>
                </ul>
            </div>
        </body>
 </html>

As the example demonstrates, any behavior you set up before the page loads is set up automatically. After the page loads, however, you need to first add the behavior and then apply it. The following update adds another click handler to list elements:

dojo.behavior.add({
  "#list > li" : {
    onclick : "/dtdg/behavior/example/another/click"
  }

});
dojo.behavior.apply(  );

dojo.subscribe("/dtdg/behavior/example/another/click", function(evt) {
  console.log("an additional event handler...");

});

Although one of the key observations you should be making is how decoupled the actual behavior of the nodes are from the markup itself, you hopefully just made the connection that behavior 's apply function provides you with a great benefit: any behavior you supply on top of existing behavior is added along with the existing behavior. In other words, new behavior doesn't just blow away what was there before; you are able to add behavior in layers and the book keeping is handled without any additional intervention on your behalf.

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.