Reputation: 831
I have a webpage which loads data via an ajax call and then populates a "table". It actually uses a 'div' element as a row and more 'div' elements as the cells. For the purposes of testing I am not applying any styles or classes to these divs, they are simply: <div><div>cell1</div><div>cell2</div></div>
. During testing I am loading 2000 rows with 5 columns in each.
I have tried using three different methods similar to these
function test(method) {
var totalRows = 2000;
var totalCols = 6;
var rows = [];
var r, c, row, cell;
for (r = 0; r < totalRows; r++) {
row = [];
rows.push(row);
for (c = 0; c < totalCols; c++) {
row.push(r + ':' + c + ' Just some text');
}
}
var container = document.getElementById('container');
var output = document.getElementById('output');
var div = document.createElement('div');
container.innerHTML = '';
var start = new Date().getTime();
switch (method) {
case 1:
for (r = 0; r < totalRows; r++) {
row = div.cloneNode(false);
container.appendChild(row);
for (c = 0; c < totalCols; c++) {
cell = div.cloneNode(false);
cell.appendChild(document.createTextNode(rows[r][c]));
row.appendChild(cell);
}
}
break;
case 2:
var outer = document.createElement('div');
var frag = document.createDocumentFragment();
for (r = 0; r < totalRows; r++) {
row = div.cloneNode(false);
frag.appendChild(row);
for (c = 0; c < totalCols; c++) {
cell = div.cloneNode(false);
cell.appendChild(document.createTextNode(rows[r][c]));
row.appendChild(cell);
}
}
container.appendChild(frag);
break;
case 3:
var els = [];
for (r = 0; r < totalRows; r++) {
els.push('<div>');
for (c = 0; c < totalCols; c++) {
els.push('<div>');
els.push(rows[r][c]);
els.push('</div>');
}
els.push('</div>');
}
// ignore string creation
start = new Date().getTime();
container.innerHTML = els.join('');
break;
}
var end = new Date().getTime();
output.innerHTML = 'time: ' + (end - start);
}
<input type="button" value="Method 1 (Direct)" onclick="test(1)"><br>
<input type="button" value="Method 2 (Fragment)" onclick="test(2)"><br>
<input type="button" value="Method 3 (innerHTML)" onclick="test(3)"><br>
<div id="output"></div>
<div id="container">
</div>
Method 1) create elements and insert directly into the DOM;
Method 2) create a document fragment and append into that. At the end insert the fragment into the DOM;
Method 3) create a text HTML representation and set the innerHTML.
In the first two methods I am cloning elements rather than creating them.
I am using plain JavaScript for this part. The examples run very quickly using Firefox, all under 40 ms. Using IE 11, the first two methods run in about 2000 ms, whereas the third method runs in 150 ms. This would be acceptable for my needs.
The problem arises when I try these methods in my actual web application. The "table" is nested in a much more complicated structure. Unfortunately it is too complicated for me to provide a working example. The table itself retains exactly the same structure. When using Firefox the table is displayed in about 1 second (after the data has been fetched from the server). This is acceptable. IE however, takes over 20 seconds and it makes little difference which of the three methods I use.
Using method 2 (appending to a document fragment first) I can see in the profiler that it takes under a second to prepare the fragment but 23 seconds to append the fragment to the DOM. The profiler shows lots of "DOM event (DOMNodeInserted)" events followed by "Garbage collection". There are perhaps 40 of these. Note: this profile is from the application not the code snippet.
The screen shot shows these entries.
The profiler labels the GC entries as JavaScript garbage collection (rather than some internal IE stuff). My questions are:
Why would it be firing the DOMNodeInserted event?
What is the GC worknig on (None of the variable are going out of scope at this point)?
Why does the profiler show 3 second gaps every second or third event (the times of the individual bits don't add up)?
Could my code be improved?
I have tried every optimization I can think of including: setting the style display to none while building it; cloning nodes instead of creating them; and declaring variable outside of the loops. As far as I can deduce I don't have any event listeners attached to this part of the DOM.
Upvotes: 2
Views: 2808
Reputation: 831
It turns out it was the profiler or more specifically having F12 tools open that caused it to slow down. My initial code was slow so I used the profile while optimising it. I will leave the question up in case anyone else has a similar problem.
Upvotes: 5