Most performant way to check onresize of dom object - javascript

This is a really specific need. Basically, I want to listen for when any of about 20 DOM objects change height or width. The change can be caused by window resize, by contained content changes, or by some javascript acting on the DOM object.
IE offers onresize() for any DOM object, not just the viewport, and this event is VERY performant. In fact, I can call a function onresize() and the function will fire continuously during the resize of the related DOM object. For example, I can adjust another DOM object to match the height of the object with onresize() and the two object's heights will mirror each other, with no perceptible lag, even when the resized object is animated to grow in height. AWESOME. But, for all browsers except IE, onresize() only works on viewport resize (browser/frame). Shucks.
Now I'm left with polling if the objects are resizing, which is very expensive and provides very noticeable lag when I'm trying to mirror the objects.
So, my question: what is the most performant way to raise an event when a DOM object is resized?
Sorry for the long question. Also, I am specifically NOT looking to achieve this with pure CSS. This is part of an experiment.
Thanks anyone who has time to reply!

Store the width and height in a variable that is later referenced in closure. The when the necessary event is fired check the clientHeight and clientWidth property against those stored in your variable.
Here is GUI I made about 6 months ago that solves that problem by example: http://prettydiff.com/jsgui/

The most performant way is to use an HTML/CSS layout that gets the browser to size the objects to match automatically. You would have to show us your exact HTML for us to advise how that might be done in your case.
As you have discovered, there are not good notifications for you to directly monitor the resizing of individual objects. About the best you can do is to monitor all possible events that might lead to a resize and then manually check each object yourself against a previously saved size or by comparing two objects to see if they match. That will likely not get you a lag-free experience, but it will be about as good as you can do with a javascript solution.

Related

How React.js speeds up rendering with a virtual DOM

