0

I have a complicated HTML structure that has a lot of nested divs. I want to be able to change an element that exists multiple times in the page in various places on the page but all have the same css class and html. What I currently do is:

var nodes = mutation.target.querySelectorAll(NODES_SELECTOR);
  for(var i=0; i<nodes.length; i++) {
    var node = nodes[i];
    var newDiv = document.createElement('div');
    newDiv.className = 'new-div';
    newDiv.innerHTML = '<div class=\'content-div\'></div>';

    /* Add various event listeners */
    newDiv.addEventListener('click', function (event) {
    // Do stuff
    });
    node.appendChild(newDiv);
 }

Currently this is very slow and I assume the problem comes from manipulating the dom on every iteration.

I read this answer https://stackoverflow.com/a/18394084/8600207, But I can't figure out how to apply this for my case, as in this answer the modification happens to 1 element, not multiple like my case. Is there a way to manipulate the dom once for multiple elements? (outside the for loop) P.S.: I could use jQuery if it solves the problem, but I can't change the HTML.

bCode
  • 136
  • 9
  • jQuery won't solve your issue if you still manipulate the DOM in each iteration. – evolutionxbox Jan 09 '20 at 15:16
  • 1
    You're not manipulating elements on the page. You're appending new elements to them. That's relatively a small adjustment. The multiple touch points are more of an issue. Depending on how the markup is structured, it would be better if all the elements could be appended in a single operation. – Taplar Jan 09 '20 at 15:17
  • That's my goal, but I can't figure out how to append multiple elements to multiple elements in a single operation so that the dom doesn't get changed every iteration. – bCode Jan 09 '20 at 15:20
  • Use a `DocumentFragment`, append to that, then append the fragment to your DOM. – Heretic Monkey Jan 09 '20 at 15:21
  • Related: https://stackoverflow.com/a/45110434/2181514 – freedomn-m Jan 09 '20 at 15:23
  • 3
    Not a direct answer, bit it may be that a costly part of the operation is the adding event listeners. Consider instead delegating these listeners. So for example, using jquery, once in your code outside the loop do $('document').on('click', '.content-div', function(e){ do stuff }) and this will listen for the click event on all existing AND future elements containing class 'content-div'. If you have a lot of elements this will help reduce the listeners massively. – Vanquished Wombat Jan 09 '20 at 15:24
  • 2
    A fragment can't be used, if the elements are arbirarily scattered allover the document. A modern browser can execute the script in the example within a second, and during that time it can insert 10 000 elements to the DOM. Make sure you're not reading any layout-depending values from the DOM (that causes expensive re-calculation every time, when the DOM was changed). Event delegation is one more step to go, as Wombat has described above. If that is not possible, declare the handler functions outside the loop, don't make functions in a loop. – Teemu Jan 09 '20 at 15:27
  • Your code can be reduced to a one-liner (minus "do stuff") - but whether it will be quicker or not will be for you to test in your environment. `$("
    ").appendTo(NODES_SELECTOR).click(function() { /* do stuff */ });`
    – freedomn-m Jan 09 '20 at 15:28
  • 1
    One more trick: create all the content to append outside of the loop, and clone it inside the loop. That's probably faster than playing with `innerHTML` in the loop. As a micro optimize, declare the variables with `let` and store `nodes.length` in a variable. – Teemu Jan 09 '20 at 15:44

0 Answers0