4

jQuery has an attr() method which retrieves the value of a given HTML attribute. For instance:

var foo = document.getElementById("foo");
$(foo).attr("id");

But, performance-wise this is not optimal since an jQuery object has to be created just to call the attr() method. This performs better: foo.id.

So, ideally we would want to avoid using attr(). But, can we do that (for any attribute)? I believe foo.id and foo.value is "safe" (cross-browser), but I remember having issues with foo.href.

Here is a list of various attributes that I would like to be able to retrieve "directly":

For any element: foo.id, foo.name
For anchors: foo.href, foo.target, foo.rel
For images, objects, iframes: foo.src, foo.width, foo.height
For form elements: foo.checked, foo.selected, foo.disabled, foo.readonly, foo.type, foo.value, foo.action

So the question is: Are the above expressions cross-browser? Can I use them safely?

A link to an article which examines this issue would also be nice.

Edit (based on the answers): The expressions in bold are not safe to use!

Šime Vidas
  • 182,163
  • 62
  • 281
  • 385

3 Answers3

13

jQuery's attr() method is misleadingly named, badly documented and hides a very important distinction: the difference between attributes and properties, something which appears to be poorly understood by many web developers, particularly those whose introduction to JavaScript has come from jQuery.

If you read no further, just take this away: you will almost never need to use attributes. It's almost always better to use the corresponding property. Indeed, this is what jQuery's attr() method usually does used to do prior to version 1.6.

Reasons to use a property and avoid getAttribute() and setAttribute() when dealing with HTML DOMs:

  • IE's implementation is broken. In older versions and quirks modes of more recent versions, attributes map directly to properties, which is contrary to the DOM standard. One consequence of this is that event handler attributes (onclick etc.) are thoroughly broken in IE. Always use a property for an event handler. Another consequence of this is that IE in these modes and versions requires you to use getAttribute("className") instead of getAttribute("class") to retrieve the class attribute.
  • Properties are (mostly) consistently implemented cross-browser
  • The relationship between some properties and attributes is not as you might think. Some attributes, notably the value attribute of an <input> element, are not tied to the property with the same name: once the value of an input has been changed (either by the user or script), getting or setting the value attribute has no effect. Instead, the value attribute is synchronized with the defaultValue property.
  • Properties are usually more convenient. Think for example about boolean attributes such as checked: this is represented in the DOM as a boolean checked property while confusion reigns about how to set the checkedness of a checkbox using an attribute. Is it removeAttribute("checked")? setAttribute("checked", "")? setAttribute("checked", false)? All are wrong, because the checked attribute is actually mapped to the defaultChecked property.

There are some situations where using an attribute may be desirable. For example, the href property of an <a> element always reports a fully qualified URL while getAttribute("href") will return the string specified in the HTML href attribute verbatim. Except, of course, in IE. jQuery's attr() does attempt to normalize this kind of inconsistency.

Some more information on this subject: http://reference.sitepoint.com/javascript/Element/setAttribute, and also this from MSDN: http://msdn.microsoft.com/en-us/library/dd347148%28v=vs.85%29.aspx

Finally, I strongly recommend using the DOM properties directly wherever possible rather than using jQuery's attr() method, but there are special cases (such as href) it handles that you need to be aware of. Of the properties you mention, all of them are completely safe to use cross-browser, with the following exceptions:

  • foo.href and foo.src (which suffers from a similar problem) as mentioned above.
  • foo.type can only be set on an <input> element before it's been added to the DOM.
  • foo.selected (where foo is an <option> element) apparently has a bug in older versions of Safari, as noted in another answer.
