In this article, I will be listing a few ways by which a DOM reflow/layout-thrashing event might occur, why it’s bad, and how to minimise its occurrences or mitigate its effects.
Now let’s dive in.
What is DOM Reflow/Layout Thrashing?
First things first. There are two names for the process I’m about to explain:
- DOM reflow in Firefox
- Layout or layout thrashing in Chrome/Safari/Opera/IE
Both of them refer to the same process, so why are there two names for the same thing? That’s just how the web-development world is I guess.
Anyway, to avoid confusion I will be using the term reflow for the rest of the article. Right! So what is this reflow thing?
Simply put, reflow is a user-blocking operation that computes the layout of the document. A reflow on an element is the calculation of its dimensions and position in the document.
When it happens
DOM reflow happens more often that you would expect. It happens when:
- Inserting/removing/updating an element in the DOM
- Modifying content on the page, e.g., the text in an input box
- Moving a DOM element
- Animating a DOM element
- Taking measurements of an element (
- Changing a CSS style
- Setting a property of the
- Changing the
classof an element
- Adding/removing a style sheet
- Resizing the window
- Changing the font
- Activation of CSS-pseudo classes, such as
The reflow avalanche effect
Be aware that a reflow triggered on an element will most probably trigger subsequent reflows on the elements around it — as it will change their position/layout as well.
Why it is bad
- Requires CPU power
- Increases first meaningful paint time
- Increases first contentful paint time
- Creates a bad user experience because it’s a blocking operation
I know. If you haven’t heard about this reflow thing before, you must be panicking right now. Almost every interaction with the DOM will trigger this blocking operation. You are not to blame if you are thinking,“Oh right, so I can’t use any CSS and I cannot manipulate the DOM, so how can I make myself useful?”
Don’t worry, my friend. There will always be reflow.
Our job as web developers is to optimise for speed by lowering the number of occurrences and mitigating their effects.
This is called engineering, my friend
Just like Formula 1 engineers are optimising for speed by measuring, calibrating, and repeating, we as web developers need to measure performance, identify the bottlenecks, make fixes, and repeat all over again.
Make sure every time you make changes, you measure again and compare the results before the new fix. What works on a certain website doesn’t necessarily work for another one.
Now let’s go into some ways by which we can write better code and trigger as few reflows as possible.
Ways to Minimise Reflow Events
So let’s look at some ways to optimise for speed. Hopefully after this section you’ll be feeling more like a super-human Formula 1 engineer.
Batch edit HTML elements
Batch edit the removed element, and add it back to the DOM.
Alternatively, you could hide the element, edit it, and then show it again.
Be aware that in both cases a reflow will be triggered twice, so make sure to use this technique where you would otherwise have more than enough reflows.
Edit elements as low in the tree as possible
Due to the reflow spiral effect, it’s recommended to trigger the reflow as low in the tree as possible to minimise subsequent reflows that might get triggered on children elements.
Say you want to toggle a class in order to apply a set of CSS styles, do so always on the element you want to change, not on the parent element. You may also gain some performance improvements if you used less wrapper elements throughout your HTML files.
Check your code for repeated width calculations of elements such as
$('element').width(). Store that value in a variable, and use that instead.
Note: You may run the risk of writing more code and it ending up being unreadable. So make sure to wrap the process in a function with a sensible name, and add a comment that explains the function is performance-optimised.
You may have noticed that the optimised code also removes the list before editing its children because instead it would cause a reflow every time some margin is added. The fastdom library may be helpful if you are dealing with a lot of cases like the above. The library will allow you to batch all your measurement or mutating processes.
Fixed/absolute position elements that change too often
If there are any elements in your site that change their layout too often, they might be affecting other element’s layout as well which will trigger a cascading reflow effect.
For example, when animating an element’s dimensions (width and height), it is best to use position that element with
position: fixed or
position: absolute. This way the animating element will not affect the elements around it while changing width and height, thus reducing the number of unwanted reflows.
Use flex box for layouts
As seen in this comparison by Google developers, there is a slight performance improvement (3.5ms vs. 14ms) when using flex box instead of floats. However, this comparison may not reveal much if you take into account that 1,300 elements were rendered — a massive number not often found on normal websites.
Even then, make sure to try out different layout techniques, and see which one is more performant in your case. Remember: measure, identify, fix, and repeat.
Anyway, what you should keep in mind is to set a fixed width and height whenever possible and to make sure that the layout doesn’t change if new elements are added to or removed from a container.
Change visibility instead of display
When possible, hide or show the element using
visibility: hidden and
visibility: visible instead of
display: none and
The key to this is that when setting
hidden, the element will still take up space within the DOM layout. This way, on contrary to altering the
display property, the element’s width and height will not change, so the browser doesn’t need to recalculate (reflow) the layout.
So in case it’s not necessary to remove the element from the layout, just use
visibility: hidden, and you will save yourself a couple of reflows.
Use cssText for more than one layout change
Instead, you can take advantage of this nifty HTML element
cssText, and add multiple styles once. In the above example, we are triggering reflow once instead of twice (as in one for the
left and one for the
Note: Make sure to check browser compatibility in MDN Web Docs.
Use textContent instead of innerText
When requesting the text of an HTML element, you can do it in two ways:
element.textContent, which returns all the text inside an element as hidden or visible, or you could get it by using
element.innerText, which requires a reflow while calculating the layout before returning the visible text.
What you should take from this is that
innerText has more performance implications than
textContent, and if you can avoid using it in your code, it will lower the number of reflows and increase performance in your site.
As I have mentioned earlier, measuring is an important step of the optimisation process. Identifying which elements/processes are causing reflows and repaints can be quite tricky. So measuring your site’s performance before and after any changes to the code can prove to be helpful for keeping track of what is working and what isn’t.
For that I would suggest using Chrome DevTools, which will help you visualise and track down almost every process that happens inside your browser. Head to DevTools inside of your Chrome browser, and then navigate to the “Performance” tab.
Click the “reload” button. This will refresh your site and measure its performance until the CPU is idle. You should end up with something like this:
Yeah, it looks like someone randomly threw a gazillion colourful boxes on the screen while shouting, “There’s your performance!”
Don’t worry, though. Once you get the hang of it, you will soon appreciate what a gorgeous tool this is. Try spending some time with it —this performance tab will give you so many insights about how your website behaves.
On the very top, we can see a timeline on which every event is documented with every type in a different color. Inside the main section below, we can see all the processes happening and what triggers each one of them.
The main section represents the main thread of your browser and lists all the processes that we are interested in when optimising for reflow. The processes being executed inside the main thread are, for example, script evaluation, HTML parsing, layout, and paints.
It would be out of scope to explain all of the bits inside the performance tab. It’s better to focus on the task at hand: the layout process. Look for the chunks coloured in purple, and try to find what is causing them by looking at the bottom of the Call Tree, found on the bottom tabs.
Try digging around, in the Bottom-Up or Call Tree tabs, and you should be able to extract some valuable information about which processes are taking too long, where they start from, and how they could be optimised.
DOM reflow or layout thrashing should always be kept at a minimum in order to increase performance and provide a fluid, nonblocking user experience. The techniques described in this article are more of a coding style and common practises rather than a to-do list. So make sure you keep these in mind while developing for the web. 👨💻
Hope you found this article useful, and you can make your cool website even cooler.