IE JavaScript Bugs: Overriding Internet Explorer’s document.getElementById() To Be W3C Compliant Exposes An Additional Bug In getAttributes()

It’s time for another technical article. Those of my readers who aren’t interested in this sort of thing can safely disregard this particular post.



The next time someone asks you why web programmers prefer Firefox to Internet Explorer, send them a link to this post. (Even if they don’t understand it!)



The increasing popularity of Ajax technologies for web application development has increased the use of JavaScript. When I first released my first open source project, xajax, back in May of 2005, the term Ajax was only a few months old. Programming in JavaScript has since become a major part of my everyday work, and increasingly a growing number of bugs found in our applications are related to inconsistencies in JavaScript implementations in different web browsers.



Most programmers who work extensively in JavaScript have been stung, often more than once, by Microsoft’s shoddy, non-standard implementation of manipulating the HTML DOM using JavaScript. IE7 has been an improvement, but it still has some bugs that make programmers want to rip out their hair.



One of the cornerstone functions for JavaScript DOM manipulation is the document.getElementById() method which allows the program to get any element in the HTML by its id attribute, which is supposed to uniquely identify that element.



There is a well known bug in the Internet Explorer implementation of the getElementById() method, which, contrary to the W3C standard, allows the method to return an element if the element’s id attribute _or_ its _name_ attribute matches the id the programmer is looking for. The standard example of why this is problem is as follows:


<html>
	<head>
		<title>Demonstrate IE7 document.getElementById() bug</title> 
		<meta name="description" content="matching on this is a bug"/>
	</head>
	<body>
		<textarea name="description" id="description">This is information about the bug</textarea>
		<script type="text/javascript">
			alert(document.getElementById('description').value);
		</script>
	</body>
</html>




If you view the example in Firefox, you will get a JavaScript alert message containing the content of the textarea. However, if you view it in IE7 the JavaScript alert will contain the word “undefined”.



The error is caused because IE’s document.getElementById(‘description’) sees the meta tag with the name attribute set to “description” and since it treats name and id attributes as interchangeable, returns the meta tag instead of the textarea which actually has an id set to “description”. Arrggh!



JavaScript programmers who are familiar with this bug often take great care to avoid the problem by being circumspect with the names and ids of their elements. However, this becomes increasingly difficult as applications become more complex with multiple reusable parts that are included into various parts of the same system, especially with multiple programmers working concurrently. Naming conventions can help, but there are times when the id of an input element (which must be unique) differs from its name attribute, which does not have to be unique.



In any case, I ran across a slick way of dealing with Internet Explorer’s badly implemented getElementById() method on a blog called Web Bug Track. The idea is to override IE’s native method with one that works according to W3C standards, like this:


 
<script type="text/javascript">
if (/msie/i.test (navigator.userAgent)) //only override IE
{
	document.nativeGetElementById = document.getElementById;
	document.getElementById = function(id)
	{
		var elem = document.nativeGetElementById(id);
		if(elem)
		{
			//make sure that it is a valid match on id
			if(elem.id == id)
			{
				return elem;
			}
			else
			{
				//otherwise find the correct element
				for(var i=1;i<document.all[id].length;i++)
				{
					if(document.all[id][i].id == id)
					{
						return document.all[id][i];
					}
				}
			}
		}
		return null;
	};
}
</script>




If we add this JavaScript code to the head of our example, it now works wonderfully, just like Firefox!



However, I recently implemented this override workaround to the code for our web application, and doing so exposed another bug in IE7 that I hadn’t run across before. This bug is in its getAttribute() method .



The bug happens when you have a form in which there is an input with the name attribute set to “id”. For example,


<html>
	<head>
		<title>Demonstrate IE7 getAttribute() bug</title> 
	</head>
	<body>
		<form id="myForm1">
			<input id="user_id" name="user_id" value="text" />
		</form>
		<form id="myForm2">
			<input id="id" name="id" value="text" />
		</form>
		<script type="text/javascript">
			var formElement1 = document.getElementById('myForm1');
			var formElement2 = document.getElementById('myForm2');
			alert(formElement1.getAttribute('id')+ "n" + formElement2.getAttribute('id'));
		</script>
	</body>
