.append VS .html VS .innerHTML performance
Asked Answered
M

8

54

This site's run a test between the 3 different methods and it seems .html is the fastest, followed by .append. followed by .innerHTML. Can someone explain to me the reasons why?

Here's the site which does the comparison among the three methods.

I have read this this SO question which is related but I don't really understand the given answer, and the question didn't really elaborate much regarding .innerHtml.

I don't understand the following part:

A temporary element is created, let's call it x. x's innerHTML is set to the string of HTML that you've passed. Then jQuery will transfer each of the produced nodes (that is, x's childNodes) over to a newly created document fragment, which it will then cache for next time. It will then return the fragment's childNodes as a fresh DOM collection. Note that it's actually a lot more complicated than that, as jQuery does a bunch of cross-browser checks and various other optimisations. E.g. if you pass just <div></div> to jQuery(), jQuery will take a shortcut and simply do document.createElement('div').

Can someone simplify this?

Maori answered 23/8, 2013 at 3:1 Comment(3)
Go one revision back: jsperf.com/jquery-append-vs-html-list-performance/19 Revision 20 is simply broken by some nitwit.Dekameter
Your tests are flawed based on method of execution. Many times performance is only as good as algorithms and choice of implementation (eg var i=1; has the equivalent result of var i;for(var c=0,n=10000;c<n;c++){i=1;}, but the first is clearly more optimized and a better choice)Reneareneau
Here a working benchmark for the 4 different cases: jsben.ch/#/yDvKHFactory
R
58

That benchmark is worthless. innerHTML is always faster than DOM manipulation.

jQuery seems faster because it prepares a string with all the HTML first while the others do one operation each iteration. Also note that jQuery.html() uses innerHTML whenever it can.

jQuery from benchmark

var html = '';
for (var i = 0; i < len; i++) {
  html += '<div>Test ' + i + '</div>';
}

$('#list').html(html);

innerHTML from benchmark

var list = document.getElementById('list');
for (var i = 0; i < len; i++) {
  list.innerHTML = list.innerHTML + '<div>Test ' + i + '</div>';
}

The test for innerHTML would be a lot faster if it was written like:

var list = document.getElementById('list');
var html = '';

for (var i = 0; i < len; i++) {
    html += '<div>Test ' + i + '</div>';
}

list.innerHTML = html;

http://jsben.ch/#/yDvKH

Rozellarozelle answered 23/8, 2013 at 3:12 Comment(11)
Please add style='display:none'. It looks better this way: jsperf.com/jquery-html-vs-innerhtml-the-better-way/2Strapping
so the Javascript function more efficient than jQuery?Gerianne
@MuhamadAkbarBinWidayat Yes. Native code is always faster. jQuery uses it itself. Besides innerHTML and DOM manipulation there is really not much left to manipulate a document.Rozellarozelle
@Rozellarozelle thanks. I try javascript native soon. So this my test: jsperf.com/jquery-append-vs-html-list-performance/27Gerianne
To do a proper comparison you should keep everything identical except for the exact thing that you are comparing, which is not the case with many of the jsperf comparisons that I've seen. Here is a simple, more accurate performance comparison: jsperf.com/innerhtml-vs-html-vs-empty-appendPerisarc
My observation has been that for tiny things, string concatenation is barely faster, but in practical application, arr.join is way faster. That's what these JS tests show. So if you're processing large amounts of JSON or something, definitely .push to an array. If you're appending one html tag, just + it.Stepfather
From 6 - 9 seconds using jQuery now I have 0.42 seconds using plain javascript. Thank you! Never use jquery's "html" function if you work with big data!Publea
innerHTML is fastest for low amount of operations but at 1000 iterations the html becomes huge and the browser blocks when tries to insert it as innerHTML. Is there any solution for that? I would like to insert list containing 10000 elements that are being iterated and HTML string is being generated from it.Derayne
@BojidarLyutskanov Don't use innerHTML for every operation. Use it only once when you've built the HTML you need in memory as a string.Rozellarozelle
@Rozellarozelle Of course I do that... The problem was that strings in javascript are immutable and new string was generated every time I try to concatenate which is an expensive operation. Everything is fine when you use array and join it.Derayne
That is actually not true. I benchmarked it for myself. For a thousand buttons or 10k buttons added DOM manipulation is faster than innerHTML. For less buttons it is even WAY faster. For 100k buttons innerHTML is faster. (I used fair code for both) So there are different situations.I rarely need to add more than 50 elements to the page. So I avoid innerHTML.Daviddavida
S
61

All three are slow to me. Modifying the dom on each iteration is slow.

