Fixed: the IE6 form tag leak
Readers' suggestions turn into a working fix for the IE6 form node leak.
After an excellent idea from AW posted as a comment against the original thread detailing IE6’s inability to remove Form Nodes from the DOM, and a subsequent suggested tweak from Tony Issakov (TI) (AW email me a URL and I’ll link back to you if you want) we were well on our way to having a solution to the IE6 Form Node Bottomless Pit of Pseudo leakery.
I got the suggested solution working pretty quickly with the test case that I had tested against last week.
By the end with some tweaking I managed to get the Form Management side of things down to 10 lines of code:
var formManager = {
formNodes:[],
createForm: function(){
return (this.formNodes.length>0)? this.formNodes.pop() : document.createElement('FORM');
},
recycleForm: function(formNode){
formNode.removeNode(true);
this.formNodes.push(formNode);
}
};
Now applying this to a practical situation was a little more inventive. Using the Dojo contentPane as my practical example. My approach has been to apply the following steps to content that you are loading using IE6:
- Take the string that is the content from the XHR
- Replace all the FORM tags references for DIV tags (which don’t leak)
- Write the content to the DOM (with the divs)
- Find all the DIVS in that content that have ACTION attributes (Forms have Actions, DIV’s don’t so it seemed like a good way to identify the FORM tags that are now DIV tags
- Create a FORM tag using the Form Manager, so we can delete it later
- Copy all the Attributes from the ex-FORM DIV tags, across to the new FORM tags
- Copy the content from the ex-FORM DIV Tags to the new FORM tags
- Replace the ex-FORM DIV tag with the new FORM tag (these two tags should look identical now)
- Remove the ex-FORM DIV tag
This looks like this new version of _setContent on the 0.4.3 content Pane widget:
_setContent = function(cont){
// FORM Tag switcherooo
if(dojo.render.html.ie){
cont = cont.replace(/<form /gi, '<DIV ');
cont = cont.replace(/<\/form>/gi, '</DIV>');
}
this.destroyChildren();
// remove old stylenodes from HEAD
for(var i = 0; i < this._styleNodes.length; i++){
if(this._styleNodes[i] && this._styleNodes[i].parentNode){
this._styleNodes[i].parentNode.removeChild(this._styleNodes[i]);
}
}
this._styleNodes = [];
try{
var node = this.containerNode || this.domNode;
while(node.firstChild){
dojo.html.destroyNode(node.firstChild);
}
if(typeof cont != "string"){
node.appendChild(cont);
}else{
node.innerHTML = cont;
}
if(dojo.render.html.ie){
var divTags = this.domNode.getElementsByTagName('DIV');
var j = divTags.length;
for(var k = 0; k<j; k++){
var thisDiv = divTags[k];
if(thisDiv.getAttribute('action')){
newFormNode = formManager.createForm();
newFormNode.innerHTML = thisDiv.innerHTML;
// for some reason this throws an
// undefined error on teh try,
// even though it works, had to comment
// out the catch.
thisDiv.replaceNode(newFormNode);
thisDiv.outerHTML = "";
thisDiv.removeNode();
}
}
}
}catch(e){
e.text = "Couldn't load content:"+e.description;
// Had to comment this out due to the above
//this._handleDefaults(e, "onContentError");
}
});
Then when you come to destroy content, you have to add for IE:
- Check to see if the Node you’re destroying is a FORM node and recycle it
Which looks like this addition…
dojo.html.destroyNode = function(node){
// summary:
// destroy a node (it can not be used any more). For IE, this is the
// right function to call to prevent memory leaks. While for other
// browsers, this is identical to dojo.dom.removeNode
if(node.parentNode){
node = dojo.dom.removeNode(node);
}
if(node.nodeType != 3){ // ingore TEXT_NODE
if(dojo.evalObjPath("dojo.event.browser.clean", false)){
dojo.event.browser.clean(node);
}
if(dojo.render.html.ie){
if(node.nodeName == 'FORM'){
formManager.recycleForm(node);
}else{
node.outerHTML="; //prevent ugly IE mem leak associated with Node.removeChild (ticket #1727)
}
}
}
};
Now of course these wouldn’t be the final suggested solution for integrating these into Dojo, I’ll be passing this code by the Contributors list and see if they have any suggestions, but the above code does work in a real world dojo content pane example. Which means that IE6 users can rest assured that their web apps won’t have to have a bottomless pit of form nodes orphans.
First published on jpsykes.com.