How to speed up the DOM rendering of this 5000-cell table?

Viewed 409

I need to modify each cell of a 100 columns x 50 rows HTML <table> each time a key is pressed. This works, but there is a 250ms - 500ms delay before the rendering is done on my machine.

(The code snippet below has to be opened in "full page" to see this effect, you can use the arrow keys to trigger keydown events).

How to speed up the rendering of such a 5000-cell HTML table?

Note:

  • I need to keep 100 columns and fixed width (each cell takes 1% of the window width), and fixed height for each row, thus the overflow: hidden CSS rules

  • here I used one random alphanumeric for each cell, but in my code, it is another content (still one char per cell)

var characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
window.onkeydown = function(e) {
    // very fast from here ...
    var start = new Date();
    var s = '';
    for (i = 0; i < 50; i++) {
        s += '<tr>';
        for (j = 0; j < 100; j++) {
            s += '<td>' + characters.charAt(Math.floor(Math.random() * 62)) + '</td>';
        }
        s += '</tr>\n';
    }
    console.log(new Date() - start);
    // ... to here: ~ 3 ms on average, no need to optimize the previous lines?
    document.getElementById('main').innerHTML = s;
    document.body.offsetHeight;  // optional, triggers reflow/repaint to measure elapsed time more accurately
    console.log(new Date() - start);
};
window.onkeydown();
* { padding: 0; margin: 0; border: 0; font-family: sans-serif; } 
body, html { width: 100%; }
td { width: 1%; overflow: hidden; } 
tr { line-height: 15px; overflow: hidden; white-space: nowrap; }
table { table-layout: fixed; width: 100%; border-collapse: separate; border-spacing: 0; } 
<table id='main'>
</table>

2 Answers

Here's an example of the same code, using console.time() and console.timeEnd() to do the timings.

When I run it in Microsoft Edge Version 88.0.705.9 (Official build) dev (64-bit), I get around a 114ms average render time...

var characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
window.onkeydown = function(e) {
    // very fast from here ...
    console.time('generate HTML');
    var s = '';
    for (i = 0; i < 50; i++) {
        s += '<tr>';
        for (j = 0; j < 100; j++) {
            s += '<td>' + characters.charAt(Math.floor(Math.random() * 62)) + '</td>';
        }
        s += '</tr>\n';
    }
    console.timeEnd('generate HTML');
    console.time('render');
    // ... to here: ~ 3 ms on average, no need to optimize the previous lines?
    document.getElementById('main').innerHTML = s;
    document.getElementById('main').getBoundingClientRect();
    document.body.innerHeight;
    console.timeEnd('render');
};
window.onkeydown();
* { padding: 0; margin: 0; border: 0; font-family: sans-serif; } 
body, html { width: 100%; }
td { width: 1%; overflow: hidden; } 
tr { line-height: 15px; overflow: hidden; white-space: nowrap; }
table { table-layout: fixed; width: 100%; border-collapse: separate; border-spacing: 0; }
<table id='main'>
</table>

And here I've done a little experiment with a strict 50x100 CSS grid-based display. It seems to render a little slower at its slowest.

var characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
window.onkeydown = function(e) {
    // very fast from here ...
    console.time('generate HTML');
    var s = '';
    for (i = 0; i < 50; i++) {
        for (j = 0; j < 100; j++) {
            s += '<div class="td">' + characters.charAt(Math.floor(Math.random() * 62)) + '</div>';
        }
    }
    console.timeEnd('generate HTML');
    console.time('render');
    // ... to here: ~ 3 ms on average, no need to optimize the previous lines?
    document.getElementById('main').innerHTML = s;
    document.getElementById('main').getBoundingClientRect();
    document.body.innerHeight;
    console.timeEnd('render');
};
window.onkeydown();
* { padding: 0; margin: 0; border: 0; font-family: sans-serif; } 
body, html { width: 100%; }
.td { width: 1%; overflow: hidden; min-width: 1em; } 
.wrapper { display: grid; grid-template-columns: repeat(100, 1fr); grid-template-rows: repeat(50, 1fr); }
<div id='main' class='wrapper'>
</div>

Do this and use a monospace font that supports more UTF-8 glyphs. There are plenty of discussions on SO about which unicode monospace font to choose.

window.onkeydown = function(e) {
    // very fast from here ...
    console.time('generate HTML');
   var s = '';
for (i = 0; i < 50; i++) {;
    for (j = 0; j < 100; j++) {
        s += String.fromCharCode(0x0000 + Math.random() * 0xFFFF);
    }
    s += '\n';
}
    console.timeEnd('generate HTML');
    console.time('render');
    document.getElementById('main').innerText = s;
    document.getElementById('main').getBoundingClientRect();
    document.body.innerHeight;
    console.timeEnd('render');
};
window.onkeydown();
* { padding: 0; margin: 0; border: 0; font-family: monospace; } 
body, html { width: 100%; }
<div id='main'>
</div>

Related