http://jsperf.com/jquery-append-vs-html-list-performance/24

I just added a new test in there:

var html = [];
for (var i = 0; i < len; i++) {
  html.push('<div>Test ' + i + '</div>');
}

document.getElementById('list').innerHTML = html.join('');

This is much faster again. :)

My method in Firefox does 26k Ops/sec vs 1,000, 10,000, and 13

enter image description here

Simpson answered 23/8, 2013 at 3:19 Comment(4)
Yeah, saw the outcome. Never knew arrays where that fast.Rozellarozelle
@Rozellarozelle - We had massive issues building a 50 column by 200 row table client-side using templating frameworks. I got the idea from destroyallsoftware.com/talks/wat to concat an entire table. We went from a 5-10 second render to instant with it. (IE 7 still takes about 2 seconds though)Simpson
That's impressive. That talk by the way was pretty hilarious :-DRozellarozelle
Thank you! This advantage compounds for longer and more complex arrays (e.g. arrays with long strings). I made this switch and saw my performance increase by over 600x - 1,800 ms vs 3 ms.Twirl
R
58

That benchmark is worthless. innerHTML is always faster than DOM manipulation.

jQuery seems faster because it prepares a string with all the HTML first while the others do one operation each iteration. Also note that jQuery.html() uses innerHTML whenever it can.

jQuery from benchmark

var html = '';
for (var i = 0; i < len; i++) {
  html += '<div>Test ' + i + '</div>';
}

$('#list').html(html);

innerHTML from benchmark

var list = document.getElementById('list');
for (var i = 0; i < len; i++) {
  list.innerHTML = list.innerHTML + '<div>Test ' + i + '</div>';
}

The test for innerHTML would be a lot faster if it was written like:

var list = document.getElementById('list');
var html = '';

for (var i = 0; i < len; i++) {
    html += '<div>Test ' + i + '</div>';
}

list.innerHTML = html;

http://jsben.ch/#/yDvKH

Rozellarozelle answered 23/8, 2013 at 3:12 Comment(11)
Please add style='display:none'. It looks better this way: jsperf.com/jquery-html-vs-innerhtml-the-better-way/2Strapping
so the Javascript function more efficient than jQuery?Gerianne
@MuhamadAkbarBinWidayat Yes. Native code is always faster. jQuery uses it itself. Besides innerHTML and DOM manipulation there is really not much left to manipulate a document.Rozellarozelle
@Rozellarozelle thanks. I try javascript native soon. So this my test: jsperf.com/jquery-append-vs-html-list-performance/27Gerianne
To do a proper comparison you should keep everything identical except for the exact thing that you are comparing, which is not the case with many of the jsperf comparisons that I've seen. Here is a simple, more accurate performance comparison: jsperf.com/innerhtml-vs-html-vs-empty-appendPerisarc
My observation has been that for tiny things, string concatenation is barely faster, but in practical application, arr.join is way faster. That's what these JS tests show. So if you're processing large amounts of JSON or something, definitely .push to an array. If you're appending one html tag, just + it.Stepfather
From 6 - 9 seconds using jQuery now I have 0.42 seconds using plain javascript. Thank you! Never use jquery's "html" function if you work with big data!Publea
innerHTML is fastest for low amount of operations but at 1000 iterations the html becomes huge and the browser blocks when tries to insert it as innerHTML. Is there any solution for that? I would like to insert list containing 10000 elements that are being iterated and HTML string is being generated from it.Derayne
@BojidarLyutskanov Don't use innerHTML for every operation. Use it only once when you've built the HTML you need in memory as a string.Rozellarozelle
@Rozellarozelle Of course I do that... The problem was that strings in javascript are immutable and new string was generated every time I try to concatenate which is an expensive operation. Everything is fine when you use array and join it.Derayne
That is actually not true. I benchmarked it for myself. For a thousand buttons or 10k buttons added DOM manipulation is faster than innerHTML. For less buttons it is even WAY faster. For 100k buttons innerHTML is faster. (I used fair code for both) So there are different situations.I rarely need to add more than 50 elements to the page. So I avoid innerHTML.Daviddavida
S
17

How can .html be faster than .innerHTML when the .html is using .innerHTML with a lot of extra code? Here .html implementation in jQuery (taken directly from jQuery file).

