I have some kind of webapp here, which of course incorporates JavaScript and jQuery.
On load, a functions wraps a span around every letter of a text - about 150 letters. Then the user can select the letters and after a confirmation, a result is displayed. Everything works nice and smooth, only the last part really kills the performance.
The results are saved in three arrays. After on click the functions fires which adds classes to the clicked elements (this is the confirmation).
I do it like this (3 times, for each array):
$.each(myArr, function( i, v ){
$(v).addClass( "my-class" );
});
It works this way, but because I manipulate the DOM heavily it kills the performance.
I am on a MacBook with 2.26 GHz and 2 GB RAM and I am not able to run a simple tooltip or anything after the classes has been added. Especially if one array is really full this has a negative performance impact.
I already tried to optimize the script. I cached every DOM object that is used more then once, but this is not possible in every case (I think...). I also used selectors like .my-class and #my-id instead of span[class = my-class] and span[id = my-id] to speed up everything. Only the last array part is bad.
Is there any way to optimize this $.each part? Caching or somehting? Or maybe using another technique?
I don't expect the script to be SUPER fast - but adding a simple tooltip after the results are shown should be possible.
What seems to be the problem is that you cause a lot of browser reflows. This happens every time you make certain changes to an object in the DOM (like changing its size, removing/appending it etc). To prevent this from happening you can:
Remove all objects at once by removing the parent object containing all objects (this causes one reflow).
Make all the neccessary changes while the objects aren't part of the DOM.
Put the parent object back in the DOM (which once again causes a reflow).
If removing the parent object causes visual distractions you can instead deep clone it (var clone = parent.cloneNode(true)), make all the changes to the clone and then replace the parent object (parent.parentNode.replaceChild(clone, parent)). Be aware if the objects have any javascript event listeners you need to rebind these to the cloned objects.
Why do not concat the arraies:
myArr = myArr[0].concat(myArr[1],myArr[2]);
$(myArr).addClass( "my-class" );
I think it can be faster.
Related
I have a DOM element (let's call it #mywriting) which contains a bigger HTML subtree (a long sequence of paragraph elements). I have to update the content of #mywriting regularly (but only small things will change, the majority of the content remains unchanged).
I wonder what is the smartest way to do this. I see two options:
In my application code I find out which child elements of #mywriting has been changed and I only update the changed child elements.
I just update the innerHTML attribute of #mywriting with the new content.
Is it worth to develop the logic of approach one to find out the changed child nodes or will the browser perform this kind of optimization when I apply approach two?
No, the browser doesn't do such optimisation. When you reassign innerHTML, it will throw away the old contents, parse the HTML, and place the new elements in the DOM.
Doing a diff to only replace (or rather, update) the parts that need an update can be worth a lot, and is done with great success in rendering libraries that employ a so-called virtual DOM.
However, they're doing that diff on an element data structure, not an HTML string. Parsing that to find out which elements changed is going to be horribly inefficient. Don't use HTML strings. (If you're already sold on them, you might as well just use innerHTML).
Without concdering the overhead of calculating which child elements has to be updated option 1 seems to be much faster (at least in chrome), according to this simple benchmark:
https://jsbench.github.io/#6d174b84a69b037c059b6a234bb5bcd0
I'm concerned about performance and best practices.
So I'm building graphical UI and learned that I shouldn't call jQuery.append() a gazillion times when creating many small elements. So I got that covered by document.createDocumentFragment(). I'm adding elements to that, then once done, I insert the fragment to the DOM (with appendChild),
Then, in another pass I calculate where each element should be. Everything is absolutely positioned and will receive x and y values via CSS transform. Unfortunately, for these calculations I need the elements in the DOM since some of them contan random length text, and I need to measure widht/height. Otherwise I would perform step 3 on the document fragment directly, before it's even inserted into the DOM.
In the final pass I apply the calculated styles over everything with jQuery.css() (will likely replace with setAttribute instead of jQuery) as part of a loop (many calls to that, unfortunately - every element has different x/y). Should I detach the container that holds all my elements while I apply the styles, then reattach it to the DOM?
Due to lack of interest I decided to test it myself. Pulling an element of 700+ articles (15000 nodes), applying various CSS on the majority of them, then putting the element back into the page was actually slower by 50%. Let the browser do its job by only showing the results of changed attributes when Layout is needed.
I read the advice somewhere to remove the element from the DOM while working on it. While this could be true when appending elements to it, this does not hold true for style manipulations.
What is the most efficient way in removing an element in DOM? (js or jquery)
removeChild()
This is what I was using always. But, recently came across this
The removed child node still exists in memory, but is no longer part
of the DOM. With the first syntax-form shown, you may reuse the
removed node later in your code, via the oldChild object reference.
So, If I don't want to preserve the removed element in memory (for better performance), what is the best method?
Or like in java, if reference is null, is it automatically garbage collected and no need to worry about performance? I am asking this specifically as I am dealing with svg and many append/remove calls are made.
Say I'm using jQuery to loop through and perform some manipulation on existing web page elements. There are multiple changes to be made, the number of elements in the set is at least somewhat large, and the element structure is somewhat complex.
Assuming I get all the elements into the same jQuery object, would it be faster to use jQuery's .clone (or .detach) method to create an "imaginary" copy to work on, remove the current elements, then re-insert the changed copy into the DOM?
...or does that not make a difference -- as live DOM elements manipulate just as fast as non-DOM ones?
Yes, actually, though your mileage may vary.
When an element is visible, manipulations will cause the browser to redraw the page. Many redraws can add up to a significant performance hit.
When an element is invisible, no redraws will be triggered.
Mass element clones are probably pretty costly, so I'd avoid doing that if possible.
You can clone that object or create a new document fragment. Make changes on that new object and replace it in the DOM:
https://developer.mozilla.org/en-US/docs/Web/API/Node.cloneNode
https://developer.mozilla.org/en-US/docs/Web/API/document.createDocumentFragment
Working on an object that is not part of the DOM will not trigger any paint/reflows.
If performance is an issue do not use jQuery, use plain old Javascript as both cloneNode and createDocumentFragment are well supported.
This question might be stupid, or basic.
Can someone explain which is the best method in adding DOM elements. We have these two ways of adding DOM elements.
Scenario: Need to add <strong>Hi</strong> inside an existing <div id="theEl"></div>.
By editing the HTML inside them.
document.getElementById("theEl").innerHTML = '<strong>Hi</strong>';
By using document.createElement().
var hi = document.createTextNode("Hi"),
strong = document.createElement("strong");
strong.appendChild(hi);
mydiv = document.getElementById("theEl");
document.body.insertBefore(strong, mydiv);
Questions
What is the best way to do? One is a single line, another is about five lines.
What is the performance aspect?
What is the right way or best practise?
Is there any difference between the codes as a whole?
If at all this question is not making sense, please let me know, I will be glad to close this or even remove this. Thanks.
For the close voter, this is not going to be a duplicate of that question. One thing I just noted is, using createElement() preserves the event handlers attached to the element. Even though that's a good point, any kind of basic web page, too has jQuery in them, which provides delegation and such stuff that allow me to have the event attached to the element even after change in HTML.
There is no "best" or "best practice". They are two different methods of adding content that have different characteristics. Which one you select depends upon your particular circumstance.
For creating lots and lots of elements, setting a block of HTML all at once has generally shown to be faster than creating and inserting lots of individual elements. Though if you really cared about this aspect of performance, you would need to test your particular circumstance in a tool like jsperf.
For creating elements with lots of fine control, setting classes from variables, setting content from variables, etc..., it is generally much easier to do this via createElement() where you have direct access to the properties of each element without having to construct a string.
If you really don't know the difference between the two methods and don't see any obvious reason to use one over the other in a particular circumstance, then use the one that's simpler and less code. That's what I do.
In answer to your specific questions:
There is no "best" way. Select the method that works best for your circumstance.
You will need to test the performance of your specific circumstance. Large amounts of HTML have been shown in some cases to be faster by setting one large string with .innerHTML rather than individually created an inserting all the objects.
There is no "right way" or "best practice. See answer #1.
There need be no difference in the end result created by the two methods if they are coded to create the same end result.
I actually like a combination of both: createElement for the outer element so you won't be removing any event handlers, and innerHTML for the content of that element, for convenience and performance. For example:
var strong = document.createElement('strong');
strong.innerHTML = 'Hi';
document.getElementById('theEl').appendChild(strong);
Of course, this technique is more useful when the content of the thing you're adding is more complex; then you can use innerHTML normally (with the exception of the outer element) but you're not removing any event listeners.
1. What is the best way to do? One is a single line, another is about five lines.
It depends on context. You probably want to use innerHTML sparingly as a rule of thumb.
2. What is the performance aspect?
DOM manipulation significantly outperforms innerHTML, but browsers seem to keep improving innerHTML performance.
3. What is the right way or best practise?
See #1.
4. Is there any difference between the codes as a whole?
Yes. The innerHTML example will replace the contents of the existing element, while the DOM example will put the new element next to the old one. You probably meant to write mydiv.appendChild(strong), but this is still different. The existing element's child nodes are appended to rather than replaced.
What did you mean by best? In just one DOM operation everything is good and shows the same performance. But when you need multiple DOM insertion, things go diferently.
Background
Every time you insert DOM node, the browser render new image of the page. So if you insert multiple child inside a DOM node, the browser renders it multiple times. That operation is the slowest that you will see.
The solution
So, we need to append most child at once. Use a empty dom node. The built in is createDocumentFragment();
var holder = createDocumentFragment();
// append everything in the holder
// append holder to the main dom tree
The real answer
If in the case is that you described, I would prefer the shortest solution. Because there is no performance penalty in one dom operation