Sunday, September 19, 2010

dojo.behavior and jquery live

My last blog post mentioned that dojo.behavior was modeled after Ben Nolan's plugin which in turn has since been replaced by jquery.live. After reading this, I made the assumption that jquery's live and dojo.behavior were analagous. Even after coding up a quick example using dojo.behavior to mirror the jquery live() example, I came to the conclusion that these two API methods offered pretty much the same functionality.

However, after a quick discussion on the #dojo IRC channel, Brian Arnold pointed out a subtle but important difference between the two APIs. Apparently, jquery's live method listens for bubbled events on the document element and then maps these events back to jquery.live declarations. dojo.behaviour is a little different. It listens for events fired from the selector on which the behavior is added in the first place. So, if I changed my previous example to look like this:
dojo.behavior.add({
'p': {
onclick: function(evt) {
dojo.place("<p>Another paragraph!</p>", dojo.query('body')[0]);
}
}
});
dojo.behavior.apply();

New paragraphs would only be added when the user clicked on the first 'Click Me' paragraph. However, with jquery, if I altered the original example to look like this:
$("p").live("click", function(){
$("body").after("<p>Another paragraph!</p>");
});

Even clicking on the 'Another paragraph' would result in new p dom nodes being added to the document.

Cheers for pointing out this difference Brian. Also - see the comments below for one other point by Brian.

One other difference between the two APIs was pointed out by Pete Higgins - dojo.behavior includes a 'found' function which operates a little like this:
var myBehavior = {
// all <span> nodes
"span" : {
// for each:
found: function(n){
console.log('this is the first time I have encountered ', n);
}
}
};
dojo.behavior.add(myBehavior);

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

Monday, September 6, 2010

First impressions of gjslint

I saw a blog post from the Closure Tools Blog recently and it piqued my interest enough to give it a try. I've been running jslint on a codebase for the past 6 months or so and I've been a little frustrated with some of the 'errors' reported by it (I don't think I'm the only one). jslint is not without its warts, but it has definitely forced me to take a second look at my code and made me rethink one or two coding decisions. Some of the 'issues' my source code continuously bumps into with jslint include:

1. Not using the new operator:
Lint at line 89 character 53: Do not use 'new' for side effects.

2. Misuse of Ternary operators:
Lint at line 145 character 112: Expected an assignment or function call and instead saw an expression.
!disabled ? dojo.removeClass(el, 'disabledButton') : dojo.addClass(el, 'disabledButton');

3. Unexpected use of >> << & |
return (transferMode >> 0) & 3;

4. Use of === vs. ==
if (range.text == '') {

Anyways, I'm always interested in seeing some static analysis of my code, so I downloaded and installed gjslint. Running it is pretty simple - just point it at a directory full of .js files:
gjslint -r path/to/my/directory

Oddly, none of the issues which kept on showing up in the regular jslint checker appeared in the results of gjslint. I guess this is because gjslint is more focused on closure's code formatting rules more so than strictly correct (at least in the eyes of jslint) JavaScript?

After running gjslint on my codebase, I saw a a _lot_ of these types of errors:
Line 54, E:0200: Invalid JsDoc tag: name
Line 55, E:0200: Invalid JsDoc tag: function
Line 56, E:0200: Invalid JsDoc tag: memberOf

Apparently, the gjslint checker only allows a certain subset of certain JsDoc rules. I tried using the --nojsdoc flag in the hope that all these 'errors' would be suppressed, but no dice.

Also - 80 characters seems to be the limit that gjslint allows for JavaScript files. Would be nice if this rule could be suppressible or at least configurable. Not even sure why its a rule in the first place.

For kicks, I tried passing the --strict flag to see the output. I seem to get a lot of indentation errors:
Line 342, E:0006: (New error) Wrong indentation: expected any of {4, 16} but got 8
Line 343, E:0006: (New error) Wrong indentation: expected any of {4, 16} but got 8
Line 344, E:0006: (New error) Wrong indentation: expected any of {4, 16} but got 8

I've never been a huge fan of being overtly strict when it comes to the number of spaces which represents an indentation. I use 4 for each indent - gjslint advises you to use 2 - no big deal in my books.

Included in this release of gjslint, there's a handy tool called fixjsstyle which will help you automatically fix any styling issues in your code. One point of caution when running this tool though - make sure your code is under source control before you run it. Stepping through your code after running fixjsstyle reveals some more gjslint rules. For example, running fixjsstyle on a code base will convert all double quotes to single quotes. Wonder why that's in the style guide? Actually, it'd be great to see some explanations behind some of the rules.

Overall - I've always felt that static code analysis of code is useful to a point. It'll definitely keep you keen when it comes to coding standards and it is useful in that it'll catch any errors that any self-respecting compiler would catch, but it'll only get you so far. I've always found gathering and understanding runtime analysis of a codebase more difficult to achieve (I still have a lot to learn in this field), but more rewarding in the end.