Attribute nightmare in IE
I've had this article in mind for a while, as I have fiddling with Internet Explorer's crazy handling of attributes in order to patch Prototype's $$() utility.
Well, fiddling wasn't cutting the deal, so I ended up doing some large scale testing (basically running thousands of test using a sample HTML 4.01 document from W3C) to try to figure out what was going on.
A first look at getAttribute
First conclusion, already hinted at elsewhere:
element.foo === element.getAttribute('foo')
Obviously, that is a completely broken implementation, as getAttribute('foo') is supposed to return the attribute as set in the document, not the corresponding DOM element's attribute.
To exemplify:
<a href="/index.html" onclick="alert('Back home!')">Home</a>
element.getAttribute('href');
// -> "http://tobielangel.com/index.html" in IE
// -> "/index.html" in well behaved browsers
and it gets worse:
element.getAttribute('onclick');
// -> "function anonymous(){alert('Back home!')}" in IE
// -> "alert('Back home!')" elsewhere.
As a side note, this explains why IE forces you to use 'htmlFor' and 'className' to grab the 'for' and 'class' attributes.
iFlags to the rescue ?
If you read Internet Explorer's online documentation, you'll notice that it's implementation of getAttribute() is a bit particular, as it takes a second optional argument called an iFlag.
According to the documentation, if this iFlag is set to the integer 2, the getAttribute() method should return "... the value exactly as it was set in script or in the source document."
Great! Exactly what we are looking for. So let's try that:
<a href="/index.html" onclick="alert('Back home!')">Home</a>
element.getAttribute('href', 2);
// -> "/index.html" in IE
// -> "/index.html" in well behaved browsers
Looks good. Too good to be true. Alas:
element.getAttribute('onclick', 2);
// -> "function anonymous(){alert('Back home!')}" in IE
// -> "alert('Back home!')" elsewhere.
And yet more offensive:
document.body.getAttribute('onload');
// -> "function anonymous(){...}" in IE
document.body.getAttribute('onload', 2);
// -> "undefined" in IE
More strangeness
Further research brought me to consider the attributes collection. Although the documentation advises the use of an integer to access each individual attribute (how useful is that ?!), you can luckily access them by name too:
element.attributes['foo'];
// -> value of the 'foo' attribute
And guess what, this works much better. If we get back to our previous example:
element.attributes['onclick'];
// -> "alert('Back home!')"
Further more, accessing 'for' and 'class' now works:
<label for="email-address" class="my-class">Email:</label>
element.attributes['for']; // -> "email-address"
element.attributes['class']; // -> "my-class"
Unfortunately, this implementation is not bullet proof. Lots of attributes cannot be accessed that way, or return incorrect or unexpected values.
'href'and'src'behave just like if they were called throughgetAttributewithout the iFlag (i.e. the absolute path is returned).'datetime','style','enctype','value','onload','onunload','dir'and others are not contained in that collection at all, so they always return'undefined'.'rowspan','colspan','maxlength','accesskey','tabindex','valign','input','longdesc'amongst others return a default value if none has been set.
Further testing, research and discussion with Andrew Dupont led us to try out something wacky (as mentioned on his blog): use IE's proprietary outerHTML property and parse it to grab the attributes (Yes I hear your uproar - but bear with us, we were really getting desperate).
... and further disappointment!
Well, guess what: in IE, everything is broken, even their own, home-grown, homemade proprietary methods.
Consider the following:
<img src="phoenix.jpg" alt="" dir="" class="foo bar baz" id="foo" />
document.getElementById('foo').outerHTML;
// -> '<IMG class="foo bar baz" id=foo alt="" src="phoenix.jpg">'
Notice how 'dir' disappeared from the returned value.
From the brief testing we conducted, it appears that the attributes collection is mapped to outerHTML. And since the latter's implementation is shaky... so is the former's.
And now what ?!
UPDATE: As mentioned in his below comment, Andrew found out in the meantime that some attributes could only to be accessed by their camelCased version (rowSpan, colSpan, dateTime, vAlign, etc.). Now, where does that come from ?
Comments
-
I made another leap forward a few hours ago: some of the properties (rowspan, colspan, datetime, valign, etc.) need you to feed camelCase versions (rowSpan, colSpan, dateTime, vAlign) to getAttribute in order to get the proper value. Yes, this is fucking ridiculous.
Luckily, there are precious few attributes that need this, so I just built a translation table.
Thu, January 11 at 13:51 PM
-
(sigh)... just more of IE’s never-ending quest to make the web completely and totally unmanageable in every way.
Nice blog entry though… I’ll file this away for when I (inevitably) run into this issue myself sometime down the road.
Thu, January 11 at 22:29 PM
-
I Love you guys! This has baffled me on more than one occaision, but is the first time I’ve ever heard of getAttribute(‘foo’,2), which would have saved my day, had i known about it.
You guys have access to top-secret documentation us mere mortals can’t get to on msdn?
Fri, January 12 at 12:57 PM
-
Hi,
I’m intrigued to know why you think that your “well-behaved” browsers are doing the right thing? Do you have any references for that? If you read the DOM 1 specs, you’ll see that attributes are pretty much regarded as ‘live’. Did you not wonder why there was a setAttribute function, if getAttribute was always supposed to return the value that was set in the initial document? :)
Also, if you look at the DOM specs you’ll see that the use of camel case to locate an attribute whose name is not camel case is quite common. I don’t say it’s good…but you’ll see in the spec that there are things like
tabIndexandaccessKey.So, if we assume that attributes are meant to be live, and that camel case is a quirk (but it’s not Microsoft’s quirk), then you are still left with the interesting question of whether the relative path in
hrefshould have been converted to an absolute one (which is really what you are seeing).Looking again at the specifications it does say that relative paths are made absolute based on the document URL (or any
baseelement) but interestingly it doesn’t say when to do that normalisation. I certainly always assumed it happened on navigation, but your experiments show it happens earlier. I can see why one might implement it that way, and there are certainly situations where it would be useful to have all URLs ‘normalised’. But I don’t think you can say it’s a bug, or a ‘bad’ implementation, when the spec itself is quite vague.In short…I sympathise with the fact that you’ve been having problems, but it’s clearly not true (at least in this case) that ‘in IE everything is broken’.
Regards,
Mark
Fri, January 12 at 13:41 PM
-
As mentioned in his below comment, Andrew found out in the meantime that some attributes could only to be accessed by their camelCased version (rowSpan, colSpan, dateTime, vAlign, etc.). Now, where does that come from ?
From the W3C’s HTML DOM spec. See for example HTMLTableCellElement
Fri, January 12 at 13:54 PM
-
Mark, see the reply I made on Ajaxian: http://ajaxian.com/archives/attribute-nightmare-in-ie#comment-246047
Fri, January 12 at 20:30 PM
-
Gentlemen! Stop to support this browser! Or cease to complain! Be strong – choose one way to go!
Sat, January 13 at 01:33 AM
-
Opera has a weird behaviour when you use getAttribute for “action” attibute. It returns an absolute url instead of the attribute as set in the document.
http://spacergif.net/2006/07/13/opera-getattributeaction-bug/
Sat, January 13 at 07:52 AM
-
I’m working on a Javascript library. I use Element.getAttributeNode to access the attribute I want on the given element. I found that was the only way I could reliably get the attributes in every browser I was targeting (IE 6+, FF 1.5+, Safari 2.0+). Would that work in this situation?
Sun, January 14 at 00:22 AM
-
@Tobie Langel
Marty’s comment above seems to indicate so (i.e. article provides some valuable information to developers). It indicates that some developers neglect reading the MSDN Library documentation. If we wish to work effectively with IE, we should know its specific features from the primary source. I wish to notice, that all of us would like to have one standard. Because we are lazy and we hate to write for each browser separately. BUT MS team is lazy too – they won’t create new engine. And we should accept it, as due.
Sun, January 14 at 04:33 AM
-
“accept it” means just a state of affairs.
...sometimes even wrong.
It would be interesting to get into details.
Sun, January 14 at 11:16 AM
-
document.body.getAttribute(‘onload’, 2); // -> “undefined” in IE
Remarks from MSDN Library: “The onload attribute of the body object sets an onload event handler for the window.”
document.body.getAttribute(‘onload’); // -> “function anonymous(){...}” in IE
That’s his own way to parsing of intrinsic event scripts. But we have an opportunity to take advantage of the attributes collection (you mentioned it). Of course, this all is inconvenient enough. But who has told, what will be easy? ;) We must remember – there’s no hope for indulgence from IE. And that’s why I wrote my first message.
Sun, January 14 at 14:42 PM
-
I talk about:These are two different things.
Is it different things? May be in Opera…<body onload='alert("body.onload")'> <script type='text/javascript'> window.onload = function () { alert('window.onload'); } </script>Mon, January 15 at 00:38 AM
-
To be more accurate you should check attribute existance with hasAttribute() and do not rely upon JavaScript type casting when outputting element.getAttribute(). element.getAttribute() should return a Node object with nodeType=2 or null (‘undefined’ in case of IE) otherwise.
Mon, January 15 at 11:54 AM
-
Hm, it is interesting. Guys, you opened my eyes. Thanks. I have belived getAttribute(‘something’) should return the same as @something XPath expression. Now I see that there is getAttributeNode for that. I should have more thouroghly looked throught the spec.
Tue, January 16 at 05:28 AM
-
This is one of the reason why I develop web pages based on W3 standards for internal use of companies. It works great in all browsers except IE. I’m forced to support IE for public web pages. It slows down development. I would like to forget IE exists, but it exists while users still use it.
Wed, January 17 at 10:18 AM
-
I honestly believe that anyone defending IE is on Microsoft’s payroll, or a masochist that likes working double the time necessary…thanks for this article.
Wed, February 07 at 05:40 AM
-
I agree with Supak, and if anyone tried to do cross-browser ajax application from scratch without using any javascript libraries, knows the pain! I might be wrong but Firefox has (or had) some issues with element.name property … use element.getAttribute(“name”) instead …
btw it’s a very useful article for ajax developers, thnx
Thu, February 15 at 09:08 AM
-
I just ran into the IE getAttribute issue in a little script I was hobbling together to dynamically add a little ‘open in new window’ icon to external links. Works perfectly in every browser … except IE. Oh well. IE users don’t get the little icon. No biggie really.
Here’s my take on IE these days. It’s the new NS4.xx. I will only do the bare minimum necessary to preserve page function and layout with IE. Anything fancy or extra must work in Firefox/Opera/Konqueror (no Mac, can’t test Safari, Konq is as close as I can get) and degrade to a basic form in IE. Then again, I have the liberty of being able to take that stand. Some of you don’t. For you, I will say a prayer for an easy solution one day. Like an IE extension that will flip out the Trident engine for Gecko. :D
Tue, February 20 at 13:30 PM
-
hi, i need to manually execute the code contained in an onload event,
i.e. eval(element.getAttribute(‘onload’));
This works in Firefox, but like you mention, IE returns an anonymous function. How can I therefor get it to execute that function? I tried using
return(element.getAttribute(‘onload’));
but that didn’t work. Any ideas?
Thanks! helpful post.
Thu, March 01 at 01:04 AM
-
Thanks Tobie this really helped. I actually took that snippet of code you sent and created a simple html file with it and saw that it worked. Then, I tried it with the application I need it for and it didn’t work – I therefore knew something else was wrong. I dug down and found that the html editor on the page (xinha) was overwriting the original body.onload code with: window.onload = xinha_init. argh!!!!!
Anyway, thanks for indirectly pointing me in the right direction, it led me to the root of the problem.
Nice site btw.
Fri, March 02 at 14:20 PM
-
I had some issue in the past developing our functional web testing tool InCisif.net in C# + IE Control. What i have noticed is an issue with the case of the attributes. I am reading let us say the attribute name of a DIV. But the attribute name is spelled this way in the HTML: ‘nAme’. I use reflection to read the attribute name spelled ‘name’ in the C# code, the first time everything is case insensitive it is working. My reflection code is set with BindingFlags.IgnoreCase. I open other pages, click on other controls. Do I do not know what… I go back to the first page and try the same code when I try to read attribute name via reflection there is a Exception ‘Member not found’, if I pass ‘nAme’ then I can read the value. For me getAttribute(“name”,0) is working ok. I really care about what is in the DOM. What ever it is there is something weird with the case of attribute.
Fri, March 09 at 01:09 AM
-
I laughed, I cried, I cried some more. Thanks for documenting this.
Tue, March 27 at 14:22 PM