Death to bad DOM Implementations

I just encountered a DOM implementation issue in IE which took about three hours to solve (and like a year off my life). The story goes like this:

I could not, for the life of me, figure out why a form submitted in Firefox was coming through perfectly while it was missing fields in IE. The form in question has some normal fields and some dynamically generated ones (if JavaScript is enabled). The normal stuff was coming through fine, but I was getting no values for the dynamically generated fields when the form was submitted in IE. I checked the $_REQUEST variable (I am using PHP) to see what was coming through, just to be sure.

I immediately figured it was missing name attributes, but I was using the proper syntax to create the input elements via the DOM (note: the actual JS is more generic than this)

var inpt = document.createElement('input');
inpt.setAttribute('name', 'company');

Indeed, when I looked at the page through the Web Accessibility Toolbar’s View Generated Source, it was indeed missing the name attribute:

<INPUT id=company maxLength=255
       validatefor="title" required="required">

After about another hour or two of fruitless Google-ing, I finally typed in the magic phrase (setting the name attribute in Internet Explorer) and ended up on Bennett McElwee’s blog post of the same name. Suddenly it was all clear and (as I expected) IE’s botched implementation of the DOM’s createElement function was to blame.

According to the MSDN page on the name attribute (linked and quoted in the blog entry):

The NAME attribute cannot be set at run time on elements dynamically created with the createElement method. To create an element with a name attribute, include the attribute and value when using the createElement method.

It continued with the following example:

var oAnchor =
  document.createElement("<A NAME='AnchorName'></A>");

The script “solution” Bennett posted was somewhat of a red herring, however, as Firefox would actually execute the createElement intended for IE and end up with an element named “<input name="company" />” which would be rendered on the page as

<<input name="company" />  id="company"
        maxlength="255" validatefor="title" required="required" />

Perhaps you can see why this would be problematic.

I augmented Bennett’s script slightly and renamed the function createElementWithName so I wouldn’t have to use it on every element I created in the script:

function createElementWithName(type, name) {
  var element;
  // First try the IE way; if this fails then use the standard way
  if (document.all) {
    element =
      document.createElement('< '+type+' name="'+name+'" />');
  } else {
    element = document.createElement(type);
    element.setAttribute('name', name);
  }
  return element;
}

I am not a super fan of the reference to document.all as it feels so much like browser sniffing. I am up for suggestions to improve the function if you have any ideas.

Anyway, I am posting this to hopefully save someone else from the major headache I had today.

