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 bad IE isn’t as DOM compatible as it claims to be.

After several searches, I discovered IE doesn’t allow the name attribute to be changed after the element is created—and it can’t be set in a DOM compatible way during creation.

Bennett McElwee suggested a solution in his blog that is nicely cross-browser; anything other than IE throws an exception and gets created properly. (I suspect modifying the parent node’s innerHTML would work as well.)

function createElement(type, name) {
   var element = null;

   try {
      // First try the IE way; if this fails then use the standard way
      element = document.createElement('<'+type+' name="'+name+'">');
   } catch (e) {
      // Probably failed because we're not running on IE
   }
   if (!element) {
      element = document.createElement(type);
      element.name = name;
   }
   return element;
}

And your type attribute too

Another part of the project requires we transform checkboxes to radio buttons and hidden fields. This could be accomplished through a page reload, but it’s overkill for such a small change. Once again, in truly DOM-compliant browsers, this requires only a couple of lines of code:

element.setAttribute("type", "radio");

The MSDN reference for the type attribute says:

As of Microsoft Internet Explorer 5, the type property is read/write-once, but only when an input element is created with the createElement method and before it is added to the document.

QuirksMode has a bug report for this, complete with test page and workaround submitted by Stijn Peeters. Stijn admits the workaround needs a little bit of cleanup.

Essentially, his solution is to always remove the element, and recreate a modified one. (See the bug above!) Here’s my solution:

try {
   element.setAttribute("type", "radio");
} catch (e) {
   var newElement = null;
   var tempStr = element.getAttribute("name");
   try {
      newElement = document.createElement("<input type=\"" +typeStr+ "\" name=\"" +tempStr+ "\">");
   } catch (e) {}
   if (!newElement) {
      newElement = document.createElement("input");
      newElement.setAttribute("type", "radio");
      newElement.setAttribute("name", tempStr);
   }
   if (tempStr = element.getAttribute("value")) {
      newElement.setAttribute("value", tempStr);
   }
   element.parentNode.replaceChild(newElement, element);
}

Update:

Aaron over at easy-reader.net encountered the same problem a few months before I did. His solution is similar, and the comments there are good. If only I had found it sooner!

Javascript, Programming, Web Standards

