0

Currently I'm using this code to render my arrays as HTML:

// remove all the items
while (htmlElement.firstChild) {
  htmlElement.removeChild(checkout.firstChild);
}

// re-add them + new ones
for(i of arr) {
  let item = document.createElement("div")
  htmlElement.append(item)
}

I run this function every time I make a change to my array. This isn't really efficient for larger arrays as I also delete/re-add all the items that weren't changed just to render a single change. Is there a more efficient and pretty solution?

Rory McCrossan
  • 331,213
  • 40
  • 305
  • 339
cubefox
  • 1,251
  • 14
  • 29
  • 4
    Have you thought about using a library/framework for this? The problem isn't particularly simple, as you need to effectively keep track of when you need to update the DOM. React/Vue/Angular are popular examples – OliverRadini Oct 30 '18 at 15:04
  • 3
    Using `innerHTML` and strings would increase the performance. "Pretty" is in the eye of the beholder so I'd remove that from your question. Also specify what "efficient" means? Faster? Less memory usage? – Heretic Monkey Oct 30 '18 at 15:05
  • 2
    @HereticMonkey *"Using innerHTML and strings would increase the performance."* **It would absolutely not increase performance.** In fact, that's probably the worst thing you could do as far as performance goes. – Scott Marcus Oct 30 '18 at 15:10
  • If most of the items stay the same you should create something to update only the changed elements in the array instead of building it all over from the start. – Mark Baijens Oct 30 '18 at 15:18
  • @HereticMonkey as Scott Marcus says, this wouldn't be faster; I put this together to demonstrate that: https://jsfiddle.net/a3gxcn0f/ – OliverRadini Oct 30 '18 at 15:19
  • I am assuming the 'efficient' is for faster. I would start with maintaining the state of array and writing a diffing algo (a.k.a Set theory) and then manipulating the DOM for just the difference to get to a desired state. Broadly, like others mentioned, this is what Virtual DOM in React is about. You can check a post [here](https://stackoverflow.com/questions/1723168/what-is-the-fastest-or-most-elegant-way-to-compute-a-set-difference-using-javasc) for Set theory/diffing in Javascript – adityap Oct 30 '18 at 15:20
  • @cubefox perhaps you should look into using a templating library to do this? I use [handlebars](https://handlebarsjs.com/), – Rich Oct 30 '18 at 15:20
  • I definitely suggest you go the route suggested by @OliverRadini - if you want to DIY it, [this blog post](https://medium.com/@deathmood/how-to-write-your-own-virtual-dom-ee74acc13060) is definitely a good start ;) – vzwick Oct 30 '18 at 15:20
  • @Rich handlebars won't handle DOM diffs – vzwick Oct 30 '18 at 15:20
  • It depends on _how_ and _when_ you use `innerHTML` - you can still use it in the end (instead of removing each element one by one). I've updated @OliverRadini's jsfiddle to prove this: https://jsfiddle.net/chazsolo/a3gxcn0f/1/ (I know the commas render in output, it's just an example) – chazsolo Oct 30 '18 at 15:30
  • @chazsolo Well, technically you *could* be right in some cases, but there are other issues to worry about with `.innerHTML` as well, such as security concerns and the fact that it will wipe out any previously bound event handlers. – Scott Marcus Oct 30 '18 at 15:34
  • @ScottMarcus I'm old. I was thinking of this: https://www.quirksmode.org/dom/innerhtml.html. A better benchmark might be https://www.measurethat.net/Benchmarks/Show/3748/17/create-dom-element – Heretic Monkey Oct 30 '18 at 15:34
  • @chazsolo very nice! If you add an empty string into that `join` it'll remove the commas and speed things up – OliverRadini Oct 30 '18 at 15:36
  • @ScottMarcus totally agree, I wouldn't recommend it over your answer for sure! Just wanted to show that it was the loop causing the majority of the performance hit. – chazsolo Oct 30 '18 at 15:37
  • @HereticMonkey A test like this isn't going to show real-world results. As the nesting levels get more complex, and there are other elements already in the DOM, the performance falls apart. Not to mention the security implications. – Scott Marcus Oct 30 '18 at 15:37

2 Answers2

3

Never, never, never update the DOM from within a loop. This can kill the performance of your page due to excessive re-flows and re-paints.

The thing to do is build up your new HTML in memory only and then once it's built up, inject it into the DOM in one single operation.

// Create an in-memory element to attach dynamically created elements to:
let div = document.createElement("div");

for(var i = 0; i < 10; i++) {
  let item = document.createElement("div")
  item.textContent = "div #" + i;
  div.append(item); // Append to in-memory node, not the DOM
}

// Now inject the completed node just once to the DOM
document.body.appendChild(div);
Scott Marcus
  • 64,069
  • 6
  • 49
  • 71
1

For the remove I don't know any way that is really efficient, all the variations I know have quite a performance impact.

For the creation, create a single html string and set it as the innerHTML property is in most cases the way with the least performance impact. You will all the method calls to createElement() and all the .append()-calls, later will access the DOM and reduce performance normally a lot.

Here is an example of how you can improve your code:

// re-add them + new ones
var htmlString = "";
for(i of arr) {
  htmlString += "<div></div>";
}
htmlElement.innerHTML = htmlString;
Reto
  • 66
  • 4