Charis Theodoulou
Sep 5 · 8 min read

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.

For the rest of this article, I will assume you are comfortable with front-end development components such as CSS, the Browser DOM, and accessing HTML elements using JavaScript.

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 (offsetHeight or getComputedStyle)
  • Changing a CSS style
  • Setting a property of the style attribute
  • Changing the class of an element
  • Adding/removing a style sheet
  • Resizing the window
  • Scrolling
  • Changing the font
  • Activation of CSS-pseudo classes, such as :hover

For a more detailed list, check out this list of CSS properties that trigger reflow, painting, and composition and this list of JavaScript codes that trigger a reflow.

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

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

If you are changing a DOM element multiple times somewhere in your JavaScript code, do so after you have removed it from the DOM.

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.

Measure once

Check your code for repeated width calculations of elements such as element.offsetWidth or $('element').width(). Store that value in a variable, and use that instead.

Unoptimised code:

Code is unoptimised, as it is calculating an element’s height in a loop

Optimised code:

Code is optimised as it is storing the value of the element’s height and using that instead in the loop. Also removing the child element for batch editing before adding it to the DOM again.

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 display: block.

The key to this is that when setting visibility to 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

If you are changing an element’s inline styles and prefer adding them dynamically from JavaScript instead of toggling a CSS class, you may be causing more reflows that you may ideally want.

Unoptimised code:

Optimised code:

Instead, you can take advantage of this nifty HTML element style property, 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 top property).

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.


Measuring Performance

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.

The Chrome Dev Tools Performance tab
The Chrome Dev Tools Performance tab
Performance tab in Chrome DevTools

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:

The Chrome Dev Tools Performance tab after a measurement
The Chrome Dev Tools Performance tab after a measurement

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.


Conclusion

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.


Better Programming

Advice for programmers.

Thanks to Zack Shapiro

Charis Theodoulou

Written by

Web & Mobile App Developer 👨‍💻 | Love playing football ⚽| Cars & racing 🏎️ | charistheo.io 🌐

Better Programming

Advice for programmers.

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade