The life of a developer

Sunday, August 20, 2006

IE Annoyance #1

Ok, today IE really pissed me off, and I mean it, REALLY. All the time I was fed up with it's ability (lack of) to render CSS properly, but that was kind of minor, after a little while you could tweak it to work properly. What really pissed me off today is it's total lack of a proper javascript DOM implementation. God, it sucks. So I'm starting this series of posts describing IE annoyances.

The Goal

I was happily implementing my XET framework for web development based on AJAX. The idea was that the client dispatched an event to the server, the server catched it and returned an XML containing certain actions to be taken, which could include some HTML tags to add to the DOM. So some client side javascript had the task of parsing the propietary xml tags and if needed append the html tags from the XML doc by using importNode. The importance here of importNode is that I'm copying nodes from one document to another, this involves changing the ownerDocument property of the node, so a clone wouldn't suffice, we have to IMPORT.

The problem

What do you think? IE doesn't support importNode. ImportNode has been in the DOM specs since the year 2000, yet the smart IE team has failed to implement it to date. And no, there's no other way, ownerDocument is a read only property, and IE won't allow you to modify it, so that makes cloneNode irrelevant. After some googling I found some guys solution of implementing a importNode function by creating new nodes in the target document and copying over all the attributes. So I gave it a try, and it worked, to an extent. It wasn't copying any of the css styles. After some more fiddling, browsing and hair pulling (I'm bald by now) I found out the problem. Maybe I should catalogue this as another annoyance, but anyway, IE doesn't recognize the attribute "class", for IE it has to be "className". At first I didn't realize this and was using insertAdjacentHTML to make the HTML parser parse the inserted HTML and that worked, must mean that the parser recognizes class and converts it to className for internal use, why the hell don't they just stick with class?

The solution

Ok, so in case you are wondering what the solution would be, here's the piece of javascript that makes it possible.

// IE HACK: Define _importNode for IE since it doesnt support importNode
if(!document.importNode)
{
document._importNode = function(oNode, bImportChildren)
{
var oNew;

if(oNode.nodeType == 1)
{
oNew = document.createElement(oNode.nodeName);

for(var i = 0; i < oNode.attributes.length; i++)
{
if(oNode.attributes[i].nodeValue != null && oNode.attributes[i].nodeValue != '')
{
var attrName = oNode.attributes[i].name;

if(attrName == "class")
oNew.setAttribute("className", oNode.attributes[i].value);
else
oNew.setAttribute(attrName, oNode.attributes[i].value);
}
}

if(oNode.style != null && oNode.style.cssText != null)
oNew.style.cssText = oNode.style.cssText;
}
else if(oNode.nodeType == 3)
{
oNew = document.createTextNode(oNode.nodeValue);
}

if(bImportChildren && oNode.hasChildNodes())
{
for(var oChild = oNode.firstChild; oChild; oChild = oChild.nextSibling)
{
oNew.appendChild(document._importNode(oChild, true));
}
}

return oNew;
}
}
// IE HACK (end)

With this small javascript you can then check for importNode and call _importNode if it's not available, or you could just call _importName importNode isntead, I just left it like this since it was more clear I was using a hack.

// This is for Firefox/Opera and the later for IE
if(document.importNode)
var elemClone = document.importNode(elemChild, true);
else
var elemClone = document._importNode(elemChild, true);

This was taken from the web, with a couple of my applied fixes since the original didn't work quite well. There yet a quirk, there's always one, IE will completely ignore the name attribute. That will be my next IE annoyance post, and to be honest I have found a solution but it doesn't work for me, not sure why not.

Until the next one

4 Comments:

At 12:50 PM, Blogger Neil Fraser said...

Very nice. Don't forget to add:

else if (oNode.nodeType == 8) {
oNew = document.createComment(oNode.nodeValue);
}

Greetings from Google in Mountain View, California.

 
At 9:02 AM, Blogger Sissy - la vispa pryntyl said...

Thanks for having saved our lives! we've been fighting with IE for one week....... you solved us a HUGE problem. Thanks a lot again :)

 
At 6:43 PM, Blogger JAB Creations said...

The key to the success of your post was showing us *how* to call the function...as I was unfamiliar with this.

After doing cleaning up and minimizing Kostas Zotos's example here...
http://www.in3d.eu/in3d_Programming/AJAX/XMLHttpRequest_responseXML.html

I was able to get all browsers to work including IE with the following lines...

if (document.importNode) {alert('standard'); document.getElementById('ajax_here').appendChild(document.importNode(xmlDoc.getElementsByTagName('div')[0],true));}
else {alert('ie'); document.getElementById('ajax_here').appendChild(document._importNode(xmlDoc.getElementsByTagName('div')[0],true));}

Thanks a ton for your post!

 
At 9:49 AM, Blogger Gabriel Hautclocq said...

I had to comment out the else in the following lines to make the CSS work with IE8:


//else
//{
oNew.setAttribute( attrName, oNode.attributes[i].value );
//}


It seems that in IE8 the attribute used is "class" ?

 

Post a Comment

<< Home