</html>




In Firefox, when you load this example you get a JavaScript alert containing the ids of the two forms:



myForm1

myform2



But in IE7 you get instead:



myForm1

[object]



Somehow, IE7′s getAttribute() method erroneously accesses the form input with the name “id” instead of the actual form element’s id! A little experimentation shows that you get the same IE7 result even if you use formElement2.id instead of the getAttribute() method. Fortunately, you can still get the correct form element id by using one of the following:



formElement2.attributes['id'].value

formElement2.getAttributeNode(‘id’).value



Our overridden getElementById() method depends on comparing the id of the element retrieved by IE7′s native method with the id that is being sought, but because of this bug in the getAttribute() method and the id property, even when the native method has returned the correct element the comparison fails because the id is the input element instead of the id attribute.



So, in order to make sure our getElementById() override for IE7 works properly, even when the element we are trying to get is a form containing an input element with the name attribute set to “id”, we have to revise our override method as follows:


<script type="text/javascript">
if (/msie/i.test (navigator.userAgent)) //only override IE
{
	document.nativeGetElementById = document.getElementById;
	document.getElementById = function(id)
	{
		var elem = document.nativeGetElementById(id);
		if(elem)
		{
			//make sure that it is a valid match on id
			if(elem.attributes['id'].value == id)
			{
				return elem;
			}
			else
			{
				//otherwise find the correct element
				for(var i=1;i<document.all[id].length;i++)
				{
					if(document.all[id][i].attributes['id'].value == id)
					{
						return document.all[id][i];
					}
				}
			}
		}
		return null;
	};
}
</script>




So there you go! Another day in the life of a JavaScript developer! Hope someone else finds this helpful until Microsoft decides to fix their JavaScript HTML DOM.


Category: technology
Tagged: , , , , ,
Bookmark: link