13 Responses

  • Been trying something similar… it all works but I can’t seem to get the focus back on the input field. Any workarounds?

    function CC_changetype(target, wrde) {
    if(document.getElementById(target.id).value == wrde)
    {
    replace = document.getElementById(target.name);
    var val = replace.value;
    var id = replace.id
    var parent = replace.parentNode;
    var sibling = replace.nextSibling;
    var newel = document.createElement(‘input’);
    newel.setAttribute(‘type’, ‘password’);
    newel.setAttribute(‘value’, ”);
    newel.setAttribute(‘id’, id);
    newel.setAttribute(‘maxLength’, ’8′);
    parent.removeChild(replace);
    parent.insertBefore(newel, sibling);
    //settimeout(‘document.getElementById(newel.getAttribute(“id”)).focus();’,1000);
    }
    }

  • With IE/Win, it does seem odd that the new element does not get focus, although it does for Firefox and Safari (both on OS X). Calling select() seems to solve the problem:

    // ...
    newel.focus();
    newel.select();

  • Hey Tom,

    It solved the problem very nicely thanx!

    Another problem popped up though… I want to copy the style of the old element to the new one. But this doesn’t work. I’m a novice to javascript and can’t find a way to solve this. I can work around with var styleheight = replace.style.height; etc but then I always need to know in advance what styles are set for the element. I guess there should be a way to copy and paste a style into the new element.

    if(document.getElementById(target.id).value == wrde)
    {
    replace = document.getElementById(target.name);
    var val = replace.value;
    var id = replace.id;
    var style = replace.style;

    var parent = replace.parentNode;
    var sibling = replace.nextSibling;
    var newel = document.createElement(‘input’);
    newel.setAttribute(‘type’, ‘password’);
    newel.setAttribute(‘value’, ”);
    newel.setAttribute(‘id’, id);
    newel.setAttribute(‘style’, style);
    newel.setAttribute(‘maxLength’, ’8′);
    parent.removeChild(replace);
    parent.insertBefore(newel, sibling);
    newel.focus();
    newel.select();

    }

  • Finished…

    I finally got it all to work the way I want it. The next script has been tested in: IE6.0, NS7.1, MFirefox1.5, and Mozilla 1.6 on XP service pack 1. Don’t know about any Mac browsers or other compatibilities…

    Again thanx for the help!

    function CC_changetype(target, wrde)
    {

    var inp = document.createElement(“input”);
    inp.name = inp.id = target.id;
    inp.style.cssText = target.style.cssText;
    var parent = target.parentNode;

    if(target.value == wrde) {
    inp.type = “password”;
    inp.value = “”;
    inp.maxLength = 8;
    inp.setAttribute(‘onblur’, target.getAttribute(‘onfocus’));
    inp.setAttribute(‘onfocus’, “this.select();”);
    var fcs = true;
    }
    else if (target.value == ”) {
    inp.type = “text”;
    inp.maxLength = 99;
    inp.value = wrde;
    inp.setAttribute(‘onfocus’, target.getAttribute(‘onblur’));
    }
    if(target.value == wrde || target.value == ”) {
    parent.insertBefore(inp, target);
    parent.removeChild(target);
    }
    if(fcs) {
    inp.focus();
    inp.select();
    }
    }

  • Oops… old copy :)

    replace

    inp.setAttribute(’onfocus’, “this.select();”);

    with

    inp.setAttribute(‘onfocus’, function(){this.select();});

  • Finished…

    I finally got it all to work the way I want it. The next script has been tested in: IE6.0, NS7.1, MFirefox1.5, and Mozilla 1.6 on XP service pack 1. Don’t know about any Mac browsers or other compatibilities…

    thanx for the help!

    but if u have more rows coming dynamically u ll get prob again. so just follow this code so that u ll get perfect result.

    replace

    inp.setAttribute(’onfocus’, “this.select();”);

    &&&

    inp.setAttribute(’onfocus’, function(){this.select();});

    with following code

    inputTag3.setAttribute(‘onblur’, function(){convertToUpperCase(this);});

    and in fucntion convertToUpperCase() write as follows.

    function convertToUpperCase(fieldId)
    {
    fieldId.value = fieldId.value.toUpperCase();
    }

  • [...] 参考:1、http://www.thunderguy.com/semicolon/2005/05/23/setting-the-name-attribute-in-internet-explorer/2、http://alt-tag.com/blog/archives/2006/02/ie-dom-bugs/ [...]

  • This function works in FF and IE. The main difference with the other scripts on this page is that all of the elements attributes are preserved.

    Usage:

    oElement = document.getElementById(‘myElement’);
    setInputAttribute(oElement, ‘type’, ‘radio’);

    The code:

    function setInputAttribute(oInputElement, sAttributeName, sAttributeValue)
    {
    /* first try the DOM-compliant way
    */
    try
    {
    oInputElement.setAttribute(sAttributeName, sAttributeValue);
    }
    catch (e)
    {
    /* then try it another way
    */

    /* build HTML string for clone input */

    //get all attributes
    var aoAttributes = oInputElement.attributes;
    var iLength = aoAttributes.length;

    //create html string for new input element
    var sHTML = “‘;

    /* create new element using the HTML string */
    oNewElement = document.createElement(sHTML);

    /* replace old element with new one */
    oInputElement.parentNode.replaceChild(oNewElement, oInputElement);
    }
    }

    Good luck!

  • For IE 6 at least, setAttribute does not work, period. I have resorted to using the node proeprties (onClick, className, colSpan, id, etc.) and yes they are case-sensitive so for example, colspan onclick doesn’t work. I have the following function:

    function setIEAttribute(node,property,value) {
    eval(“node.” + property + ” = value”);
    }

    that seem to do the trick. So in my code I have, for example,

    cell.setAttribute(“colspan”,”2″);
    if (isIE) setIEAttribute(cell,”colSpan”,”2″);

    It’s not pretty but I’d rather have the constant reminder that it’s IE that causing this grief, not the DOM.

  • Hi Don,

    your fix worked for me. thanks

  • Fixes for all of these IE bugs can be found here:
    http://webbugtrack.blogspot.com/2007/08/bug-242-setattribute-doesnt-always-work.html

    Hope this helps

  • I created a similar workaround that uses three functions. A global createElement() function which has a try-catch block in it, and two other functions that cater to Internet Explorer and standards browsers. The try-catch only executes once, as the createElement function is overwritten at run time to be the createElement variation supported by the current browser.

    It might run a tad faster than other variations of this bug fix because the try-catch block is only used once.

    http://fundamentaldisaster.blogspot.com/2008/10/cross-browser-javascript-creating-dom.html

  • can any one help the onblur event is not working

    var element3 = document.createElement(“input”);
    element3.type = “text”;
    element3.id = “txt_discountAmount”;
    element3.size = “8″;
    //element3.onblur = fun1(this.value);
    //inp.setAttribute(’onfocus’, function(){this.select();});
    element3.setAttribute(“onblur”,function(){getValue(this);});
    //element3.onblur = “fun(this)”;

    cell6.appendChild(element3);

    function getValues(value)
    {
    alert(value);
    }

Hire Tom! Hire Tom!