Quoting this (https://news.ycombinator.com/item?id=9155564) article
The short answer is that the DOM is not slow. Adding & removing a DOM
node is a few pointer swaps, not much more than setting a property on
the JS object.
Are the DOM bottlenecks only those things that cause a redraw? If so then shouldn't one render from React's virtual DOM amortize to the same performance as redrawing an entire component (in one browser API call of course)? I would think that the algorithms executed by the browser only try and redraw the diff from one state to another (like git maybe?). Implying that the browser maintains a virtual DOM by itself. So then what is the point of having a virtual DOM?
Also should adding an element that has the display style property set to none not be affecting performance badly? I would profile this myself but I do not know where exactly to turn as I started javascript programming only recently.
This question may be somewhat broad for SO, but as a general answer, some other quotes from the same article are also very relevant:
However, layout is slow...
[...]
Worse, layout is triggered synchronously by accessing certain properties...
[...]
Because of this, a lot of Angular and JQuery code is stupidly slow
[...]
React doesn't help speed up layout...
What react's virtual DOM does, is calculate differences between one state of the DOM and the next state and minimizes DOM updates in a very smart way.
So:
DOM itself is not slow
but layout is slow
and almost all DOM updates require layout updates
so less DOM updates is faster
And the react engine does just that (same as several other tools/ libraries with a virtual DOM).
More info on what virtual DOM is and its advantages e.g. here.
Q: "Are the DOM bottlenecks only those things that cause a redraw?"
A:
The redraw is GPU dependent. Has nothing to do with the speed od DOM updates. DOM updates are almost instant.
Everything depends on changes that do affect the document flow. If a certain DOM or DHTML change affects the document flow. The closer the affected element is to the root of the document element the greater the impact on the document reflow.
You don't need to change the DOM content in order to cause a document reflow. A simple style property change on a given parameter may push elements of the stream to change position and cause therefore force the document reflow.
Therefore no, DOM changes on fixed size Elements will not cause a document reflow, whereas the update of display is practically instant. Will be applied only on locally affected area, most of the time in a frame which may be less than 300 x 200 pixels square; a size of an area that can be redrawn with over 120fps on a really slow GPU's. But that's 5 times smoother than watching Avengers in Cinema.
( Any spatially nonequivalent change in stream-aligned content will cause a reflow. So we have to watch for changes that affect the size and position of our floating elements, changes on inline elements inside a long stream of another inline element, etc, etc. )
'
Q: "should adding an element that has the display style property set to none not be affecting performance badly?"
A:
That's correct. Adding an element with style.display: "none" to the DOM will cause no change to the existing rendering of the document, and therefore, not trigger a document reflow and will, naturally, have no impact at all; i.e.: will be as fast as adding a new property to a JavaScript object.
Regards.

Reflow/Layout performance for large application

I am using GWT to build a HTML application where the performance is correct in general.
Sometimes, it can load many objects in the DOM and the application becomes slow. I used Chrome Developer Tools Profiler to see where that time was spent (under Chrome once the app is compiled ie no GWT overhead) and it is clear that the methods getAbsoluteLeft()/getBoundingClientRect() consume the major part of this time.
Here is the implementation used under Chrome (com.google.gwt.dom.client.DOMImplStandardBase) :
private static native ClientRect getBoundingClientRect(Element element) /*-{
return element.getBoundingClientRect && element.getBoundingClientRect();
}-*/;
#Override
public int getAbsoluteLeft(Element elem) {
ClientRect rect = getBoundingClientRect(elem);
return rect != null ? rect.getLeft()
+ elem.getOwnerDocument().getBody().getScrollLeft()
: getAbsoluteLeftUsingOffsets(elem);
}
This makes sense to me, as the more elements in the DOM, the more time it may take to calculate absolute positions. But it is frustrating because sometimes you know just a subpart of your application has changed whereas those methods will still take time to calculate absolute positioning, probably because it unnecessarily recheck a whole bunch of DOM elements. My question is not necessarily GWT oriented as it is a browser/javascript related problem :
Is there any known solution to improve GWT getAbsoluteLeft/javascript getBoundingClientRect problem for large DOM elements application ?
I did not find any clues on the internet, but I thought about solution like :
(reducing number of calls for those methods :-) ...
isolate part of the DOM through iframe, in order to reduce the number of elements the browser has to evaluate to get an absolute position (although it would make difficult components to communicate ...)
in the same idea, there might be some css property (overflow, position ?) or some html element (like iframe) which tell the browser to skip a whole part of the dom or simply help the browser to get absolute position faster
EDIT :
Using Chrome TimeLine debugger, and doing a specific action while there are a lot of elements in the DOM, I have the average performance :
Recalculate style : nearly zero
Paint : nearly 1 ms
Layout : nearly 900ms
Layout takes 900ms through the getBoundingClientRect method. This page list all the methods triggering layout in WebKit, including getBoundingClientRect ...
As I have many elements in the dom that are not impacted by my action, I assume layout is doing recalculation in the whole DOM whereas paint is able through css property/DOM tree to narrow its scope (I can see it through MozAfterPaintEvent in firebug for example).
Except grouping and calling less the methods that trigger layout, any clues on how to reduce the time for layout ?
Some related articles :
Minimizing browser reflow
I finally solve my problem : getBoundingClientRect was triggering a whole layout event in the application, which was taking many times through heavy CSS rules.
In fact, layout time is not directly proportional to the number of elements in the DOM. You could draw hundred thousands of them with light style and layout will take only 2ms.
In my case, I had two CSS selectors and a background image which were matching hundred thousands of DOM elements, and that was consuming a huge amount of time during layout. By simply removing those CSS rules, I reduce the layout time from 900ms to 2ms.
The most basic answer to your question is to use lazy evaluation, also called delayed evaluation. The principle is that you only evaluate a new position when something it depends upon has changed. It generally requires a fair amount of code to set up but is much cleaner to use once that's done. You'd make one assignment to something (such as a window size) and then all the new values propagate automatically, and only the values that need to propagate.

Optimize JS/jQuery performance (getBoundingClientRect) and eliminating layout redraw

So I have a project where I'm trying to optimize a fairly complex Javascript function to the max - partly this is due to the fact that its supposed to run on smart-phones (Webkit) and every little bit counts.
I've been using various debugging and timing techniques to go through my code and rewrite everything that might be slow - like parts of jQuery based stuff where native might do better and so on. What the function does is basically take a string of html text and cut it up to fit exactly into 3 DIVs that do not have fixed position or size (a client templating mechanism).
At the moment the entire function takes around 100ms to execute in iPads browser (but in the production environment I need to ideally execute it 200 times) and the problem is that out of those 100ms at least 20ms are because of this single line of code (in 3 loops):
var maxTop = $(cur).offset().top + $(cur).outerHeight();
"cur" is just a reference to a container DIV element and the line above is calculating its bottom position (so where my text should break). From looking at the offset jQuery code I understand it uses getBoundingClientRect and even eliminating jQuery offset/sizing and calling it directly does nothing to speed it up - so its getBoundingClientRect fault (at least in Webkit). I did a bit of research on it and I understand it causes layout redraw.
But still - can't believe that I do multiple DOM clears/clones/appends and all of those are much faster than a simple element position lookup? Any ideas out there? Maybe something webkit specific? Or something that doesn't cause redraw?
Would much appreciate it!
did you try:
var maxTop = cur.offsetTop + cur.offsetHeight;
?
point is, offsetTop and offsetHeight are native dom properties, and so access should be faster than through a function.
Since I also ran into a similar problem, I had a loop in which I was fixing a series (sometimes 1000+) of DOM elements (from float to absolute). I immediately applied the fixed styling to the elements, which was a big mistake to make: Every time something is written to the DOM the style has to be recalculated when your script asks for a position of an element. Hence, do all your reading, and then all your writing, even if that means two separate loops (you can safely write to the dataset property of your DOM element).
See also: http://gent.ilcore.com/2011/03/how-not-to-trigger-layout-in-webkit.html

Performance of setting img src to unchanged value?

If I have an img tag like
<img src="example.png" />
and I set it via
myImg.src = "example.png";
to the same value again, will this be a no-op, or will browsers unnecessarily redraw the image? (I'm mainly interested in the behaviour of IE6-8, FF3.x, Safari 4-5 and Chrome.)
I need to change many (hundreds of) images at once, and manually comparing the src attribute might be a little bit superfluous - as I assume, that the browser already does this for me?
Don't assume the browser will do it for you. I am working on a project of similar scale which requires hundreds of (dynamic-loading) images, with speed as the top priority.
Caching the 'src' property of every element is highly recommended. It is expensive to read and set hundreds of DOM element properties, and even setting src to the same value may cause reflow or paint events.
[Edit] The majority of sluggishness in the interface was due to all my loops and processes. Once those were optimized, the UI was very snappy, even when continuously loading hundreds of images.
[Edit 2] In light of your additional information (the images are all small status icons), perhaps you should consider simply declaring a class for each status in your CSS. Also, you might want to look into using cloneNode and replaceNode for a very quick and efficient swap.
[Edit 3] Try absolutely-positioning your image elements. It will limit the amount of reflow that needs to happen, since absolutely-positioned elements are outside of the flow.
When you change a bunch of elements at once, you're usually blocking the UI thread anyway, so only one redraw after the JavaScript completes is happening, meaning the per-image redraw really isn't a factor.
I wouldn't double check anything here, let the browser take care of it, the new ones are smart enough to do this in an efficient way (and it's never really been that much of a problem anyway).
The case you'll see here is new images loading and re-flowing the page as they load, that's what's expensive here, existing images are very minor compared to this cost.
I recommend using CSS Sprite technique. More info at: http://www.alistapart.com/articles/
You can use an image that contains all the icons. Then instead of changing the src attribute, you update the background property.

When does reflow happen in a DOM environment?

What kinds of activities will trigger reflow of web page with DOM?
It seems there are different points of view. According to http://www.nczonline.net/blog/2009/02/03/speed-up-your-javascript-part-4/, it happens
When you add or remove a DOM node.
When you apply a style dynamically (such as element.style.width="10px").
When you retrieve a measurement that must be calculated, such as accessing offsetWidth, clientHeight, or any computed CSS value (via getComputedStyle() in DOM-compliant browsers or currentStyle in IE).
However, according to http://dev.opera.com/articles/view/efficient-javascript/?page=3, taking measurement triggers reflow only when there is already reflow action queued.
Does anybody have any more ideas?
Both articles are correct.
One can safely assume that whenever you're doing something that could reasonably require the dimensions of elements in the DOM be calculated that you will trigger reflow.
In addition, as far as I can tell, both articles say the same thing.
The first article says reflow happens when:
When you retrieve a measurement that must be calculated, such as accessing offsetWidth, clientHeight, or any computed CSS value (via getComputedStyle() in DOM-compliant browsers or currentStyle in IE), while DOM changes are queued up to be made.
The second article states:
As stated earlier, the browser may cache several changes for you, and reflow only once when those changes have all been made. However, note that taking measurements of the element will force it to reflow, so that the measurements will be correct. The changes may or may not not be visibly repainted, but the reflow itself still has to happen behind the scenes.
This effect is created when measurements are taken using properties like offsetWidth, or using methods like getComputedStyle. Even if the numbers are not used, simply using either of these while the browser is still caching changes, will be enough to trigger the hidden reflow. If these measurements are taken repeatedly, you should consider taking them just once, and storing the result, which can then be used later.
I take this to mean the same thing they said earlier. Opera will try its hardest to cache values and avoid reflow for you, but you shouldn't rely on its ability to do so.
For all intents and purposes just believe what they both say when they say that all three types of interactions can cause reflow.
Cheers.
Look at the "Rendering triggered by Property Read Access" section of Understanding Internet Explorer Rendering Behaviour, where the following code in IE will cause rendering activity.
function askforHeight () {
$("#lower").height();
}
document.body.style.display = 'none';
document.body.style.display = 'block';
This often solves those incomprehensible layout bugs.

Categories

Resources