Tim Down
  • 318,141
  • 75
  • 454
  • 536
  • Yea, I know about that attribute/property difference. :) But I was afraid that people won't understand if I use the term "DOM HTML element property". The term "attribute" is incorrect but it's widely used and understood to refer to certain DOM element properties that were created based on the HTML attributes in the HTML source code. – Šime Vidas Dec 16 '10 at 02:10
  • Also, I believe IE9 beta resolved the href issue. `foo.href` returns the full URL, whereas `foo.attributes.href.value` and `foo.getAttribute("href")` returns the value of the href attribute in the source code. – Šime Vidas Dec 16 '10 at 02:21
  • @Šime Vidas: I like to take every opportunity to point out the difference between DOM attributes and properties. Sorry if it was unnecessary for you. I've now added a bit about the properties you asked about. – Tim Down Dec 16 '10 at 09:25
  • @Tim Yea, I like to do that too occasionally, but it can be difficult. I remember having a lengthy discussion via comments in another SO question. I was trying to explain to another user that the members of the `attributes` object of an DOM element are not HTML **attributes** but in fact DOM Attr node **properties** (objects), and that HTML attributes only exist in the HTML source code, whereas properties exist in the DOM (and also that the term attribute has a different meaning in JavaScript: a property of an object has internal attributes). But he just couldn't get it, so I gave up. – Šime Vidas Dec 16 '10 at 13:08
  • @Tim According to that MSDN article, Microsoft appears to use this terminology: 1. the original HTML attributes are referred to as **content attributes** (they can be accessed via the `attributes` property or the `getAttribute()` method, 2. the properties of the DOM element that were created based on those HTML attributes are referred to as **DOM attributes**. – Šime Vidas Dec 16 '10 at 13:21
  • @Šime: Yes, I noticed that too on the MSDN article. I've never seen that terminology elsewhere though. – Tim Down Dec 16 '10 at 14:20
  • @Tim What about the `name` attribute? I just realized that a DOM element node based on this source code: `
    `, does not contain a `name` property. Demo: http://jsfiddle.net/YjgCb/
    – Šime Vidas Jan 19 '11 at 22:21
  • @Šime: That's because `name` is not a valid attribute for `
    ` elements. For elements for which `name` is valid, there is a corresponding `name` property that works fine.
    – Tim Down Jan 20 '11 at 00:17
  • According to John Resig's keynote at JSConf, jQuery 1.6 will feature a new method, `prop()`, which will help differentiate between properties and attributes. – Yahel Apr 24 '11 at 20:00
  • @yc: I'm not sure whether this is good news or not. It depends on whether they change the behaviour of `attr()`, which I don't really think they can do at this point. Given that `attr()` already uses properties, I'm not sure what a new `prop()` method could do that would help, short of deprecating `attr()` and replacing it with two separate methods. I think they made their bed 5 years ago with `attr()` and now they have to lie in it. – Tim Down Apr 24 '11 at 23:27
  • @Tim Down I think they're planning on changing `attr()`s behavior, that's why its happening in a `1.6` release rather than a `1.5.3` or something like that. Link: http://ontwik.com/javascript/jquery-conf-2011-jquery-keynote-by-john-resig/ 14:00 in: John Resig: "We think this will have the potential to break things in code, but we think its an essential change." – Yahel Apr 25 '11 at 00:09
  • 1
    @yc: Wow. That is going to break all kinds of stuff so I wouldn't be surprised if they backtracked on that, but I hope not. Resig's right, it is an essential change. Thanks for the link. – Tim Down Apr 25 '11 at 00:56
  • 1
    *"We think this will have the potential to break things..."* New top nominee for understatement of the year. – user113716 May 20 '11 at 00:14
4

Looking at how jQuery handles it:

  • href, src, and style attributes need special treatment.
  • Safari has a bug when accessing "selected" attribute on a select element. If accessing the "selected" attribute, Safari needs special treatment.

Check out the jQuery source, and search for this line to see what I mean by "special treatment":
attr: function( elem, name, value, pass ) {

In short: I think it safe to do what you want for most of the attributes listed, except for those shown above.


To keep things easy, you could do this:

$foo = $("#foo");
console.log( $foo.attr("id"), $foo.attr("style"), $foo.attr("href") );
Only 1 jQuery object is made, and you don't have to worry about manually handling anything.
simshaun
  • 21,263
  • 1
  • 57
  • 73
  • OK, to sum up: href and src need attr(). Got it. (The style attribute is messed up so I wouldn't want to retrieve it anyway.) – Šime Vidas Dec 16 '10 at 00:36
  • 1
    Caching jQuery objects (`var $foo = $("#foo");` is a good practice on page-load. However, inside event handlers, we have the `this` value which points to the DOM element related to the event. In this case, creating `$(this)` is a performance penalty and being able to avoid it by retrieving attribute values "the DOM 0 way" is a nice technique. – Šime Vidas Dec 16 '10 at 00:39
  • @ŠimeVidas you can do the same for objects inside event handlers too, unless they are dynamically changing. Just put them in an `each` loop and save `$this` once on DOM load. A bit subpar but could be helpful for performance. Another method to increase performance would be attaching the event to the parent and using the event's target, if there are enough elements (or enough number of event calls) that make you worried about performance. Of course, if what you are doing warrants so (e.g. building a widget or app that will be very heavily used), you can always handcraft everything in plain JS. – Halil Özgür Oct 06 '12 at 14:24
-1

1) I don't really think performance is an issue. My guess is that 'foo' is simply a pointer, not an object that takes up space in memory. Not sure about this though.

2) You don't have to create a variable foo at all. $("#foo").attr("id") will do.

3) DOM has several methods for accessing attributes. Try .getAttribute("name"), .setAttribute("name"), and .removeAttribute("name")

Jeff
  • 12,147
  • 10
  • 51
  • 87
  • 1) `this.id` is more then 10x faster than `$(this).attr("id")` 3) I believe those methods don't work in IE6 and IE7 – Šime Vidas Dec 16 '10 at 00:18
  • 1
    getAttribute() should work fine in IE6 and IE7. The problem is that it's inconsistent across browsers. – simshaun Dec 16 '10 at 00:22
  • IE6 should support all of these methods. They are part of DOM1. – Jeff Dec 16 '10 at 00:23
  • But there seems to be an issue: http://en.wikipedia.org/wiki/Comparison_of_layout_engines_(Document_Object_Model)#trident_Element_Attribute – Šime Vidas Dec 16 '10 at 00:27
  • @simshaun, @jeff: IE 6 and 7's implementation of attributes is **totally broken**. See my answer. – Tim Down Dec 16 '10 at 02:00