html: function( value ) {
    return jQuery.access( this, function( value ) {
        var elem = this[0] || {},
            i = 0,
            l = this.length;

        if ( value === undefined ) {
            return elem.nodeType === 1 ?
                elem.innerHTML.replace( rinlinejQuery, "" ) :
                undefined;
        }

        // See if we can take a shortcut and just use innerHTML
        if ( typeof value === "string" && !rnoInnerhtml.test( value ) &&
            ( jQuery.support.htmlSerialize || !rnoshimcache.test( value )  ) &&
            ( jQuery.support.leadingWhitespace || !rleadingWhitespace.test( value ) ) &&
            !wrapMap[ ( rtagName.exec( value ) || ["", ""] )[1].toLowerCase() ] ) {

            value = value.replace( rxhtmlTag, "<$1></$2>" );

            try {
                for (; i < l; i++ ) {
                    // Remove element nodes and prevent memory leaks
                    elem = this[i] || {};
                    if ( elem.nodeType === 1 ) {
                        jQuery.cleanData( getAll( elem, false ) );
                        elem.innerHTML = value;
                    }
                }

                elem = 0;

            // If using innerHTML throws an exception, use the fallback method
            } catch(e) {}
        }

        if ( elem ) {
            this.empty().append( value );
        }
    }, null, value, arguments.length );
}
Strapping answered 23/8, 2013 at 3:20 Comment(1)
Funny how this answer actually asks a question with an explanation! :)Shackleford
A
7

As Bart said, innerHTML is always faster than DOM manipulation.

I was testing hyperHTML, so I thought I share my results. I didn't actually run my benchmarks in CodePen originally, and there is an interesting difference in that the jQuery times are much closer to innerHTML running in CodePen.

Chrome:
createFragment 312.80 ms  
hyperHTML      253.10 ms     
innerHTML       62.70 ms   
$.append       183.40 ms

Chrome (extensions off): 
createFragment 225.10 ms 
hyperHTML      139.80 ms 
innerHTML       47.80 ms 
$.append       170.90 ms

Firefox: 
createFragment 141 ms 
hyperHTML       84 ms 
innerHTML       25 ms 
$.append        90 ms

Edge: 
createFragment 422.50 ms 
hyperHTML      184.60 ms 
innerHTML       44.00 ms 
$.append      1629.69 ms

IE11: 
createFragment   1180.29 ms 
hyperHTML       13315.59 ms //slow fallbacks, IE sucks 
innerHTML         125.70 ms 
$.append         2382.49 ms

I think it is all pretty simple. JavaScript is not as fast as the browser at parsing and creating elements, because the browser is machine specific compiled code. You can't do better than just handing over the HTML and letting the browser do the work without interruption.

It is possible that some of the performance differences are due to XSS checking, which would seem prudent.

function runbench(){
  var data = [];
  for (var i = 0; i < 10001; i++) {
      data.push("<span>" + i + "</span>");
  }

  var perf=[];
  var t0 = performance.now();
  var c = document.createDocumentFragment();
  for (var i = 0; i < 10001; i++) {
      var e = document.createElement("span");
      e.innerHTML = data[i];
      c.appendChild(e);
  }
  document.querySelector('#createFragment').appendChild(c);
  document.querySelector('#createFragment').classList='done';
  var t1 = performance.now();
  perf.push(t1-t0);

  var t0 = performance.now();
  document.querySelector('#innerHTML').innerHTML = data.join('');
  document.querySelector('#innerHTML').classList='done';
  var t1 = performance.now();
  perf.push(t1-t0);

  var t0 = performance.now();
  $('#jqhtml').html(data.join(''));
  document.querySelector('#jqhtml').classList='done';
  var t1 = performance.now();
  perf.push(t1-t0);

  var t0 = performance.now();
  $('#jqappend').append(data.join(''));
  document.querySelector('#jqappend').classList='done';
  var t1 = performance.now();
  perf.push(t1-t0);

  var t0 = performance.now();
  hyperHTML.bind(document.querySelector('#hyperHTML'))       
  `${data.map(function (item) {
      return "<span>" + item + "</span>";
  })}`;
  document.querySelector('#hyperHTML').classList='done';
  var t1 = performance.now();
  perf.push(t1-t0);

  var stats = [];
  stats.push("<table>")
  stats.push("<tr><td>createFrag: </td><td>" + perf[0].toFixed(2) + "</td></tr>");
  stats.push("<tr><td>innerHTML: </td><td>" + perf[1].toFixed(2) + "</td></tr>");
  stats.push("<tr><td>$.html: </td><td>" + perf[2] .toFixed(2) + "</td></tr>");
  stats.push("<tr><td>$.append: </td><td>" + perf[3] .toFixed(2) + "</td></tr>");
  stats.push("<tr><td>hyperHTML: </td><td>" + perf[4].toFixed(2) + "</td></tr>");
  stats.push("</table>");
  $('#performance').html(stats.join(''));
  document.querySelector('#performance').classList='done';
}