16 Responses to “Death to bad DOM Implementations”

  1. Bennett Says:

    Thanks for reading my post and continuing the fight against bad DOM implementations.

    Firefox would actually execute the createElement intended for IE and end up with an element named “<input name="company" />”

    Hmm, this didn’t happen in my testing. Firefox executes createElement with the IE argument; this fails and throws an exception (because the argument is not a valid element name). Then, since the element was not created, it calls createElement again with the correct arguments and all is well.

    It would be nicer to try the standards-compliant method first (createElement followed by setAttribute) and then only if that fails, try the special IE code. Unfortunately, on IE the setAttribute fails in such an insidious way that I can’t see how to use JavaScript to detect the failure. You can set the name attribute, and read it back and it seems fine. But the name is not really set, which messes up form submissions. This is why I had to use the nasty IE call first. As I said, in my testing, this fails on non-IE browsers, which is why the fallback to the “proper” createElement works.

    I’m disappointed that it didn’t work for you. I agree that browser-sniffing is not good; that’s why I used the method I did. But if it doesn’t work for you, then presumably it doesn’t work for some others too. What hope do we have?

    September 2nd, 2005 at 8:32 pm
  2. Aaron Says:

    I had high hopes for your script because it made perfect sense (well, about as much as it could, given the situation). I would have thought the first call would have failed and thrown an exception. I was actually shocked that Firefox tried to make it work with an element name so plainly wrong. I am not sure which Firefox version you were running when you did your testing, but maybe something changed between that version and the version I just tested on (1.0.6 on Windows).

    It would seem logical that the only reason Firefox should accept any string for createElement is that it needs to be able to work on the XML DOM—where element names are up to the discretion of the developer—as well as the HTML/XHTML one. One would think, however, that there would be some restriction placed on use of “<” and “>” within the string as inclusion of these characters would invalidate the tag.

    One thing I did not try was a simple document.createElement(tag+' name="'+name+'"'); which may have passed muster with Firefox altogether. It may be worth an experiment.

    September 3rd, 2005 at 8:13 am
  3. Bennett Says:

    Hi again. Curiouser and curiouser. I am also using Firefox 1.0.6 on Windows, and the function works fine.

    I have created a test page. If simply uses the function to add radio buttons to a form; I’ve created a couple of variations of the function as well. If you could go to that page using Firefox and let me know what you see (and whether the radio buttons work), that would be helpful.

    I expect that I don’t see the problem you do because I haven’t tested every possibility — you must have found something I missed. I appreciate your help on this.

    September 5th, 2005 at 9:45 pm
  4. Chris Purcell Says:

    I used the following function when I hit this problem. It doesn’t use browser sniffing.

    document.createNamedElement = function(type, name) {
      var element;
      try {
        element = document.createElement('<'+type+' name="'+name+'">');
      } catch (e) { }
      if (!element || !element.name) { // Not in IE, then
        element = document.createElement(type)
        element.name = name;
      }
      return element;
    }

    Basically, if the evil IE-style createElement call doesn’t work as intended, it will return nothing (undefined or null), throw an exception (with the same result), or create an object whose name attribute is not set. In all of these cases, therefore, try again the correct way.

    I’m also a believer in adding functions to native objects if they seem to belong there. YMMV ;-)

    October 10th, 2005 at 9:13 am
  5. Aaron Says:

    I think Chris has it. That works like a charm.

    December 3rd, 2005 at 3:32 pm
  6. Ryan Smith Says:

    My workaround to this problem was to just manipulate the innerHTML of the object that I’m adding the element to rather than trying to create the element though the DOM.

    document.getElementById(‘myEle’).innerHTML = ‘>div onclick=”javascript:do();”<Text>/div<’

    Certainly not the most elegant way, but it works.

    January 31st, 2006 at 1:44 pm
  7. Evan Fribourg Says:

    Thanks for blogging this — you sacrificed the year of your life for a good cause ;)

    April 3rd, 2006 at 7:21 pm
  8. Aaron Campos Says:

    Kind of redundant but since I ran into this too I figured I’d post the slight variation I used to get this to work using the Prototype.js Try.these() function:

    function createElementWithName( type, name ){
      return Try.these(
        function(){
          return document.createElement('<'+type+' name="'+name+'" /%gt;')
        },
        function(){
          var element = document.createElement(type);
          element.setAttribute( 'name', name );
          return element;
        } );
    }
    April 19th, 2006 at 7:00 pm
  9. Bob Ippolito Says:

    You really don’t want to use an implementation that has to do the browser capability detection on every single createElementWithName call. Instead you should do it once when *defining* the function. Will be much faster and cleaner.

    April 26th, 2006 at 6:43 pm
  10. Pete Moore Says:

    Thanks for posting this - f@#k IE - I also just spent several hours losing my mind trying to get to the bottom of this.

    I couldn’t use the solutions above as I need to rename a copied DOM element. The hack I ended up with was to mess with innerHTML, eg:

    element.parentNode.innerHTML.replace(/sourceFormName/,'newFormName')

    If anyone has a better suggestion, I’m all ears :)

    OT: I use FireBug with FireFox (if you don’t have it, stop what you are doing and install it now). I can’t help but thinking I could have saved the last several hours if I had an equivalent for IE … any suggestions?

    May 1st, 2006 at 9:39 am
  11. Anthony Lieuallen Says:

    Having just discovered this page, and seeing some very sensical comments, I came up with this version:

    function createElementWithName(){}
    (function(){
      try {
        var el=document.createElement( '<div name="foo">' );
        if( 'DIV'!=el.tagName ||
            'foo'!=el.name ){
          throw 'create element error';
        }
        createElementWithName = function( tag, name ){
          return document.createElement( '<'+tag+' name="'+name+'"></'+tag+'>' );
        }
      }catch( e ){
        createElementWithName = function( tag, name ){
          var el = document.createElement( tag );
          // setAttribute might be better here ?
          el.name = name;
          return el;
        }
      }
    })();
    May 3rd, 2006 at 1:08 pm
  12. Aaron Says:

    @bob: Exactly my thinking and Anthony beat me to the execution.

    @pete: It’s been a while since I’ve done major JS surgery in IE, so I haven’t been looking for tools, but when I need to read out what’s been triggered and what hasn’t to see where a script is failing, I usually use my homegrown app jsTrace.

    @ anthony: Thanks, that’s great. I was planning to tackle the same thing myself, but ended up too bogged down to work on it. And, yes, I’d prefer the DOM method to add the name as it’s the most forward-compatible. Great work.

    May 3rd, 2006 at 1:52 pm
  13. Mark Griffin Says:

    document.createNamedElement = function(type, name)

    function createElementWithName()

    This may be a silly question but how do you activate these functions?
    That is how do you actually call them? I’m relatively new to javascript.
    Normally you call a java funtion by simply var return = function()

    May 5th, 2006 at 6:16 pm
  14. Mark Griffin Says:

    Ooops! Actually you can call function by simply say function() itself…

    May 5th, 2006 at 7:17 pm
  15. wizardishungry/blog » When to use Google to solve your problems and when to solve things yourself Says:

    […] At work, I’ve been working on a fairly complex AJAX application that melds the Prototype Javascript library with quite a bit of ad-hoc DOM manipulation. It worked fine in Firefox, but my DOM code silently failed in Internet Explorer, despite the AJAX request successfully reaching the server. Googling the various DOM functions turned up nothing but red herrings, so I fired up my Internet Explorer script debugger and decided to step through line by line. […]

    December 12th, 2006 at 6:38 pm
  16. alt-tag.com Says:

    IE DOM Bugs…

    I’ve been working on a Javascript project where it’s necessary to create input elements (radio buttons and checkboxes) dynamically. With a functional DOM, it takes only a couple of lines of code, and works fine in Firefox and Safari. Too ba…

    December 20th, 2006 at 1:34 pm
  17. A better createElementWithName() Says:

    […] Back in 2005, I wrote a piece about IE’s abysmal generation of NAMEd elements via the DOM (which, interestingly enough, has proven to be one of the most popular posts on the blog, pointing to the fact that this is an obvious pain point for many DOM scripters out there). Since then, numerous improvements have been suggested, including the latest refactor […]

    August 27th, 2007 at 10:50 am