Example Table With this example I will show some aspects of memory leak problems
Example: We create a table and want to store additional data for each cell, not the cell data itself as part of <td>.....</td>. You might be able to expand the DOM cell by expando variables, but this is very slow and does not perform well, beside the leak problem.
Correct way: Keep JS part and DOM part clear devided. Our example: we create a div and load a table into it. Lets say, the div exists already on you HTML page with id = "myDiv".
var MyDiv= document.getElementById('MyDiv');
// Create an Array to hold the extended cell data
var arrTable= new array();
We create the table with 2 rows an 3 cells per row. And we add an expando (Event Handler to ech cell). To prevent closure (see next part) I do NOT write the ID as identifier into the function like onclick=showCellData("cell_"+i+"_"+y). I just use onclick= showCellData(), therfore I do not have to care about closure var i and y.
function createTable() {
var MyTable = document.createElement("TABLE"); for(var i=0;i<2;i++){ var arrTable[i] = new Array(); var MyTR = document.createElement("TR"); for(var y=0;y<3;i++){ var MyTD = document.createElement("TD"); MyTD.id ="cell_"+ i +"_"+y; MyTD.onclick=showCellData(); //MyTD.onclick=showCellData(="cell_"+ i +"_"+y); // 1) might cause a closure leak arrTable[i][y]="Some extended Data for each cell"; // MyTD.MyCustomCellData = "Some extended Data for each cell"; // 2) Circular Ref. MyTR.appendChild(MyTD); MyTD=null; } MyTable.appendChild(MyTR); MyTR=null; } MyDiv.appendChild(MyTable); // Add the table to the div: MyTable=null; MyDiv = null;
var MyTable = document.createElement("TABLE"); for(var i=0;i<2;i++){
var arrTable[i] = new Array(); var MyTR = document.createElement("TR"); for(var y=0;y<3;i++){ var MyTD = document.createElement("TD"); MyTD.id ="cell_"+ i +"_"+y; MyTD.onclick=showCellData(); //MyTD.onclick=showCellData(="cell_"+ i +"_"+y); // 1) might cause a closure leak arrTable[i][y]="Some extended Data for each cell"; // MyTD.MyCustomCellData = "Some extended Data for each cell"; // 2) Circular Ref. MyTR.appendChild(MyTD); MyTD=null; } MyTable.appendChild(MyTR); MyTR=null;
var arrTable[i] = new Array(); var MyTR = document.createElement("TR"); for(var y=0;y<3;i++){
var MyTD = document.createElement("TD"); MyTD.id ="cell_"+ i +"_"+y; MyTD.onclick=showCellData(); //MyTD.onclick=showCellData(="cell_"+ i +"_"+y); // 1) might cause a closure leak arrTable[i][y]="Some extended Data for each cell"; // MyTD.MyCustomCellData = "Some extended Data for each cell"; // 2) Circular Ref. MyTR.appendChild(MyTD); MyTD=null;
} MyTable.appendChild(MyTR); MyTR=null;
} MyDiv.appendChild(MyTable); // Add the table to the div: MyTable=null; MyDiv = null;
}
Now we need to know which cell has been clicked, using showCellData();
function showCellData(event) {
if(window.attachEvent){ var MyCellId = event.srcElement.id; // IE solution }else{ var MyCellId = e.target.id; // FF solution } // var MyCellId = this.id should work as well // Now we split the id into the row- and cell-id to access the JS engine Array var arrSplit= MyCellId.split("_"): var trId= arrSplit[1]; var tdId= arrSplit[2]; // now we access the additional data of the cell, stored in the array var MyData = arrTable[trId][ tdId]; alert(MyCellData); // do whatever you want with the extendend cell data OR document.getElementById(MyCellId).style.background="red; MyCellId=null;
if(window.attachEvent){
var MyCellId = event.srcElement.id; // IE solution
}else{
var MyCellId = e.target.id; // FF solution
} // var MyCellId = this.id should work as well // Now we split the id into the row- and cell-id to access the JS engine Array var arrSplit= MyCellId.split("_"): var trId= arrSplit[1]; var tdId= arrSplit[2]; // now we access the additional data of the cell, stored in the array var MyData = arrTable[trId][ tdId]; alert(MyCellData); // do whatever you want with the extendend cell data OR document.getElementById(MyCellId).style.background="red; MyCellId=null;
In the example above:
- We assing null to each temporary JS variable like MyTD = null - We don't use expando Variable like: MyTD.MyCustomCellData - We assing an expando event Handler to the td without any parameters for the function showCellData(); - We assign an id to MyTD variable. MyTD.id =.... This does NOT LEAK because it is a standard DOM property
There is no need to set EVERY variable to null (for example i and y), but it is not wrong, as it gives the memory back immediately, otherwhise the garbage collecter does the job (if no Leak condition exists)
In this example we created a "reference" from the TD's of DOM to "TD's" in the array arrTable. There is no real reference between the DOM and the JS engine. Therfore no LEAK will occur. The only bridge ist the value of the cell's id. Therfore we can access eighter the JS Array or the DOM element by the string, representing the id. [ "cell_"+ i +"_"+y ]. For example to set all TD's to background blue, you just loop the table's array and assign the styles by document.getElementById( "cell_"+ i +"_"+y).style.background="red";
So what is missing? The example may still leak....
On unload of the page, we have to remove ALL expando Event handlers, that we assigned to the TD.
Create a function cleanup() that loops the Table Array and remove all event handlers on unload of the page, or at the time when you destroy the table dynamically. I only show the part of removing the event handler. I assume that it is clear, that you get the i and y for each cell by looping the array. Then you are able to create the cell id [ "cell_"+ i +"_"+y ].
document.getElementById(MyCellId).onclick=null;
OR
var myCell = document.getElementById(MyCellId); myCell.onclick=null; myCell=null;
If you use an array to store real references to DOM elements you may run into troubles. It is not wrong to do this, but having complex code you may run into closure coditions and to find the reason for a LEAK becomes difficult. Therfore I recoment to keep JS part and DOM part cleanly separated (especially for not very experianced programmers)
Examples simplyfied of REAL DOM - JS references:
var arrayDom = new array();...
arrayDom[0][0] = document.createElement("TABLE"); arrayDom[1][0] = document.createElement("TR"); arrayDom[0][0].appendChild(arrayDom[1][0]); arrayDom[1][1] = document.createElement("TR"); arrayDom[0][0].appendChild(arrayDom[1][1]);
Div1 = document.createElement("DIV"); DIV1.id="div_1"; DIV1.onmousedown=new function(){......} arrayDivs[1] = Div1;
If you clean up such an examples above correctly, there is no leak conditon.
Eg.
arrayDivs[1].onmousedown = null; // no Leak (if the function does not use lokal variables - see closures) arrayDivs[1]=null;