hendricd
11 Jan 2008, 11:30 AM
I know you Ext.core.dev's (Brian and I have discussed some of these related issues) are working on DOM cleanup for a future release. I just wanted to share a behavior you should consider.
IMO: It's good practice for any Library to gracefully "destroy what it creates". So, I tried it:
Ext.EventManager.on(window, "beforeunload", function(){
Ext.destroy( aViewport );
},window,{single:true});
The dream here, of coarse, is allow the Viewport to cascade-destroy all its child items, and so on; hopefully ending with no leaks or orphans.
But, on good ol' IE, we (seemingly) need a custom removeNode implementation to pull this off (clear circular references, release event-listeners to name a few).
For those who haven't seen this approach to "IE Leak prevention", the idea is to simply move the node (marked for death) into another DIV (the death-trap) that is not part of the document flow, and then purge it by setting deathTrap.innerHTML = "". (a cryin' shame we even have to consider this, but /:)):
(Ext.)removeNode : isIE ? function(){
var d;
return function(n){
if(n){
d = d || document.createElement('div');
d.appendChild(n);
d.innerHTML = '';
}
}
}() : function(n){
if(n && n.parentNode){
n.parentNode.removeChild(n);
}
}
Next, let's examine Ext.destroy:
destroy : function(){
for(var i = 0, a = arguments, len = a.length; i < len; i++) {
var as = a[i];
if(as){
if(as.dom){
as.removeAllListeners();
as.remove(); //Eventually passed to Ext.removeNode
continue;
}
if(typeof as.destroy == 'function'){
as.destroy();
}
}
}
},Pretty simple. Any argument passed (ultimately) gets removed from the DOM. If it's not an Element and it's derived from Component, the Components destroy method is called.
And, there-in lies some trouble. The Container (el) for Ext.Viewport (the parent container for most Ext UIs) is document.body.
So, if you (like all good boys and girls) want to cleanup after ourselves with:
myViewPort.destroy()On IE, what happens -- is that the BODY (yourViewPort.el) gets moved into our 'removeNode-death-trap', and IE errors out with 'Unknown Runtime Error !' :">
No doubt!
A proposed solution:
Ext.destroy = function(){
for(var i = 0, a = arguments, len = a.length; i < len; i++) {
var as = a[i];
if(as){
if(typeof as.destroy == 'function'){
as.destroy();
}else if(as.dom){
as.removeAllListeners();
as.remove();
}
}
}
};Allow the Component OR Element to decide how (and what in the DOM) to cleanup.
And this, (albeit selfishly ;)), permits an Ext.Element used in a [psuedo-]Decorator pattern (such as ux.ManagedIframe) to benefit from its own destroy() method.
Then, let's either fix Viewport, or removeNode itself:
Ext.removeNode = Ext.isIE ? function(){
var d;
return function(n){
if(n && n.tagName != 'BODY'){
d = d || document.createElement('div');
d.appendChild(n);
d.innerHTML = '';
}
}
}() : function(n){
if(n && n.parentNode){
n.parentNode.removeChild(n);
}
};Then, we're well-on-our-way towards no-Orphans.
IMO: It's good practice for any Library to gracefully "destroy what it creates". So, I tried it:
Ext.EventManager.on(window, "beforeunload", function(){
Ext.destroy( aViewport );
},window,{single:true});
The dream here, of coarse, is allow the Viewport to cascade-destroy all its child items, and so on; hopefully ending with no leaks or orphans.
But, on good ol' IE, we (seemingly) need a custom removeNode implementation to pull this off (clear circular references, release event-listeners to name a few).
For those who haven't seen this approach to "IE Leak prevention", the idea is to simply move the node (marked for death) into another DIV (the death-trap) that is not part of the document flow, and then purge it by setting deathTrap.innerHTML = "". (a cryin' shame we even have to consider this, but /:)):
(Ext.)removeNode : isIE ? function(){
var d;
return function(n){
if(n){
d = d || document.createElement('div');
d.appendChild(n);
d.innerHTML = '';
}
}
}() : function(n){
if(n && n.parentNode){
n.parentNode.removeChild(n);
}
}
Next, let's examine Ext.destroy:
destroy : function(){
for(var i = 0, a = arguments, len = a.length; i < len; i++) {
var as = a[i];
if(as){
if(as.dom){
as.removeAllListeners();
as.remove(); //Eventually passed to Ext.removeNode
continue;
}
if(typeof as.destroy == 'function'){
as.destroy();
}
}
}
},Pretty simple. Any argument passed (ultimately) gets removed from the DOM. If it's not an Element and it's derived from Component, the Components destroy method is called.
And, there-in lies some trouble. The Container (el) for Ext.Viewport (the parent container for most Ext UIs) is document.body.
So, if you (like all good boys and girls) want to cleanup after ourselves with:
myViewPort.destroy()On IE, what happens -- is that the BODY (yourViewPort.el) gets moved into our 'removeNode-death-trap', and IE errors out with 'Unknown Runtime Error !' :">
No doubt!
A proposed solution:
Ext.destroy = function(){
for(var i = 0, a = arguments, len = a.length; i < len; i++) {
var as = a[i];
if(as){
if(typeof as.destroy == 'function'){
as.destroy();
}else if(as.dom){
as.removeAllListeners();
as.remove();
}
}
}
};Allow the Component OR Element to decide how (and what in the DOM) to cleanup.
And this, (albeit selfishly ;)), permits an Ext.Element used in a [psuedo-]Decorator pattern (such as ux.ManagedIframe) to benefit from its own destroy() method.
Then, let's either fix Viewport, or removeNode itself:
Ext.removeNode = Ext.isIE ? function(){
var d;
return function(n){
if(n && n.tagName != 'BODY'){
d = d || document.createElement('div');
d.appendChild(n);
d.innerHTML = '';
}
}
}() : function(n){
if(n && n.parentNode){
n.parentNode.removeChild(n);
}
};Then, we're well-on-our-way towards no-Orphans.