Skip to Content
Course content

135: Understanding Reflow and Repaint

Click on the "Edit" button in the top corner of the screen to edit your slide content.

Imagine you're reorganizing your home office. If you decide to move your heavy oak desk from one wall to the other, you can't just move the desk. You have to shift your chair, move the lamp, rewire your cables, and maybe even move a bookshelf to make it fit. Everything in the room is affected by that one big move. That's a lot of effort.

Now, imagine you just decide to paint your walls a different shade of blue. You don't move the desk, the chair stays put, and the cables don't budge. You're just changing the surface appearance. It's much faster and less disruptive than moving the furniture.

In the browser's eyes, moving the desk is a Reflow, and painting the walls is a Repaint. Here is how that maps to your code:

  • The Furniture Move (Reflow): When you change something that affects the geometry of the page—like width, height, margin, or adding a new element to the DOM—the browser has to recalculate the position and size of that element and every other element it affects. This is computationally expensive.
  • The Paint Job (Repaint): When you change something that is purely visual—like color, background-color, or visibility: hidden—the browser doesn't need to move anything. It just re-draws the pixels on the screen. This is much "cheaper" for the CPU.

The Heavy Lifting of Layout Shifts

I've seen a lot of developers accidentally kill their app's performance by triggering constant reflows. The trick is remembering that reflows often "bubble." If you change the width of a parent <div>, the browser doesn't just update that div; it has to check if the children need to wrap, if the sidebar needs to shift, and if the footer needs to be pushed further down the page.

// This triggers a Reflow
const box = document.querySelector('.profile-card');
box.style.width = '400px'; 
box.style.marginLeft = '20px';
// Every element around .profile-card might now need to move.

Quick Fixes and Visual Updates

Repaints are far more forgiving. If you're building a UI that needs to feel snappy—like a button that glows when you hover over it—stick to properties that don't trigger a layout change. I usually lean heavily on opacity or color for these types of interactions.

// This triggers a Repaint (no Reflow)
const heartIcon = document.querySelector('.like-button');
heartIcon.style.color = 'red';
heartIcon.style.backgroundColor = '#ffebee';
// The layout remains identical; the browser just swaps the colors.

The Performance Trap: Layout Thrashing

Here is where things get tricky. There is a phenomenon called "Layout Thrashing." This happens when you write to the DOM and then immediately read a layout property. To give you the correct value for the "read," the browser is forced to perform a reflow right that second, even if it was planning to batch all the changes together for later.

Look at this mistake:

// BAD: Layout Thrashing
for (let i = 0; i < paragraphs.length; i++) {
    // READ (forces reflow to get current height)
    const height = paragraphs[i].offsetHeight; 
    // WRITE (invalidates layout)
    paragraphs[i].style.height = (height + 10) + 'px'; 
}


In the loop above, the browser is forced to reflow the page for every single paragraph. I've seen this turn a 10ms operation into a 200ms operation on older devices. The fix? Read all your values first, then perform all your writes. Batching is your best friend here.




📋 Practical Task

Optimizing a Dynamic Dashboard Widget

You have a dashboard widget that updates the height of a content area and changes its background color based on user data. Currently, the code is causing layout thrashing and unnecessary reflows.

Your Goal: Rewrite the updateWidget function to minimize reflows. You must separate the "read" operations from the "write" operations to prevent the browser from recalculating the layout multiple times in a single loop.


<div id="widget-container">
    <div class="item">Metric A</div>
    <div class="item">Metric B</div>
    <div class="item">Metric C</div>
</div>

<script>
function updateWidget() {
    const items = document.querySelectorAll('.item');
    
    // CURRENT INEFFICIENT CODE:
    items.forEach(item => {
        // This read forces a reflow because the previous iteration's write invalidated the layout
        const currentHeight = item.offsetHeight; 
        item.style.height = (currentHeight + 20) + 'px';
        item.style.backgroundColor = 'lightblue';
    });
}

// Call this function to test your optimization
updateWidget();
</script>

Requirements: 1. Create an array or object to store the measured heights first. 2. Loop through the items a second time to apply the style changes. 3. Ensure the backgroundColor change remains as a repaint-only operation.

Rating
0 0

There are no comments for now.

to be the first to leave a comment.