Beware of id and name attribute mixups when using getElementById in Internet Explorer

It is probably old news to the JavaScript experts among you, but since I recently ran into this problem myself and pulled my hair in frustration before a coworker hinted at the solution I think it’s worth mentioning:

When using getElementById to get a reference to an element via the id attribute, Internet Explorer for Windows (and some versions of Opera) will also match an element whose name attribute contains the same value.

This doesn’t always cause any noticeable problems since in most cases you’re not all that likely to have identical name and id values for different elements, but when it does happen it can lead to errors that are very hard to debug.

Here is a simple example HTML document that is susceptible to this problem:

  1. <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
  2. "http://www.w3.org/TR/html4/strict.dtd">
  3. <html>
  4. <head>
  5. <meta name="description" content="A brief description of the content on this page.">
  6. <title>The name and id attributes in IE</title>
  7. </head>
  8. <body>
  9. <div id="description">
  10. <p>A description of something.</p>
  11. </div>
  12. </body>
  13. </html>

Now imagine that you want to use JavaScript to do something to div#description. For simplicity’s sake, let’s say you write the following script that hides the div element by setting its display CSS property to none when the page is loaded:

  1. function hideIt() {
  2. var obj = document.getElementById('description');
  3. obj.style.display = 'none';
  4. }
  5. window.onload = hideIt;

That works as expected everywhere except in Internet Explorer, where nothing happens. The reason for that is that in IE, getElementById finds and returns the meta element whose name attribute has the value description before it gets to the div element.

You can avoid this either by making sure that there are no name and id conflicts in your document or by using a script that fixes the problem by overriding IE’s native getElementById method.

If you were aware of this already, good for you. If not, I hope I saved you some frustration.

Posted on February 14, 2008 in Browsers, JavaScript