https://codepen.io/jwhooper/pen/GzKwMV

Acetylcholine answered 21/1, 2019 at 17:35 Comment(0)
G
4

I think the innerHTML is faster with suggesstion @Brat.

And on creating loop and appending string should be good on using variable first. It is make your performance more good.

good code:

var html = '';
for (var i = 0; i < len; i++) {
  html += '<div>Test ' + i + '</div>';
};
$('#list').append(html);

not efficient code:

for (var i = 0; i < len; i++) {
  var html = '<div>Test ' + i + '</div>';
  $('#list').append(html);
}

for example: http://jsben.ch/#/yDvKH

Gerianne answered 23/8, 2013 at 4:2 Comment(1)
a much faster test that i tested on my end is to have an array outside the loop var html = [] and inside the loop use html.push("<td>...</td>") and once the loop ends, use element.innerHTML = html.join("") try running a test with this, its orders of magnitude fasterPaez
T
2

I also had a problem with big table redraw (about 10x100 size). It takes about 300ms to redraw whole table.

The reason was not in the jQuery.append() and not in dom.innerHTML, but in appending each element each time.

The fastest way is to concatenate all elements html code and then append it to DOM. Like this:

function redrawMyTable( myData )
{
    var innerHTML = '';
    for ( var i = 0; i < myData.length; i++ )
    {
      innerHTML += createRowFromData( myData[i] );
    }

    myTableTbody.innerHTML = innerHTML;
}
function createRowFromData( rowData )
{
    var rowHTML = '';
    for ( var i = 0; i < rowData.length; i++ )
    {
      rowHTML += createCellFromData( rowData[i] );
    }
    return rowHTML;
}
function createCellFromData( cellData )
{
    //Do everything you need, and return HTMl code as a string
    return cellHTML;
}

Now it takes only 20-30 ms (against 300ms :))

Tibbitts answered 17/2, 2015 at 7:26 Comment(0)
H
2

6 years later

Point is - don't manipulate the live DOM. Do it outside. Today, it doesn't matter where. You can use a HTML String, a DocumentFragment (which excludes Internet Explorer) or create a new Element but don't add it to the DOM, fill it as you need and THEN add it.

On Chrome and Firefox my observation is that it's all the same run time, give or take a few percent.

Building a long HTML String in chunks that are stored in an array and then join('')-ed is also not necessary any more. Years ago, I measured big time differences. Not today. Point one: there's no recognizable time difference (on Chrome and FF), and point two: the time isn't lost at this point, but in rendering.

Hippel answered 16/12, 2019 at 13:8 Comment(0)
I
0

my code innerHTML vs fragment

fragment use running time : 1300~1500ms

innerHTML use running time : 1800~2000ms

`

const data = [];
            
for(let i = 0; i < 1000001;i++){
    data.push(i);
}


function useInnerHtml(result_wrap){
    let text = ''
    for(const item of data){
         text += '<div>' + item + '</div>';
    }
    result_wrap.innerHTML = text;
}

function useFragment(result_wrap){
    const fragment= new DocumentFragment(); // or document.createDocumentFragment();
    for(const item of data){
        
        const div =  document.createElement('div');
        div.textContent = item;
        fragment.appendChild(div);
        
    }
    result_wrap.appendChild(fragment);
}


function createData(obj){
    let startTime = new Date().getTime();
    
    const targetParentNode = obj.parentNode;
    const result_wrap = targetParentNode.querySelector('.result-wrap');
    
    if(result_wrap.hasChildNodes() == false){
        if(result_wrap.className.includes('inner-html')){
            useInnerHtml(result_wrap);
        }else if(result_wrap.className.includes('fragment')){
            useFragment(result_wrap, targetParentNode);
        }else{
            alert('');
        }
    }else{
        alert('click remove button');
    }
    
    let endTime = new Date().getTime();
    
    let time = (endTime - startTime);
    targetParentNode.querySelector('.running-time').textContent = 'running time : ' + time + 'ms'
}

function removeContent(){
    [...document.querySelectorAll('.result-wrap')].map(e=>e.replaceChildren());
}

`

https://codepen.io/joohyoungkim19940805/pen/BaJQeGW

Imperfection answered 24/3, 2022 at 0:56 Comment(1)
Your answer could be improved with additional supporting information. Please edit to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers in the help center.Brainwork

© 2022 - 2024 — McMap. All rights reserved.