Preventing 'layout thrashing'
wilsonpage.co.uk
wilsonpage.co.uk
Does the browser just pay attention to whether each line of JS updates the DOM and queue up its updates until it encounters one that doesn't? Doesn't fit my model for how the JS engine fits into the browser. I guess I don't really know, but I always assumed it just reflowed on a fixed timeout.
Edit: Nevermind, I get it: it's that the intervening statement reads from the DOM, thus triggering a flush. I just missed that in the article.
We had a rule: If you think you need a callLater(), you don't need to use callLater(). If you still need a callLater(), you need to get someone to come and look at your code now to tell you that you don't need to use callLater(). If you both agree that you need to use a callLater(), you've still got to justify it at code review time.
The biggest difference I can see at the moment is that Flex doesn't recompute layout until the end of the frame, even if you do read from it. JS does recompute, so you need to defer for performance rather than (as in Flex) correctness. In either environment, the sane thing to do is to avoid having to defer your calls at all. It may be more work now, but your sanity will thank you later.
As an example of how bad things can get, Adobe's charting components would take more than 13 frames to settle rendering, because of all the deferred processing. This is a good example of how deferring your calls can actually cost you quite a lot of performance.
Moreover, once this animation problem will be be solved and kids will be able to do it in a snap, it will not be cool anymore and we'll go back to static, just as flat ui came as soon as shades were done easy.
[0] Good explanation of how this process works on android: http://www.youtube.com/watch?v=Q8m9sHdyXnE
Anyone want to offer the net's very first ever explicit definition of this term?
Modern browsers use a dirty-bit system for DOM modifications: they don't perform a layout every time it's changed, they just note the change and then perform the layout when the script returns. However, if the DOM is queried in the meantime, they have to perform layout, because otherwise they won't have up-to-date dimensions and positions. So if you interleave queries with modifications, the dirty bit gets set, the DOM gets laid out, the dirty bit gets set, the DOM gets laid out, etc, defeating all the optimizations browser vendors have put in place.
Layout is an expensive operation, too - on a moderately complex website like Google Search it takes about 17ms on desktop, and that can increase by 10x on mobile devices. 170ms is well past the point of visual perception.
Oh how times have changed.
http://gent.ilcore.com/2011/03/how-not-to-trigger-layout-in-...
This library gets around it by assuming you have no data dependencies between your modifications and subsequent queries of the DOM. That's a dangerous assumption to make in a large JS app. Oftentimes you want a data dependency, eg. you're introducing new content into an element and want to measure how high it'll be so you can add a transition. (BTW, a common hack to measure elements without actually rendering them is to stick them in a hidden iframe.)
I think the right solution is for application developers to carefully consider layout and architect their apps appropriately. Usually you need a separate "measure" pass and "modify" pass. In the former, read out all the DOM properties you need and attach them to the element (if you're using JQuery, $.data works great here, otherwise you can use data attributes). In the latter, read out the properties you measured earlier, perform any computation, and then set the final state of the DOM and kick off any transitions needed to get there. You may need to interleave multiple measure and modify phases, but at least then you know which phases will trigger a layout.
We have been recently doing HTML5 app for iPad and noticed you really have to be careful with layouts and recalculate styles to get a smooth performance. We also built a rudimentary tool to run automatic layout performance tests on a real device, because the layout problems easily creep in if you don't constantly keep eye on them.
I personally would opt for explicitly separating the reads from the writes, because
a) the flow of the program gets extremely complicated this way (it's hard to read the order in which instructions are executed)
b) it would avoid race conditions (or worst case make them a lot more obvious.)