Tuesday, September 14, 2010

dojo.connect vs. dojo.behavior

Having used dojo for the past few years, I've become very familiar with using dojo.connect to list for events being fired on objects. The API is simple, clear and concise:
var h = dojo.connect(dojo.byId("myNode"), "onclick", myInstance, "reactToNodeClick");

Plenty of documentation already exists on dojo.connect, so I'm not going to rehash it out here. Reading a recent article on dojo.behavior and couldn't help but agree that there are an enormous amount of similarities between dojo.connect and dojo.behavior. Apparently, dojo.behavior uses the dojo.connect plumbing under the covers, so I was curious to figure out why there was a separate dojo API entry-point to this exact same functionality. dojo.behavior must be offering something above and beyond dojo.connect, or at least it must expose new functionality for connecting to dom and custom events. According to comments on this article, dojo.behavior is based on Ben Nolan's plugin which seems to have been deprecated in favor of jquery's live method. Taking a look at the docs for jquery, I took a shot at converting their example to use dojo.behavior:
<!DOCTYPE html>
<html>
<head>
<style>
p {
background: yellow;
font-weight: bold;
cursor: pointer;
padding: 5px;
}
p.over {
background: #ccc;
}
span {
color: red;
}
</style>
<script type="text/javascript" src="dojotoolkit/dojo/dojo.js">
</script>
<script type="text/javascript">
dojo.require("dojo.behavior");
</script>
</head>
<body>
<p>
Click me!
</p>
<span></span>
<script>
dojo.addOnLoad(function() {
dojo.behavior.add({
'p': {
onclick: function(evt) {
dojo.place("<p>Another paragraph!</p>", dojo.query("p")[0]);
}
}
});
dojo.behavior.apply();
});
</script>
</body>
</html>

So, it seems that dojo.bevahior's main advantages over dojo.connect are:
1. behaviors are added to dom elements which have yet to be inserted into the overall document. This allows the developer to organize all the behavior for a page in one place and also allows for quickly adding behavior to dynamically added dom elements. This stackoverflow question has a pretty nice explanation of this.
2. You can add multiple behaviors to elements all in the one statement. Example:
dojo.behavior.add({
'.foo' : {
found: function(node) {
// do something with each node found
}
}, 
// shortcut for found
'.foo2' : function(node) {
// do something with the node
}
});

although, it could be argued that a combination of dojo.forEach and dojo.query would allow a developer to declare multiple dojo.connects in one statement. For example:
dojo.forEach([".foo", ".foo2"], function(item) {
dojo.query(item).connect("onclick", function(node) {
// do something with the node.
});
});

Unless I'm missing something in the dojo.behavior API I don't see a way to disconnect all the previously added behaviors. I use dojo.disconnect quite a bit when using dojo.connect to clean up after myself (phiggins article again). dojo.behavior.add doesn't return an array of handles so you cannot remove behavior once it has been added. A global suspend/resume API might be a useful addition to the dojo.behavior API to allow a developer to stop/start behaviors. It could even look like this:
dojo.behavior.suspend(selector);
dojo.behavior.suspendAll();
dojo.behavior.resume(selector)
dojo.behavior.resumeAll();

// possible usage
dojo.behavior.add({
'p': {
onclick: function(evt) {
dojo.place("<p>Another paragraph!</p>", dojo.query("p")[0]);
}
}
});
dojo.behavior.apply();
// stop all behavior on p elements that were just applied in the previous statement.
dojo.behavior.suspend('p');
// allow behaviors on p elements again
dojo.behavior.resume('p');

I dunno … that seems hackish when compared with dojo.connect/dojo.disconnect.

Even after this exportation into dojo.behavior, I'm not sure I'd ever use it over dojo.connect/disconnect. dojo.connect/dojo.disconnect gives me a level of granularity that I'm comfortable with. Even searching through the dojo source code for references to dojo.behavior, I don't really see it being used at all in internal framework code (dojo.connect/dojo.disconnect is used EVERYWHERE though).

Can you see any other uses for dojo.behavior - why would you chose to use it over dojo.connect/dojo.disconnect?

Some other useful links:
http://paulirish.com/2010/on-jquery-live/
http://forum.jquery.com/topic/jquery-live-jquery-fn-live-discussion

1 comment:

  1. I think the main benefit is the dynamic addition of elements into the page will get the same event-handlers as described in your referenced StackOverflow post.

    However, if you have a consistent API in your code that will do this for you, I don't see the need for behavior. For instance, if you have a form where you want to allow a user to upload a file or multiple files, you might create a button for them to add more file upload components into the page. That function will likely call a method "addFileUploadButton" or similar. In that method you would have the code that will physically insert the browse button into the page as well as any event-handlers etc you might need. Each time the user clicks the button to add a new file upload component, all the appropriate code is executed.

    Furthermore, you could use this same function when the page loads to add the first element into the page.

    ReplyDelete