I wrote this article to show you with examples, how avoid performance issues when develop Web Applications with Vanilla Javascript.
Use s...
For further actions, you may consider blocking this person and/or reporting abuse
FWIW DocumentFragment rarely provides much of a performance benefit these days. Also, in most cases imperative DOM construction is much faster than innerHTML and it's variants (insertAdjacentHTML etc), especially since the HTML parser's performance can be severely impacted by browser extensions like ad blockers. The only trick to remember is that when building DOM up imperatively, you should always build a detached tree first, then apped the fully constructed tree to the document.
Why? I'm assuming it's because a smaller tree traverses faster, yeah?
Mutating a disconnected tree is cheaper than a connected one because it has no chance of causing side effects like layout. So you build up the largest possible disconnected tree, then "commit" all those mutations by attaching its root.
Newdays DOM is smart enough to not launch reflow after each DOM change.
The trick is "do not ask DOM for values which are not available without reflow".
For example:
You have some container, div, you add to it a couple of other elements and after each addition you ask DOM "what is height of the parent div". Most likelly this will cause a reflow and this is very costly operation.
A detached tree (or disconnected tree) is a DOM tree that hasn't been added to the document yet. Mutating DOM nodes that are not currently appended to a document is extremely cheap.
Example:
Slow Version
Items are appended to the list, which is already "connected" because it has been inserted into
document.body
:Fast Version
All we have to change here is to move the
document.body.appendChild(list)
to the end.Because items are appended to the list before the list is appended to
document.body
, building up each item and inserting it into the list is very cheap. Rendering can only happen after the very last line of code executes.For styles, you could also use
Object.assign(elem.style,
{
prop1: “value1”,
/* ... */
})
Thank you for sharing! I’ve certainly learnt a few useful things.
or by using spread syntax {...elem.style, prop1: "val1" }
Spread operator, great, thanks for your comment Fabio.
It is convenient to generate the least amount of mutation in the Dom, if you use an object the mutation will be as many properties as the object has, when using a text the mutation will only be one
It doesn't make much sense to me. In the end, the same changes are applied to the element, no matter if you use an object or text.
Could you please elaborate on your statement ?
Hi!, the use of object generates a mutation of the style for each property associated to it, an object of 10 properties generates 10 mutations to the DOM when modifying style, forcing the browser to interpret the style property 10 times, instead using a string you will generate a single mutation to the element and therefore a single interpretation of the style.
This should not be a problem for you, since this should only inconvenience the user experience when multiple elements are modified concurrently in a record time.
Another benefit of the use of string, is that your style does not have dirty properties.
Wow! Thank you very much! Now it makes sense.
Could you refer me to other resources so I can read more about that? It’s a very interesting topic.
Thanks you, I will update the article.
I’d just like to note a few mistakes you’ve made here:
will output “0123456789”, since
innerHTML
is a string, and thus you’re not addingi
, but appending it.will however output “45” instead, since both
counter
andi
are numbers.Also, you’re using
innerHTML
to insert text in quite a few of your snippets. This is however not recommended, as it goes through the HTML parser even though it doesn’t contain any HTML (Even then, you should use DOM functions to insert elements, notinnerHTML
). Thus, you should useinnerText
instead.Hi Tobias,
you're right. I made a mistake. I upgraded the article. Thanks!
45, not 55
Thanks, I fixed it.
it would be nice to have codepens or similar for the examples, this way we can see it running and you will be able to check if the code you are presenting works or not
The last example:
you are not updating the input but the container, this will update the input
Thanks for sharing
You're right. I will create a codepen with the examples. Thanks.
innerHTML is pretty darn fast: browsers have been optimized for its use, because programmers seemed to like it. See codepen.io/jwhooper/pen/GzKwMV
But unless you're building massive DOM structures (scores of thousands of elements), you're unlikely to actually see the difference in performance. I'd rather worry about writing clear, maintainable code.
In this case you can use a tiny library like lit-html
Thanks for these good tips ;)
You're welcome
Under the "Don't use DOM values inside loops", did you mean querySelectorAll? Because querySelector only returns the first match or null.
Hi Raslanove,
Thanks, I upgraded the code.
I personally use some of those techniques, but first one especially reminded me of this quote. ;-)
cloneNode() is actually slower than html.concat()
jsperf.com/dom-manipulations-szhou
Either way, thanks for your post! Very helpful!
No, added one more case (cloneNode + append.apply)
jsperf.com/dom-manipulations-szhou/2, this is the fastest - for Chrome on Mac