Sixteen Small Stones

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.

Exit mobile version