javascriptroom blog

Why Two Vertical Scrollbars Appear After Hiding/Showing Body Overflow with JavaScript: Fix & Explanation

If you’ve ever built a modal, lightbox, or mobile menu, you’ve likely used JavaScript to toggle overflow: hidden on the <body> element. This common technique prevents background scrolling when a modal is open, improving user experience. However, a frustrating bug often arises: after hiding and then showing the body’s overflow, two vertical scrollbars suddenly appear.

This issue is more than just a visual annoyance—it can break layouts, confuse users, and undermine the polish of your website. In this blog, we’ll demystify why this happens, how to diagnose it, and provide actionable fixes to ensure smooth, scrollbar-free interactions.

2026-02

Table of Contents#

  1. Understanding the Problem: What’s Happening?
  2. Why Does This Happen? The Technical Breakdown
  3. How to Diagnose the Issue
  4. The Fixes: Solving Two Scrollbars for Good
  5. Prevention Tips: Avoid Future Scrollbar Conflicts
  6. Conclusion
  7. References

1. Understanding the Problem: What’s Happening?#

Let’s start with a common scenario:

You’re building a modal. When the modal opens, you run JavaScript to set document.body.style.overflow = 'hidden' to freeze the background. When the modal closes, you set it back to 'auto' (or 'visible') to re-enable scrolling. But after closing the modal, instead of one scrollbar, two vertical scrollbars appear: one on the viewport and another on the <body> itself.

Here’s a minimal example to reproduce the issue:

<!-- index.html -->
<body>
  <button id="modalToggle">Toggle Modal</button>
  <div class="modal">This is a modal!</div>
  <!-- Long content to force scrolling -->
  <div style="height: 2000px;">Long content here...</div>
</body>
 
<script>
  const modalToggle = document.getElementById('modalToggle');
  const modal = document.querySelector('.modal');
 
  modalToggle.addEventListener('click', () => {
    modal.classList.toggle('open');
    // Toggle body overflow
    const isOpen = modal.classList.contains('open');
    document.body.style.overflow = isOpen ? 'hidden' : 'auto';
  });
</script>

When you click "Toggle Modal" twice (open then close), you’ll likely see two scrollbars. Why? Let’s dive into the technical details.

2. Why Does This Happen? The Technical Breakdown#

2.1 The Root Cause: Conflicting Overflow on <html> and <body>#

The key insight: Browsers treat the <html> and <body> elements as separate containers when calculating overflow. Most developers assume the <body> controls scrolling, but this is misleading.

  • The <html> element (often called the "root element") is the parent of <body>.
  • By default, the viewport scrollbar is controlled by the <html> element, not <body>.

When you set body { overflow: hidden; }, you’re only hiding overflow for the <body> container. The <html> element’s overflow remains untouched (usually auto by default). If the <html>’s content (which includes the <body>) exceeds the viewport height, the <html> will still show a scrollbar.

Later, when you set body { overflow: auto; }, the <body> may now have content exceeding its own height (due to layout shifts or margin/padding), causing it to render its own scrollbar. Now you have two scrollbars: one from <html> (viewport) and one from <body>.

2.2 Browser Defaults and the Viewport Scrollbar#

Browsers have default styles that complicate things. For example:

  • Most browsers set html { overflow: auto; } and body { margin: 8px; } by default.
  • The <body>’s margin causes the <html> to expand, which can trigger a scrollbar on <html> even if the <body> itself doesn’t overflow.

When you hide <body> overflow, the <body>’s margin is still present, so the <html> may retain its scrollbar. When you re-enable <body> overflow, the <body> now has its own scrollbar (if its content overflows), leading to duplication.

2.3 Layout Shifts and Scrollbar Width#

Scrollbars take up physical space (typically 8–16px wide). When you hide <body> overflow, the scrollbar disappears, and the page content shifts right to fill the gap. When you re-enable overflow, the scrollbar reappears, shifting content left.

This shift can cause the <body>’s content to reflow. If the reflow makes the <body>’s content taller than its container, it triggers a second scrollbar.

3. How to Diagnose the Issue#

To confirm the problem, use your browser’s DevTools:

  1. Open DevTools (F12 or Ctrl+Shift+I).
  2. Go to the Elements tab and select the <html> element.
  3. Check the Computed styles for overflow (look for overflow-x and overflow-y).
  4. Repeat for the <body> element.

If both <html> and <body> have overflow: auto (or scroll), and their content exceeds their heights, you’ll see two scrollbars.

4. The Fixes: Solving Two Scrollbars for Good#

4.1 Fix 1: Control Overflow on Both <html> and <body>#

The simplest fix is to toggle overflow for both <html> and <body> when opening/closing the modal. This ensures their overflow states are synchronized, preventing conflicting scrollbars.

Update the JavaScript to target both elements:

// Toggle overflow for both <html> and <body>
const isOpen = modal.classList.contains('open');
document.documentElement.style.overflow = isOpen ? 'hidden' : 'auto'; // <html>
document.body.style.overflow = isOpen ? 'hidden' : 'auto'; // <body>

Why this works: By hiding overflow on both <html> and <body>, you eliminate the <html> scrollbar when the modal is open. When closing, both reset to auto, so only one scrollbar (from <html>) appears.

4.2 Fix 2: Use CSS Classes for Cleaner State Management#

Inline styles can get messy. Instead, define a CSS class to handle overflow and toggle it on both elements:

/* styles.css */
.overflow-hidden {
  overflow: hidden !important; /* !important ensures no override */
}
// Toggle the class on <html> and <body>
const isOpen = modal.classList.contains('open');
document.documentElement.classList.toggle('overflow-hidden', isOpen);
document.body.classList.toggle('overflow-hidden', isOpen);

This approach is more maintainable, especially for complex apps with multiple modals or scroll states.

4.3 Fix 3: Compensate for Scrollbar Width (Optional)#

To prevent layout shifts when toggling overflow, calculate the scrollbar width and add padding to the <body> to offset it. This ensures content doesn’t jump left/right:

// Calculate scrollbar width (run once)
const scrollbarWidth = window.innerWidth - document.documentElement.clientWidth;
 
// When hiding overflow:
document.body.style.paddingRight = `${scrollbarWidth}px`;
 
// When showing overflow:
document.body.style.paddingRight = '0';

Combine this with Fix 1 or 2 for a seamless experience.

5. Prevention Tips: Avoid Future Scrollbar Conflicts#

  • Always target both <html> and <body> when toggling overflow. Never modify just one.
  • Reset default margins with a CSS reset (e.g., body { margin: 0; }) to avoid unexpected <html> scrollbars.
  • Test across browsers: Behavior varies slightly (e.g., Safari vs. Chrome). Use tools like BrowserStack to verify.
  • Avoid overflow: visible on <body>: It can cause unexpected layout issues. Stick to auto or hidden.

6. Conclusion#

Two vertical scrollbars after toggling body overflow are a common but fixable issue. The root cause is conflicting overflow settings between the <html> and <body> elements, exacerbated by browser defaults and layout shifts. By synchronizing overflow states for both elements and using clean CSS classes, you can eliminate the problem entirely.

Remember: The viewport scrollbar is controlled by <html>, not <body>. Always toggle both to keep scrollbars in check!

7. References#