Let's Make a Framework: Element Attributes

2011-05-05 00:00:00 +0100 by Alex R. Young

Welcome to part 61 of Let's Make a Framework, the ongoing series about
building a JavaScript framework.

These articles are tagged with
lmaf. The project we're creating is called Turing. Documentation is
available at turingjs.com.

Element Attributes

This DOM stuff really takes a lot of work, doesn't it? In the last few
parts we've been mucking around with CSS classes, so I thought it would
be pertinent to address element attributes. The jQuery API allows
attributes to be accessed with the .attr() method. jQuery
actually distinguishes between attributes and properties:

As of jQuery 1.6, the .prop() method provides a way to explicitly retrieve property values, while .attr() only retrieves attributes.

I'm fairly sure most frameworks either treat these the same or have a
generic 'get attribute/property/whatever value and interpret the
parameter' -- consider MooTools'
(it also has

jQuery is quite good at being explicit where it makes sense, so I'd like
to look at element attribute manipulation without confusing it with DOM

Getting Attributes

Element.getAttribute returns an attribute like this:

document.querySelector('#content-main article').getAttribute('role');
// "main"

What about when an attribute is not present? That's actually an
interesting point, which is mentioned in MDC's documentation:

Essentially all web browsers return null when the specified attribute does not exist on the specified element. The DOM specification says that the correct return value in this case is actually the empty string, and some DOM implementations implement this behavior.

Consequently, you should use hasAttribute to check for an attribute's existence prior to calling getAttribute() if it is possible that the requested attribute does not exist on the specified element.

Some frameworks don't do this, but it's probably a good idea -- there's
a comment in jQuery mentioning the BlackBerry browser returning an empty

Browser Support

IE does something very strange with attributes that return URLs. Rather
than leaving the URL alone it'll normalize it. To force it to return the
value we actually want we need to pass a second parameter to

element.getAttribute('href', 2);

The documentation for this is at the getAttribute Method page on

Therefore, jQuery sets up hooks to handle this for every attribute that
returns a URL:

jQuery.each([ "href", "src", "width", "height" ], function( i, name ) {
  jQuery.attrHooks[ name ] = jQuery.extend( jQuery.attrHooks[ name ], {
    get: function( elem ) {
      var ret = elem.getAttribute( name, 2 );
      return ret === null ? undefined : ret;

IE6/7 also cause problems when working with attributes on form elements.
To get around this, getAttributeNode is used (except for

formHook = jQuery.attrHooks.name = jQuery.attrHooks.value = jQuery.valHooks.button = {
  get: function( elem, name ) {
    var ret;
    if ( name === "value" && !jQuery.nodeName( elem, "button" ) ) {
      return elem.getAttribute( name );
    ret = elem.getAttributeNode( name );
    // Return undefined if not specified instead of empty string
    return ret && ret.specified ?
      ret.nodeValue :


If the document is not XML then the attribute name needs to be
capitalised correctly for IE:

propFix: {
  tabindex: "tabIndex",
  readonly: "readOnly",
  "for": "htmlFor",
  "class": "className",
  maxlength: "maxLength",
  cellspacing: "cellSpacing",
  cellpadding: "cellPadding",
  rowspan: "rowSpan",
  colspan: "colSpan",
  usemap: "useMap",
  frameborder: "frameBorder",
  contenteditable: "contentEditable"

// Elsewhere:
jQuery.attrFix = propFix;

It seems like tabIndex behaves strangely in most browsers:

attrFix: {
  // Always normalize to ensure hook usage
  tabindex: "tabIndex"

jQuery's comments include a reference to more details on handling this:
Getting, setting, and removing tabindex values with JavaScript.


When I started writing this article I thought getting attributes would
just be a case of using getAttribute, but thanks to browser
inconsistencies it becomes a lot more work. jQuery's use of hooks
keeps most of this out of the core framework code, so it's easy to see
what has been added to support browser behaviour. I really like the way
jQuery does this.

Next week I'll look at actually building this thing.