From: https://developers.google.com/speed/articles/reflow
Author: Lindsey Simon, UX Developer
Recommended knowledge: Basic HTML, basic Javascript, working knowledge of CSS
Reflow is the name of the web browser process for re-calculating the positions and geometries of elements in the document, for the purpose of re-rendering part or all of the document. Because reflow is a user-blocking operation in the browser, it is useful for developers to understand how to improve reflow time and also to understand the effects of various document properties (DOM depth, CSS rule efficiency, different types of style changes) on reflow time. Sometimes reflowing a single element in the document may require reflowing its parent elements and also any elements which follow it.
There are a great variety of user actions and possible DHTML changes that can trigger a reflow. Resizing the browser window, using JavaScript methods involving computed styles, adding or removing elements from the DOM, and changing an element's classes are a few of the things that can trigger reflow. It's also worth noting that some operations may cause more reflow time than you might have imagined - consider the following diagram from Steve Souders' talk "Even Faster Web Sites":
From the table above it's clear that not all changes to the style in JavaScript cause a reflow in all browsers, and that the time it takes to reflow varies. It is also somewhat clear that modern browsers are getting better at reflow times.
At Google, we test the speed of our web pages and applications in a variety of ways - and reflow is a key factor we consider when adding features to our UIs. We strive to deliver lively, interactive and delightful user experiences.
Guidelines
Here are some easy guidelines to help you minimize reflow in your web pages:
- Reduce unnecessary DOM depth. Changes at one level in the DOM tree can cause changes at every level of the tree - all the way up to the root, and all the the way down into the children of the modified node. This leads to more time being spent performing reflow.
- Minimize CSS rules, and remove unused CSS rules.
- If you make complex rendering changes such as animations, do so out of the flow. Use position-absolute or position-fixed to accomplish this.
- Avoid unnecessary complex CSS selectors - descendant selectors in particular - which require more CPU power to do selector matching.
In this video, Lindsey explains some simple ways to minimize reflow on your pages:
Additional resources
- Mozilla's Notes on HTML reflow
- Opera's Repaints and reflows
- Satoshi Ueyama's debug-Firefox reflow demos:
- Nicole Sullivan on Reflows and Repaints
-------------------------------------------------------------------------------
From: http://www-archive.mozilla.org/newlayout/doc/reflow.html
Notes on HTML Reflow
Chris Waterson (waterson@netscape.com)
Overview
Reflow is the process by which the geometry of the layout engine's formatting objects are computed. The HTML formatting objects are called frames: a frame corresponds to the geometric information for (roughly) a single element in the content model; the frames are arranged into a hierarchy that parallels the containment hierarchy in the content model. A frame is rectangular, with width, height, and an offset from the parent frame that contains it.
More than one frame may be needed to represent a single element from the content model; for example, text that wraps is broken into several frames, one per wrapped line. In this case, the primary frame is the frame containing the first line of text, withcontinuing frames (or continuations) created for subsequent lines.
HTML uses a flow based layout model, meaning that most of the time it is possible to compute the geometry in a single pass. Elements later ``in the flow'' typically do not affect the geometry of elements that are earlier ``in the flow'', so layout can proceed left-to-right, top-to-bottom through the document. There are exceptions to this rule: most notably, HTML tables may require more than one pass.
The XUL box layout model, on the other hand, is constraint based, meaning that geometric preferences and constraints of neighboring elements are taken into consideration before the elements' final geometry can be computed. The box is the geometric primitive for the XUL layout model.
All HTML reflow, including the intial reflow, begins at the root frame, which corresponds to the <html>
element of the HTML document. Reflow proceeds recursively through some or all of the frame hierarchy, computing geometric information for each frame object that requires it. Reflow may have the side-effect of creating new continuation frames, for example, for a text frame when the text must be wrapped.
Some reflows are immediate in response to user or script actions; for example, resizing the window or changing the document's default font. These are dispatched directly from the presentation shell (e.g., nsIPresShell::StyleChangeReflow
), and affect the entire frame tree. Other reflows are incremental and are dealt with asynchronously; for example, when content streams in from the network. Incremental reflows are queued by the presentation shell for batched dispatch.
Reflow State
The reflow state object, nsHTMLReflowState
, is used to pass constraining information ``down'' from parent frames to child frames. For example, a <div>
with a constrained width (e.g., set via the CSS width
property) would note this in the reflow state object before flowing its children.
When reflow begins, the root reflow state is initialized with information about the top-level container for the document's presentation; e.g., the width and height of the application window. This is passed as an argument to the Reflow
method of the root frame in the frame hierarchy.
Each container frame constructs a new reflow state object (based on its container's reflow state object) in which the container will reflow its children. Most of the constraints in the new reflow state are computed when the state is created; for example, the available space in the new reflow state is computed by subtracting the container frame's border and padding from the parent reflow state's available space.
Reflow Reason
All reflows have a reason, which is maintained in the reflow state object (and may mutate, as described below). The reflow reason controls how a frame reacts during a reflow, and is one of the following:
Initial
, for the very first time that the frame hierarchy is flowed. In this case, a frame knows that there is no residual state that can be used to simplify geometry computation.Incremental
, when something in the frame tree changes; for example, when more content is read from the network, or some script manipulates the DOM. An incremental reflow is targeted at a single frame in the frame hierarchy. During an incremental reflow, a frame can assume that none of the constraints computed ``from above'' (for example, available width) have changed; instead, something ``within'' the frame has changed, which may have bottom-up impact to the frame hierarchy.Resize
, when the containing boundary for the frame hierarchy changes. During a resize reflow, the frame can assume that none of the frame's internal state (e.g., a text frame's text) has changed; instead, a top-down change in the layout constraints has occured.StyleChange
, when the entire frame hierarchy must be traversed to recover from stylistic change; for example, a change in the default font size.Dirty
, when a container frame has consolidated several individualIncremental
reflows that have been targeted at its child frames.
Initial, incremental, resize, and style change reflows may each be performed as an immediate ``global'' reflow from the presentation shell:
- An initial reflow is performed when the presentation shell is initialized to flow the shell's initial frame hierarchy.
- Incremental reflows are dispatched en masse when the presentation shell's incremental reflow queue is asynchronously serviced.
- A resize reflow is performed when the presentation shell's dimensions change; e.g., because the user resized the shell's window.
- A style change reflow is performed when the presentation shell's global stylistic information is changed; e.g., addition or removal of a style sheet, a change to the shell's default font.
A dirty reflow is never performed directly from the presentation shell. Instead, a dirty reflow is detected when an incremental reflow reaches its target frame, described below.
Reflow Metrics
The reflow metrics object, nsHTMLReflowMetrics
, is used to propagate information from child frames back to the parent. For example, the dimensions of each child frame of an unconstrained <div>
would be passed back to the <div>
's frame via the nsHTMLReflowMetrics
object.
Incremental Reflow
Although all of the reflow in Gecko attempts to re-use as much existing state as possible (and is in therefore some sense ``incremental'') an Incremental
reflow corresponds to a reflow that is specifically targeted at an individual frame in the frame hierarchy. A frame requests a Incremental
reflow (or one is requested on a frame's behalf) when something about the frame itself has changed.
Scheduling. To request (or schedule) an incremental reflow (e.g., in response to a change in the content model), a reflow commandobject is created and passed to the presentation shell via the nsIPresShell::AppendReflowCommand
method. The presentation shell does not process the command immediately. Instead, it queues the command, and processes it asynchronously along with other queued reflow commands en masse.
Coalescing. As described below, the reflow command has a type and a target frame. Multiple reflow commands with the same type and target frame are coalesced: the presentation shell simply refuses to add subsequent commands of the same type for the same frame to the queue. A caller may also cancel a reflow command that is in the queue; e.g., if the target frame is destroyed.
Dispatch. The presentation shell processes the reflow queue by removing a single reflow command from the queue and dispatching it to its target frame. (But cf. the reflow tree work, which will remove several commands from the queue at once.) A path is built from the target frame to the root frame and stored in the reflow command. A reflow state object is created with a reflow reason ofIncremental
, the reflow command is stored in the state, and the Reflow
method of the root frame is invoked.
Processing. The root frame notes the Incremental
reflow reason specified in the reflow state, and inspects the path contained within the reflow command object. Specifically, it extracts the next frame along the path from the reflow command object, creates its own reflow state, also with an Incremental
reason, and invokes the Reflow
method of the next frame.
The incremental reflow proceeds recursively through the frame hierarchy. Each frame along the incremental reflow path (as specified in the reflow command object) extracts the next frame and dispatches the reflow downward. In order to correctly dispatch the reflow to the child frame, the frame may need to perform some state recovery; for example, a block frame will traverse its line list to recover the space occupied by floated frames.
At some point, the incremental reflow reaches the target frame, at which point the reflow command's type becomes significant.
ContentChanged
indicates that the content corresponding to the target frame has changed somehow; for example, the text associated with a text frame has been modified. In reality, the only frame that responds to this sort of reflow is the block frame. The block frame treats this sort of change as a `full reflow' (i.e., as a resize). This makes me believe that we could probably eliminate this class altogether.StyleChanged
indicates that the stylistic information corresponding to the target frame has changed; for example, the font size has increased. This causes the frame to mutate the reflow state's reason toStyleChange
, which is propagated recursively to the entire subtree beneath the target frame.ReflowDirty
indicates that container frame has decided to coalesce several incremental reflows targeted at its children into a single reflow. The container frame maintains the necessary state to determine which children must be reflowed.UserDefined
, for ``special situations''. Currently, this is only used by the viewport frame to schedule a reflow to reflow all of the viewport's fixed-position frames. We should probably try to eliminate it.
An incremental reflow may damage other parts of the frame hierarchy; for example, changing the size of the font used in a specific paragraph may cause the paragraph to grow or shrink. A container must therefore propagate any damage that the incremental reflow of the child frame caused, possibly reflowing other children as well.
Dirty Reflows
If several incremental changes occur in the same part of the frame hierarchy, it is possible to have several Incremental
reflows targeted at nearby frames. In this case, it is likely that the individual Incremental
reflows will end up doing redundant work. For example, each keystroke typed into a text widget could generate a separate Incremental
reflow targeted at the text frame. Were we to process each individually, the text widget would be flowed once for each keystroke, which would be wasteful if the latency of an individual reflow exceeds the speed at which text is being typed. The purpose of the Dirty
reflow is to allow these individual reflows to be coalesced intelligently.
A frame that decides it needs a dirty reflow sets the NS_FRAME_IS_DIRTY
state bit on itself, and then calls the ReflowDirtyChild
method on its parent frame. In ReflowDirtyChild
, the parent frame sets the NS_FRAME_HAS_DIRTY_CHILD
state bit on itself, and does any bookkeeping necessary to remember which child is dirty (for example, the block frame marks the linebox dirty that contains the child frame). The parent frame can then either decide to schedule a ReflowDirty
Incremental
reflow targeted at itself, or to delegate that responsibility to itsparent. If it decides to delegate, then it sets the NS_FRAME_IS_DIRTY
state bit on itself and recursively calls ReflowDirtyChild
.
Eventually, the ReflowDirty
Incremental
reflow is dispatched, and arrives at the container frame that scheduled it. The target frame recovers its bookkeeping information (e.g., the block frame iterates through the dirty lineboxes), and reflows the dirty child frames.
Isn't information lost if a ReflowDirty
Incremental
reflow coalesces different kinds of incremental reflows (e.g., a ContentChanged
with aStyleChanged
)? No, because these kinds of reflows aren't coalesced; instead, they're directly enqueued to the presentation shell's reflow queue.
HTML and XUL Interaction
As mentioned above, HTML and XUL have fundamentally different layout models, the former being a flow-based model, and the latter being a constraint based model. These differences are mediated by two adapter classes: nsBoxFrame
and nsBoxToBlockAdaptor
.
nsBoxFrame
nsBoxFrame
is an HTML frame that ``wraps'' a XUL box. Its purpose is to convert HTML reflows their box analog.
nsBoxToBlockAdaptor
nsBoxToBlockAdaptor
is a XUL box that wraps an HTML block frame, used to convert changes in the box layout into HTML reflows.
-------------------------------------------------------------------------------
From: http://www.stubbornella.org/content/2009/03/27/reflows-repaints-css-performance-making-your-javascript-slow/
REFLOWS & REPAINTS: CSS PERFORMANCE MAKING YOUR JAVASCRIPT SLOW?
I’ve been tweeting and posting to delicious about reflows and repaints, but hadn’t mentioned either in a talk or blog post yet.
I first started thinking about reflows and repaints after a firey exchange with Mr. Glazman at ParisWeb. I may be stubborn, but I did actually listen to his arguments. :) Stoyan and I began discussing ways to quantify the problem.
Going forward the performance community needs to partner more with browser vendors in addition to our more typical black box experiments. Browser makers know what is costly or irrelevant in terms of performance. Opera lists repaint and reflow as one of the three main contributors to sluggish JavaScript, so it definitely seems worth a look.
Let’s start with a little background information. A repaint occurs when changes are made to an elements skin that changes visibility, but do not affect its layout. Examples of this include outline, visibility, or background color. According to Opera, repaint is expensive because the browser must verify the visibility of all other nodes in the DOM tree. A reflow is even more critical to performance because it involves changes that affect the layout of a portion of the page (or the whole page). Reflow of an element causes the subsequent reflow of all child and ancestor elements as well as any elements following it in the DOM.
For example:
<body> <div class=”error”> <h4>My Module</h4> <p><strong>Error:</strong>Description of the error…</p> <h5>Corrective action required:</h5> <ol> <li>Step one</li> <li>Step two</li> </ol> </div> </body>
In the html snippet above, a reflow on the paragraph would trigger a reflow of the strong because it is a child node. It would also cause a reflow of the ancestors (div.error and body – depending on the browser). In addition, the h5 and ol would be reflowed simply because they follow that element in the DOM. According to Opera, most reflows essentially cause the page to be re-rendered:
Reflows are very expensive in terms of performance, and is one of the main causes of slow DOM scripts, especially on devices with low processing power, such as phones. In many cases, they are equivalent to laying out the entire page again.
So, if they’re so awful for performance, what causes a reflow?
Unfortunately, lots of things. Among them some which are particularly relevant when writing CSS:
- Resizing the window
- Changing the font
- Adding or removing a stylesheet
- Content changes, such as a user typing text in
an input box - Activation of CSS pseudo classes such as :hover (in IE the activation of the pseudo class of a sibling)
- Manipulating the class attribute
- A script manipulating the DOM
- Calculating offsetWidth and offsetHeight
- Setting a property of the style attribute
Mozilla article about reflows that outlines causes and when they could be reduced.
How to avoid reflows or at least minimize their impact on performance?
Note: I’m limiting myself to discussing the CSS impact of reflows, if you are a JavaScripter I’d definitely recommend reading my reflow links, there is some really good stuff there that isn’t directly related to CSS.
- Change classes on the element you wish to style (as low in the dom tree as possible)
- Avoid setting multiple inline styles
- Apply animations to elements that are position fixed or absolute
- Trade smoothness for speed
- Avoid tables for layout
- Avoid JavaScript expressions in the CSS (IE only)
Change classes as low in the dom tree as possible
Reflows can be top-down or bottom-up as reflow information is passed to surrounding nodes. Reflows are unavoidable, but you can reduce their impact. Change classes as low in the dom tree as possibleand thus limit the scope of the reflow to as few nodes as possible. For example, you should avoid changing a class on wrapper elements to affect the display of child nodes. Object oriented css always attempts to attach classes to the object (DOM node or nodes) they affect, but in this case it has the added performance benefit of minimizing the impact of reflows.
Avoid setting multiple inline styles
We all know interacting with the DOM is slow. We try to group changes in an invisible DOM tree fragment and then cause only one reflow when the entire change is applied to the DOM. Similarly, setting styles via the style attribute cause reflows. Avoid setting multiple inline styles which would each cause a reflow, the styles should be combined in an external class which would cause only one reflow when the class attribute of the element is manipulated.
Apply animations with position fixed or absolute
Apply animations to elements that are position fixed or absolute. They don’t affect other elements layout, so they will only cause a repaint rather than a full reflow. This is much less costly.
Trade smoothness for speed
Opera also advises that we trade smoothness for speed. What they mean by this is that you may want to move an animation 1 pixel at a time, but if the animation and subsequent reflows use 100% of the CPU the animation will seem jumpy as the browser struggles to update the flow. Moving the animated element by 3 pixels at a time may seem slightly less smooth on very fast machines, but it won’t cause CPU thrashing on slower machines and mobile devices.
Avoid tables for layout (or set table-layout fixed)
Avoid tables for layout. As if you needed another reason to avoid them, tables often require multiple passes before the layout is completely established because they are one of the rare cases where elements can affect the display of other elements that came before them on the DOM. Imagine a cell at the end of the table with very wide content that causes the column to be completely resized. This is why tables are not rendered progressively in all browsers (thanks to Bill Scott for this tip) and yet another reason why they are a bad idea for layout. According to Mozilla, even minor changes will cause reflows of all other nodes in the table.
Jenny Donnelly, the owner of the YUI data table widget, recommends using a fixed layout for data tables to allow a more efficient layout algorithm. Any value for table-layout other than "auto" will trigger a fixed layout and allow the table to render row by row according to the CSS 2.1 specification. Quirksmode shows thatbrowser support for the table-layout property is good across all major browsers.
In this manner, the user agent can begin to lay out the table once the entire first row has been received. Cells in subsequent rows do not affect column widths. Any cell that has content that overflows uses the ‘overflow’ property to determine whether to clip the overflow content.
This algorithm may be inefficient since it requires the user agent to have access to all the content in the table before determining the final layout and may demand more than one pass.
Avoid JavaScript expressions in the CSS
This rule is an oldie but goodie. The main reason these expressions are so costly is because they are recalculated each time the document, or part of the document, reflows. As we have seen from all the many things that trigger a reflow, it can occur thousands and thousands of times per second. Beware!
Further study
The Yahoo! Exceptional Performance team ran an experiment to determine the optimal method to include an external stylesheet. We recommended putting a link tag in the head because, while it was one second slower (6.3 to 7.3 seconds) all the other methods blocked progressive rendering. While progressive rendering is non-negotiable (users hate staring at a blank screen), it does make me curious about the effects of rendering, repaints, reflows and resulting CPU thrashing on component download and overall response time. If we could reduce the number of reflows during loading could we maybe gain back a tenth of the lost time (100ms)? What if it was as much as half?
At SXSW I was trying to convince Steve that reflows are important by telling him about an experiment I’ve been meaning to run for a long time, but just haven’t had time. I do hope someone can pick up where I left off (hint! hint!). While loading the page I’d like to intentionally trigger reflows at various rates. This could perhaps be accomplished by toggling a class name on the body (experiment) versus the last child of the body with no descendants (control). By comparing the two, and increasing in the number of reflows per second, we could correlate reflows to response time. Measuring the impact of reflows on JS responsiveness will be harder because anything we do to trigger the reflows will likely impact the experiment.
In the end, quantifying the impact is only mildly interesting, because browser vendors are telling us it matters. Perhaps more interesting is to focus on what causes reflows and how to avoid them. That will require better tools, so I challenge browser vendors and the performance community to work together to make it a reality!
See it in action
Perhaps you are a visual person? These videos are a really cool visualization of the reflow process.
- http://www.youtube.com/watch?v=nJtBUHyNBxs
- http://www.youtube.com/watch?v=ZTnIxIA5KGw
- http://www.youtube.com/watch?v=dndeRnzkJDU
Reflow gone amok
In order to improve performance browser vendors may try to limit reflows from affecting adjacent nodes or combine several reflows into one larger change such as Mozilla’s dirty reflows. This can improve performance, but sometimes it can also cause display problems. You can use what we’ve learned about reflows and trigger them when necessary to correct related display problems.
For example, when toggling between tabs on our image optimization site, http://smush.it, the height of the content is variable from tab to tab. Occasionally the shadow gets left behind as it is several ancestor nodes above the content being toggled and its container may not be reflowed. This image is simulated because the bug is difficult to catch on camera as any attempts to shoot it cause the reflow that corrects it. If you find yourself with a similar bug, move the background images to DOM elements below the content being toggled.
Another example is dynamically adding items to an ordered list. As you increase from 9 to 10 items or 99 to 100 items the numbers in the list will no longer line up properly across all navigators. When the total number increases by an order of magnitude and the browser doesn’t reflow siblings, the alignment is broken. Quickly toggling the display of the entire list or adding a class, even if it has no associated styles, will cause a reflow and correct the alignment.
Tools
A few tools have made waves lately. Stoyan Stefanov and I have been looking for decent ways to measure reflows and repaints and there are a few tools which show promise (despite being very early alpha). Beware, some of these seriously destroyed my browser before I got them working correctly. In most cases you’ll need to have installed the latest nightly builds.
When Mozilla announced the MozAfterPaint Firefox API, the internets were abuzz.
- UPDATE: Lindsey Simon at Google wrote a bookmarklet that tests reflow times on any browser. Fantastic. (Note: all that shaking is normal!)
- John Resig wrote a bookmarklet to visualize paint events.
- Kyle Scholz created this tool to visualize paint events before onload.
- Alex at Yoono has created the XUL profiler.
Has anyone else seen any cool tools for evaluating reflows? Please send them my way!
And a couple other tools not directly dealing with reflows.
- IE Visual Roundtrip Analyzer (VRTA), because if Microsoft named it something fun we might actually want to use it. :)
- Performance Profiling Tools for WPF.
Ultimately, we need a cross browser tool to quantify and reduce reflows and repaints. I’m hoping that the performance community can partner with browser vendors to make this tool a reality. The browser vendors have been telling us for a while that this was where we needed to look next, the ball is in our court.
留言列表