9 Responses to IE JavaScript Bugs: Overriding Internet Explorer’s document.getElementById() To Be W3C Compliant Exposes An Additional Bug In getAttributes()

  1. Nicely explained and very frustrating.

  2. Steven H

    Absolutely amazing post! Thanks for the fix I’ve been looking for for longer than I can remember.

  3. wow, thx for pointing (and fixing) this out, I am sure this will come in handy!

  4. Sander Aarts

    Thanks for the script. I noticed that it doesn’t work for Opera though, which has the same bug. So I took the liberty of rewriting you script to make it work for Opera as well. In this version the function will rewrite itself depending whether document.all is available (IE + Opera) or not.

    I’ve tested the script on IE7, Fx2 and Op9.

    document.nativeGetElementById = document.getElementById;

    document.getElementById = function( id ) {

    if ( document.all ) {// only override when document.all is supported (IE + Opera)

    document.getElementById = function( id ) {

    var elem = document.nativeGetElementById( id );

    if ( !elem ) return null;

    // make sure that it is a valid match on id

    if ( elem.attributes[‘id’] && elem.attributes[‘id’].value id ) return elem;

    // otherwise find the correct element

    for ( var i = 1; i < document.all[ id ].length; i++ ) {

    if ( document.all[ id ][ i ].attributes[ 'id' ].value id ) return document.all[ id ][ i ];

    }

    };

    } else {// otherwise change back to original

    document.getElementById = document.nativeGetElementById;

    document.nativeGetElementById = null;// we don’t need it anymore

    }

    };

    document.getElementById();// run document.getElementById() once to let it rewrite itself

    Hope you like it.

  5. Michael

    When I implement your fix, my “clock” script now throws a stack overflow on each interval. Why would that be happening?

    (The clock uses a function I wrote that works in IE and in FIREFOX…..at least it did before the above code was added to another part of the program)

    function getElement(ele)

    {

    var theobj = false;

    if(typeof ele == ‘string’)

    theobj = (document.getElementById)?document.getElementById(ele):document.all[ele];

    else

    theobj = ele;

    return theobj;

    }

  6. Michael,

    I think that the problem you are having results from the fact that my script replaces the built in document.getElementById function.

    Your script appears check to see if document.getElementById exists. If it does then it uses it and if it doesn’t it uses the document.all array.

    Try replacing

    theobj = (document.getElementById)?document.getElementById(ele):document.all[ele];

    with:

    theobj = (document.nativeGetElementById)?document.getElementById(ele):document.all[ele];

    and see if that works.

  7. nicowens

    We all appreciate the post and your selfless dedication to troubleshooting behavior issues and documenting solutions.

    I’m certainly no MS-zealot, but when I hear someone of your intellect and dedication do so much MS-bashing I feel the need to respond … plus I’m waiting for a DNS propogation ;)

    Forget the bugs and behavior issues of some of the latest versions of IE for a moment and be open-minded. MS doesn’t intentioanlly go behind the back of the W3C, at least not since the late 90s. I was a member of the W3C standrads body in the late 90s and understand well the shortcomings of that arrangement. It has more to do w/ capitalism and bureaucracy.

    Imagine you’re a for-profit entity (don’t read evil) in the middle of a heated browser war. Your developers have great idea and have been given the go-ahead to produce whatever they want in their browser product in order to take out the dread NS. Again, don’t interpret as evil. Those developers like everyone else loved their product and were just trying to keep their jobs and maybe get a raise. Along ambles this slow standards-minded group that is trying desparately to inject a set of rules for all browsers to adopt. There are two main problems w/ a standards-body like this, which is not government-regulated, in an industry that is functioning much like the Wild West at the time. First, if MS has a great idea for their browser (read hover attribute) and the standrds-body refuses to accept it into the regulations, are you going to build it as a competitive advantage or are you going to agree and dump the idea? Secondly, the standrds group at the time was notorioiusly slow at adopting changes or even agreeing to have the conversation in a speedy manner.

    So, MS did what every other like-minded for-profit corporation would’ve done w/ the right resources and ideas. They ran w/ their ideas and left the standrads-body in the dust. This further infuriated those members of the W3C who had self-appointed themselves the ambassadors of everything HTML/CSS at the time and it only served to further exacerbate and already defunct relationship.

    The W3C reflected at the time the attitudes of its members, which were open-source people at heart and just not willing to let a giant like MS control the browser market. it was an unwritten war that continues to this day, mostly perpetuated by people who weren’t around and don’t understand that MS revolutionized technologies like the WWW, CSS, XML. We all owe a HUGE debt of gratitude to TBL for his tireless dedication to bringing this world into a new millenium. He should be Time’s Man of the Year every year for what he put together. I don’t mean to offend him or the early starters of the www since I stand so high only b/c their shoulders are so tall, broad, and strong.

    Delete/disapprove if you prefer; I would understand your decision.

  8. nicowens,

    Thanks so much for your comment. I appreciate your measured thoughts and I agree with much of what you have said.

    I should clarify that despite my complaints, I do believe that Microsoft, despite the antipathy regularly directed toward it, including from me at times, has done an immense amount of good for technology and for the world and has often been unjustly demonized.

    For balance you should check out my post on the bug I discovered PHP 5.2.5. It did get fixed eventually.

    Programmers just like to complain. :)

    Nice to meet you nicowens.

  9. jsarma

    Hey, good news! I just did a test of your example in IE 10 on windows 7, and confirm that this is now fixed. If you set the browser to compatibility mode, it fails just like before, which is ironic, because you’d expect compatibility mode to increase compatibility.

    I can’t find any Microsoft documentation of this change, but one proof is that a lot of people who were using the ID and name attributes interchangably are now complaining about how their site no longer works in IE 10: http://www.coderanch.com/t/607559/HTML-CSS-JavaScript/getElementById

    So I guess compatibility mode is designed to deal with those people’s problems.

Leave a Reply

  • Log in to comment
  • Register for an account
  • OR Comment using your Facebook account:

Be sure you are familiar with the Comment Policy before commenting.

Anyone who wishes to comment here must register for a sixteensmallstones.org login or connect using their Facebook account. Registration is simple and fast.

Once you have activated your account, you must log in to post comments. The first time you comment will still be moderated, but once I have approved your first comment you should be able to continue to add additional comments on any article without further impediment as long as you are logged in.

Copyright © 2005-2014 J. Max Wilson. Some Rights Reserved.