DOM Attributes and The Dojo Toolkit 1.2

By on October 23, 2008 12:01 am

The Dojo Toolkit 1.2 has landed and I’ll be talking about a new feature — dojo.attr — and its closely related cousin, dojo.style. In a given block of HTML, not all attributes are created equally. Take the following example:

In IE, you can’t write to the style object as an attribute because it is not a true attribute. The same applies to the event handler; it is not a real attribute, but a shortcut to the DOM Level 0 Events interface. None of those attributes work in IE—with the exception of ‘name’, which doesn’t work in Firefox. Of course, you’re telling yourself, “But that’s not how I code these attributes.” And you’re right. We’ve been conditioned to code things in certain ways to get them to work. In IE, you can’t write to the style object as an attribute, because it is not an attribute. Same with the method. And ‘tabindex’ failed because it was not in camelCase, even though this would work if you wrote it that way in HTML. dojo.attr provides a single method to address all the cases listed above.

dojo.attr is designed to address the following concerns:

  • Handle “Virtual Attributes”
  • Pass-through functionality to dojo.style
  • Pass-through functionality to dojo.connect
  • Attribute name normalization

dojo.attr and dojo.style use arguments length detection to determine if the method should set a property or return a property. The object context is passed as the first parameter, the property name as the second, and an optional value as the third. Rewriting our code, it looks like this:

var node = dojo.byId("testDiv");
onOver = function(evt){console.warn('OVER');}

dojo.attr(node, "onmouseover", onOver);
dojo.attr(node, "tabindex", 1);
dojo.attr(node, "style", {"background": "#ff0000"});
dojo.attr(node, "name", "nameAtt");
dojo.attr(node, "innerHTML", "New Content");

The benefits of dojo.attr become immediately obvious, especially if you’ve done a lot of work with forms. The most common attributes can and often are accessed directly, such as element.id, element.title, or element.tabIndex. But getAttribute must be used for other, lesser common attributes, such as element.name, or element.enctype. Table attributes like element.border can be accessed directly, yet element.cellpadding cannot. Using dojo.attr, it all just works.

dojo.attr also allows you to set multiple attributes at once (including our event handler), letting us pare down our code even further. As an example, we’ll add parameters to the style object. And we can even use dojo.hitch to have our handler maintain execution context:

var node = dojo.byId("testDiv");
var widget = {
	onOver: function(evt){console.warn('OVER');}
}
dojo.attr(node, {
	onmouseover: dojo.hitch(widget, "onOver"),
	tabindex: 1,
	name: "nameAtt",
	innerHTML:"New Content",
	style: {
		background: "#ff0000",
		"float":"left",
		marginLeft:"140px",
		marginTop:"-15px",
		padding:"3px",
		border:"#666666 2px solid",
		opacity:0.4
	},
});

Note that we were able to remove the quotes from the attribute names, since they are now keys in the property bag we passed to the function—with the exception of “float”, because it is a reserved keyword in ECMAScript. Also, note how we were able to pass a second property bag to the ‘style’ attribute; we’ll explain why you can do that in the next section of this article. Finally, notice that we were able to set the contents of our node by using innerHTML as a property—dojo.attr provides this as a convenience method.

We can access the attributes using the same method. By not passing a value as the third parameter, dojo.attr acts as a getter:

dojo.attr(node, "tabindex");	// returns 1
dojo.attr(node, "name");	// returns 'attName'

Style handling with dojo.style

dojo.style uses the same convention as dojo.attr to get and set properties. However, in spite of dojo.style‘s getter/setter appearance, some CSS properties are not acquired the same way they are set. Take for example the border property used in the example above:

dojo.style(node, "border"); // returns "#666666 2px solid"

Now, let’s remove that property from the dojo.style block and instead, use an HTML style:

// Style
body div{
	border:"#666666 2px solid";
}
// JavaScript
dojo.style(node, "border"); // returns nothing

In style sheets, ‘border’ is shorthand notation; there is not a ‘border’ property in the DOM style object. When we set it using JavaScript, it is enumerating the style object. It works, but may lead to confusion. To prove this out, let’s insert a new CSS rule, with dojox.html.styles:

dojo.require("dojox.html.styles");
dojo.style(node, {
	border:"#666666 2px solid"
});

console.log("border:", dojo.style(node, "border"));
	// outputs border:"#666666 2px solid"
var sel = dojox.html.insertCssRule('body div', 
	'border:#0000FF 5px dotted !important;');
	// note !important to over ride element.style
console.log("border:", dojo.style(node, "border"));
	// outputs border:"#666666 2px solid"

The display has updated, and we have our new border, but the ‘border’ property still returns the old value.

To be safe, you need to always acquire borders, margins, and padding by their fully qualified property names:

dojo.style(node, "borderLeftWidth"); 	// returns 5
dojo.style(node, "marginLeft"); 	// returns 140
dojo.style(node, "paddingLeft"); 	// returns 3

For more information on dojo.style, my friend and colleague Peter Higgins has an excellent article at Dojo Campus.

dojo.attr and dojo.style shows how the Dojo team is working to enable you to write more concise, elegant code. And everything shown here except for dojox.html.styles is in Dojo Base, so it’s all available in a tiny, 26KB download!