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.
Thanks for reading my post and continuing the fight against bad DOM implementations.
Hmm, this didn’t happen in my testing. Firefox executes
createElementwith 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 callscreateElementagain with the correct arguments and all is well.It would be nicer to try the standards-compliant method first (
createElementfollowed bysetAttribute) and then only if that fails, try the special IE code. Unfortunately, on IE thesetAttributefails 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”createElementworks.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?
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
createElementis 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.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.
I used the following function when I hit this problem. It doesn’t use browser sniffing.
Basically, if the evil IE-style
createElementcall 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 ;-)
I think Chris has it. That works like a charm.
——-
Post a comment