Comments

  1. Yes, that’s an annoying little bug. It would’ve been more logical if IE returned an array in that case, so you at least have a chance of finding the element you want.

  2. Roger

    Thanks for the “heads up” on this. It hasn’t caused me any grief as yet, but hopefully a bell will now ring if I do come across it in the future.

  3. @Harmen: That wouldn’t make any sense at all since the function is called getElementById and not getElementsById (note the extra s).

    And getElementsById breaks the spec so that doesn’t make any sense either. IDs are supposed to be unique by design.

    And the function is a document interface function so you can’t do document.body.getElementById(elementId) either.

    Copied from spec: “Returns the Element whose ID is given by elementId. If no such element exists, returns null. Behavior is not defined if more than one element has this ID.”

    So IE is wrong to return an element that has the equivalent name, it should only return an element that has the ID you asked for.

  4. I recommend using DOMAssistant Javascript library, which should be able to overcome these problems.

  5. Oh it gets better. getElementsByName is a bit buggy too. That one knocked me for six.

    Oh and never call your form submission button ‘submit’, that’s just asking for trouble.

  6. @Mats; I know what the function should do, and obviously returning an array would be incorrect, for all the reasons you state yourself, but it would at least be a way out of IE’s faulty behaviour.

  7. dusoft,

    Thanks for the kind words. Unfortunately, though, you will experience the same problems with DOMAssistant.

    A while back I didn’t code a workaround out of avoiding any possible performance hit, and a bit naively being under the impression that every web developer should know and control the uniqueness of IDs and don’t mix them with names.

    However, in reality, with several developers working on the same code and also code coming from a source which you can’t control, these things will occur from time to time.

    Therefore, in the upcoming version of DOMAssistant, this has been addressed (due to be released in a week or two).

    To analyze the problem, it doesn’t seem to affect all kinds of elements, and it depends on the order of the elements in the source order. This will cause the error:

    <input type=”text” id=”some_name” value=”Correct”>

    <input type=”text” name=”some_name” value=”WRONG”>

    While this won’t:

    <input type=”text” name=”some_name” value=”WRONG”>

    <input type=”text” id=”some_name” value=”Correct”>

    As a sidenote, this is only the tip of the iceberg when it comes to weirdness in IE. I recommend reading Attribute nightmare in IE (not for the faint-hearted).

  8. @Simon: It’ll probably be blindingly obvious when explained, but why should submit buttons never be called ‘submit’?

  9. Thanks Roger, that is something I will look out for. Thanks for the example as well, because, up until reading it I was thinking “this will never happen to me, I only give name attributes to inputs and I always give the same id”. Now I will never id anything “description” and I must look into other possibilities too!

  10. February 14, 2008 by Paul S

    @Mark Stickley, I believe it’s because there’s a JS function called submit() but I could be wrong?

  11. Thanks for the tip of, Roger! I’m sure this will (or perhaps already has) come up at some time… I just can’t believe that a function that’s supposed to look only for ID’s somehow picks up name tags too. Silly IE.

  12. February 14, 2008 by Martin Odhelius

    Yes I went into the exact same issue, with the exact same id (description) some months ago and recognize your frustration ;) Almost like the classic in old versions of IE where you could misspell some properties without even getting an error, like spelling length as lenght and it still worked…

  13. Even though IDs should be unique, I believe querySelectorAll() should (according to specs) return every element matched even if they have same IDs. So if IE messes it up I don’t believe “IDs should be unique” is a good reason not to return an array.

    Unfortunately, my understanding of the spec indicates that despite the invalidity of multiple elements having the same id, we’re required to return all elements with a given id.

    David Smith

    So, querySelectorAll(‘#foo’) will not be as fast as getElementById(‘foo’) because querySelectorAll should keep looking after it finds one.

    Of course, instead of making it return an array it would be better to simply fix the bug. But then the best thing of all would be if some sort of virus destroyed every shred of IE out there and replaced it with a real browser.

  14. Yes well known to some.

    The original bug report (and 3rd resolution) posted on Web Bug Track seems to be the best solution. Bug report

    I also like that the site is dedicated to all bugs of this kind of nature in IE, Firefox, Safari, Opera & Konqueror (etc.)

    If you really want to scare yourself and spend an hour updating your IE IQ, just search for IE or DOM Methods. The number of bugs is just astounding!

  15. I don’t see it as a real bug. The HTML 4.0.1 specification states that “the id attribute shares the same name space as the name attribute when used for anchor names.”

    This also applies to XHTML documents rendered on existing HTML user agents:

  16. The HTML spec also states that “Scripting engines should observe the following precedence rules when identifying an element: a name attribute takes precedence over an id if both are set.”

    So in your example, why is IE wrong?

  17. @Rick: Because, like the spec states, the function “Returns the Element whose ID is given by elementId. If no such element exists, returns null. Behavior is not defined if more than one element has this ID.” It clearly says ID and not name, that’s why IE is wrong.

    Even if they both are allowed on the same element.

  18. @Mats: The DOM spec, which I think you are quoting, was written for XML Documents. Unfortunately for all, we live in HTML.

    So let’s do an example. On some single element in an HTML doc, I set id=”slime” and name=”dorkus”. What should getElementByID(“dorkus”) return? The DOM says null, but HTML says a scripting engine should find the element.

    And if I do getElementByID(“slime”), the DOM finds it and HTML implies I shouldn’t.

    So which spec do you build your browser to support? I’m not saying IE’s getElementByID is correct. God strike me down first. I’m just confused.

    (By the way, I think Roger’s example is an edge case of broken IE and (sorry roger) stupid programmer error colliding.)

  19. There’s an issue that’s even more horrible (IMHO) than the name versus id one:

    • Create a form element with a name of “submit”.
    • Programatically obtain a reference to the form — i.e., var f = document.getElementById(‘myform1’);
    • Call f.submit();

    This gets especially difficult to manage if your JS and your actual form elements come from two different places.

  20. February 14, 2008 by Roger Johansson (Author comment)

    Michaël:

    I don’t see it as a real bug. The HTML 4.0.1 specification states that “the id attribute shares the same name space as the name attribute when used for anchor names.”

    Hmm, that example in the spec is interesting. An h1 element whose id attribute has the same value as an a element’s name attribute causes a validation error, which is good since the attributes share the same name space. If the name attribute is on a meta or input element however, neither validating or checking with Tidy flags it as an error. So do they share the same name space only when the name attribute is on an a element? It isn’t exactly crystal clear, at least not to me.

    Rick: As far as I can tell, the text Mats is quoting is from the Document Object Model (HTML) Level 1 spec, and so should apply to HTML.

    I do agree that this is an edge case that you don’t run into very often and that you should avoid this situation, but if I can save a couple of people some frustration by revealing my stupidity it’s worth it ;-).

  21. February 15, 2008 by Sander Aarts

    I ran into the same problem once, though with id=”keywords”. Especially when working on static templates that don’t have description or keywords meta tags all seems fine at first.

    Btw, the script you link to in your post does not take Opera into account. And simply adding a UA string identifier to the if statement at the beginning is not enough to make it work.

    I’ve tried to rewrite it for Opera and ended up adding a little extra: in this version the function rewrites itself depending on whether document.all is available (IE + Opera). That way the document.all check only has to be done once and for all browsers that don’t support that feature document.getElementById will just return to its original function. 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
    

    I hope you like it.

  22. February 15, 2008 by Sander Aarts

    *amn, this Markdown code block doesn’t look good. Copying the code (all of it) still works though ;-)

  23. @Roger I just looked. getElementById is defined in the XML portion of the DOM spec and inherited into the HTML portion. Bummer.

  24. Thanks for the tip, Roger. Now I just have to hope I remember this article if and when the time comes ;-)

  25. February 22, 2008 by smartminion

    I solved this (a long time ago) by deciding to be very anal and code HTML like a programming language with strict naming conventions…

    In my code you’ll see obnoxious things like:

    <div id=”idDescription”> <form name=”formContact”> <input type=”text” name=”strEmail”> </form> </div>

    While very verbose it’s been a huge timesaver and (for me) results in much more professional apps… Too much “Code Complete” I guess.

  26. I banged my head on that exact same problem just a couple days ago! Terribly frustrating, I finally tracked it down firebug-like using the location bar: javascript:alert(document.getElementById('description').tagName);

    That made it very obvious at last…

  27. Thanks for pointing this out Roger. Quite a handy tip.

Comments are disabled for this post (read why), but if you have spotted an error or have additional info that you think should be in this post, feel free to contact me.