Attributes and properties: the essential difference

When reading specifications for XUL elements on XUL Planet or elsewhere, you'll often find both attributes and properties for an element. You'll probably notice that there are several attributes that have a corresponding property, with the exact same name. Examples of this are "hidden" and "value", very commonly used.
If you have a XUL element object on your script, you can get or set an attribute like so:
someValue = element.getAttribute("someAttribute");
element.setAttribute("someAttribute", someValue);
Getting and setting properties is done like this:
someValue = element.someProperty;
element.someProperty = someValue;
There are some differences between attributes and properties. Properties can be read-only, which means that setting them will throw an error. Properties can be handled a little more "naturally" in javascript, because handling of properties is done in the same way it's done with any object. No need to know that the element is, in fact, a XUL element. You just need to know what properties and methods are available in the object. Makes the code nicer.
But that's not such a big deal. Here's the big deal: setting attributes may not yield the result you expect. Suppose you have an empty textbox and want to set its value in a script. Let's execute the following code and see what happens:
textbox.value = "First value";
textbox.setAttribute("value", "Second value");
After this code is executed, the textbox will be showing "First value", but if you look at the textbox element with the DOM Inspector, you'll see that it's attribute "value" will have a value of "Second value". Now that's weird. Or so I think.
What happened? Well, element attributes will be used to set the element's internal state at load time, but you have no guarantees that changing them at runtime will have any effect. Properties are the opposite: you can be sure that setting a property or running a method will change the internal state of the object like you would expect.
OK, so this means you should favor using properties over attributes at all times. That's no problem, right? I already mentioned some advantages of properties, and the code will look much cleaner using them instead of attributes. It's a no-brainer. Problem solved.
Oh, wait. I forgot about templates. Using templates you can make an element change the value of its attributes dynamically, depending on a datasource. On my last post, I had a textbox that uses a template to show its value:
<textbox value="rdf:http://mypage.com/my-schema#some-value" />
This won't work. I mean, it will show the value from the template properly, but changing the value in the datasource will have no effect in the generated element. Maybe doing a full template rebuild will work, but this shouldn't be necessary. The problem is that the template knows nothing about properties. It only deals with element attributes, so changing the value in the datasource will in effect cause a setAttribute call, which does nothing for the state of the element, as mentioned previously.
To work around this issue, an event called DOMAttrModified comes to the rescue. This event is triggered every time an attribute is changed in the element. The improved textbox looks like this:
<textbox value="rdf:http://mypage.com/my-schema#some-value"
DOMAttrModified="if(event.attrName == 'value') this.value = event.newValue; return true;" />
This works. The code in the event handler can be taken out to a script, obviously. The "attrName" property of the event object tells us which attribute was changed, and the "prevValue" and "newValue" properties give us the previous value and new value, respectively. This snippet of code turns the setting of the attribute into the setting of a property. This way all updates in the datasource will be properly reflected in the page.
This event has proved to be quite handy, specially when working with XBL. Bindings will let you add a constructor, with which you can set the internal state of your element with the values of the different attributes it has set. But the only way you'll know an attribute has been changed afterwards is to add a DOMAttrModified handler to your binding. Then you can do something similar as with the textbox example, and convert the setting of an attribute to a setting of a property.
I'm not quite sure why is it that attributes can be independent to the state of the object, but I guess there's a decent reason behind it. Perhaps it's just standard. I don't know.
But now you know how to work around it.

Labels: , , , , ,

Comments: Post a Comment

<< Home

This page is powered by Blogger. Isn't yours?