Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Best method to get attributes from getElementsByTagName()?

I'm playing around with getting attributes of a link tag, there seems to be several ways of accessing the attributes:

document.getElementsByTagName("link")[0]['media']
document.getElementsByTagName("link")[0].media
document.getElementsByTagName("link")[0].getAttribute('media')
document.getElementsByTagName("link")[0].attributes['media']

It's bordering on ridiculous how many paths there are to the same data. Is one of these methods far superior to the rest?

like image 917
JKirchartz Avatar asked Sep 20 '11 18:09

JKirchartz


2 Answers

I would use .media for this case, since media is indeed a property on the link element. Each of these has its use:

  • ['media']: Retrieves the "media" property value using square bracket notation. Use square bracket notation when you don't know the name of the property at design time. For example, when iterating properties.
  • .media: Retrieves the "media" property value. I'd use this in most cases. It provides concise, direct access to the property value.
  • .getAttribute('media'): Retrieves the "media" attribute value. Use this when you want the value of an attribute that is not necessarily a property on the element. Not all attributes are properties and not all properties are attributes.
  • .attributes['media']: Retrieves the "media" attribute node. Use the attributes collection when you need more information about an attribute than just it's value. For example, the attribute name. You can also easily use this to get the value, since .toString() returns the value, but that may be overkill if all you want is the value. The attributes collection is also useful for iterating the attributes of an element.
like image 63
gilly3 Avatar answered Sep 29 '22 11:09

gilly3


The method you are looking for is called getElementsByTagName. It returns an array-like list of elements (which is not an array).

Note that your last sample .attributes['media'] does not return a string as the other methods. It returns an attribute node instead.

In theory the ways of accessing the content should be equivalent but browser bugs led to other behavior in reality. It's probably best to use an abstraction layer (a library such as jQuery) to get consistent behavior. If you intend to program without a library the choice depends on your taste however I'd say that going via the attribute node is safest in general.

To add a bit more technical detail, although the different way return the same ways most of the time this is not necessarily true for non-existent attributes. Take the following HTML as example: <a href='test'>. You can try it yourself in another browser on a test jsFiddle (the output below is from Firefox).

// Get reference to element
var a = document.getElementsByTagName('a')[0];

// Existent attributes
console.log(a.href); // String: http://fiddle.jshell.net/_display/test
console.log(a.getAttribute('href')); // String: test
console.log(a.attributes['href']); // Attribute node: href

Note that one time an absolute URI was returned, another time the original value was returned.

// Existent invalid attributes
console.log(a.other); // undefined
console.log(a.getAttribute('other')); // String: thing
console.log(a.attributes['other']); // Attribute node: other

Everything that exists on page load gets merged into DOM but is not available as property if invalid.

// Inexistent but valid attributes
console.log(a.title); // Empty string
console.log(a.getAttribute('title')); // null
console.log(a.attributes['title']); // undefined

The first call returned a properties default value. Then we saw null as a marker for an inexistent attribute. Lastly we got a so called NamedNodeMap which is something like a mixture of an array and object. Accessing it as an object gave the undefined value.

// Creating attributes
a.setAttribute('title', 'test title');
console.log(a.title); // String: test title
console.log(a.getAttribute('title')); // String: test title
console.log(a.attributes['title']); // Attribute node: title

Attribute becomes available as property, too.

// Creating "attributes" by using property
a.rel = 'test rel';
console.log(a.rel); // String: test rel
console.log(a.getAttribute('rel')); // String: test rel
console.log(a.attributes['rel']); // Attribute node: rel

Setting property for a valid attribute also creates an entry in attributes map.

// Inexistent invalid attributes
console.log(a.dummyInvention); // undefined
console.log(a.getAttribute('dummyInvention')); // null
console.log(a.attributes['dummyInvention']); // undefined

Property access on a, marker return value and index access on node map.

// Creating invalid attributes via setAttribute
a.setAttribute('title2', 'test title2');
console.log(a.title2); // undefined
console.log(a.getAttribute('title2')); // String: test title2
console.log(a.attributes['title2']); // Attribute node: title2

Attribute gets created even though its existent is invalid but it is not available as property.

// Creating invalid "attributes" via property
a.title3 = 'test title3';
console.log(a.title3); // String: test title3
console.log(a.getAttribute('title3')); // null
console.log(a.attributes['title3']); // undefined

Object a is extended but DOM is untouched.

// NamedNodeMap of length 4 and indexes other, href, title, rel, title2 (valid attributes or result of setAttribute in order of creation except those from parsing)
console.log(a.attributes);

The node map only reflects the current state of the DOM. It is not aware of extension to our object a that we received via getElementsByTagName.

It's important to note that manipulating JavaScript object does not necessarily affect the DOM. The DOM only reflects the things that have been available on parsing plus modification with DOM methods or property modifications (of predefined properties that is). I hope I did not miss any important cases and that the comments have been verbose enough to see what happens.

I would appreciate a comment on the final NamedNodeMap because I would like to know if Firefox's behavior is correct there to discard the order of the attributes from parsing.

like image 44
Augustus Kling Avatar answered Sep 29 '22 10:09

Augustus Kling