简体   繁体   中英

Performant append to array of existing DOM elements

I have the following piece of code, that loops over an Array of DOM objects, runs a test and appends some text to the node if returns true.

$.each( selections, function ( i, e ) {
    var test = some_test(e);
    if(test) {
       $(e).append('passed');
    }
});

Whilst this code works fine, on a large set it is obviously going to perform a lot of appends to the DOM. This article on reasons to use append correctly demonstrates how appending to the DOM is far more performant :

var arr = reallyLongArray;
var textToInsert = [];
var i = 0;
$.each(arr, function(count, item) {
    textToInsert[i++]  = '<tr><td name="pieTD">';
    textToInsert[i++] = item;
    textToInsert[i++] = '</td></tr>';
});
$('table').append(textToInsert.join(''));

My question is what is the most performant way to make changes to a set of existing DOM elements without having to call .append on each element ? The above example demonstrates the creation of a new element. Is this the only way ?

What makes live DOM manipulations slow is mainly the fact that it's causing DOM reflows for most manipulations that are made. Therefore, you should strive to reduce the amount of DOM reflows by reducing the number of live DOM manipulations.

If you want to manipulate multiple elements that are already part of the DOM, one strategy that can be used is to temporarly remove the parent of those nodes from the DOM, manipulate the elements and then re-attach the parent node where it was.

In the exemple below, I detach the table before manipulating it's rows and then reattach it to the DOM. That's 2 reflows rather than n reflows.

 var data = [1, 2, 3, 4, 5, 6, 7]; populateCells(document.querySelector('table'), data); function populateCells(table, data) { var rows = table.rows, reAttachTable = temporarilyDetachEl(table); data.forEach(function (num, i) { rows[i].cells[0].innerHTML = num; }); reAttachTable(); } function temporarilyDetachEl(el) { var parent = el.parentNode, nextSibling = el.nextSibling; parent.removeChild(el); return function () { if (nextSibling) parent.insertBefore(el, nextSibling); else parent.appendChild(el); }; } 
 <table> <tr><td></td></tr> <tr><td></td></tr> <tr><td></td></tr> <tr><td></td></tr> <tr><td></td></tr> <tr><td></td></tr> <tr><td></td></tr> </table> 

You could completely rebuild the set of DOM elements using one of the techniques from the linked article, then clear the existing set and .append() the new one. But given that you already have the set and just want to adjust a subset of its items, there's nothing wrong with your existing code.

When talking about performance it is a good idea to profile your code so that you don't have to guess.

Here is a simple test case around this example:

http://jsperf.com/most-performant-way-to-append-to-dom-elements

(and related fiddle to demo the visual side of this: http://jsfiddle.net/f62ptjbf/ )

This test compares four possible methods for doing this: (does not by any means cover all solutions)

  1. Append "passed" as text node (like your example code)
  2. Append "passed" as a SPAN node (slight variation to your example)
  3. Build a DOM fragment that renders the nodes and records the "passed", then add to DOM as a single append operation from HTML string
  4. Remove selection elements from DOM, manipulate them, then re-add to DOM.

It shows that [at least in my copy of Chrome browser] the fastest method is removal of elements before processing, then re-add to DOM after processing. (#4)

Here is another test that shows [at least in my copy of Chrome] it is faster to append SPAN elements with the text "passed" than it is to append text nodes.

http://jsperf.com/append-variations

Based on these findings, I could recommend two potential performance improvements to your code:

  1. Remove DOM elements in selection before manipulating them, then re-add when finished.
  2. Append a SPAN with the text "passed" instead of appending the text directly.

However, #1 works best if the elements have a shared parent. The performance will be a function of the number of append operations.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM