Fixing Mouse Wheel Scroll in Overlay Panels

Complete Guide: Fixing Mouse Wheel Scroll in Overlay Panels

Problem

When you have a scrollable panel (like a filter list, modal, or dropdown) overlaid on a Google Map, the mouse wheel often doesn’t work as expected:

  • You scroll inside the panel, but nothing happens
  • Or the map zooms instead of the panel scrolling
  • Or scroll works intermittently

Why This Happens

There are two separate systems fighting for wheel events:

  1. CSS Scroll Context - The browser’s native scrolling behavior
  2. JavaScript Event Listeners - Google Maps registers wheel listeners for zoom functionality

CSS properties alone can’t solve this because the map’s JavaScript listener captures the event before your CSS scroll can use it.

The Complete Solution

You need both CSS and JavaScript working together.


Step 1: CSS Foundation

Set up proper scroll containment on your scrollable container:

.scrollable-panel {
  // Core scrolling
  overflow-y: auto;
  overflow-x: hidden;
  max-height: 400px;  // Or calc() based on viewport
  
  // iOS smooth scrolling
  -webkit-overflow-scrolling: touch;
  
  // Prevent scroll chaining to parent elements
  overscroll-behavior: contain;
}

What Each Property Does

  • overflow-y: auto
    Enable vertical scrolling when content overflows

  • overflow-x: hidden
    Prevent horizontal scroll (cleaner UX)

  • max-height
    Define the scrollable boundary - required for overflow to work

  • -webkit-overflow-scrolling: touch
    Momentum scrolling on iOS Safari

  • overscroll-behavior: contain
    Prevents scroll from “chaining” to parent when you hit top/bottom

Important: Without max-height, the container will expand to fit content and never scroll.

Step 2: JavaScript Event Handler (The Key Fix)

CSS alone won’t work when a map is involved. You must stop the wheel event from propagating to the map’s listener:

  wheelListenerAttached = false

  componentDidMount() {
    this.attachWheelListener()
  }

  componentDidUpdate(prevProps) {
    // Re-attach when component becomes visible
    if (this.props.show && !prevProps.show) {
      this.attachWheelListener()
    }
  }

  componentWillUnmount() {
    this.detachWheelListener()
  }

  attachWheelListener = () => {
    const element = document.getElementById('my-scrollable-panel')
    if (element && !this.wheelListenerAttached) {
      element.addEventListener('wheel', this.handleWheel, { passive: false })
      this.wheelListenerAttached = true
    }
  }

  detachWheelListener = () => {
    const element = document.getElementById('my-scrollable-panel')
    if (element && this.wheelListenerAttached) {
      element.removeEventListener('wheel', this.handleWheel)
      this.wheelListenerAttached = false
    }
  }

  handleWheel = (e) => {
    e.stopPropagation()  // This is the magic line
  }
}

Why { passive: false }?

By default, browsers use passive event listeners for wheel events (for performance). Setting passive: false allows us to call stopPropagation(). Without this, the browser may ignore your event handler.

Step 3: Parent Container Setup

If your scrollable element is inside a positioned container (like an overlay), ensure the parent doesn’t interfere:

  position: absolute;
  max-height: calc(100vh - 80px);  // Leave room for header/margins
  // Do NOT add overflow-y here if child handles scrolling
  // Having both parent and child with overflow-y causes conflicts
}

.scrollable-content {
  overflow-y: auto;
  overflow-x: hidden;
  -webkit-overflow-scrolling: touch;
  overscroll-behavior: contain;
  max-height: calc(100vh - 120px);  // Slightly smaller than parent
}

Debugging Checklist

If scroll still doesn’t work:

  • Does the scrollbar appear?
    If no, check max-height and overflow-y
  • Does clicking and dragging the scrollbar work?
    If yes, it’s a wheel event issue (use JS fix)
  • Does the map zoom when you scroll over the panel?
    If yes, events are propagating (use JS fix)
  • Does nothing happen at all?
    Events are being swallowed - check for conflicting listeners

Check DevTools: Inspect the element’s computed styles for overflow properties

Browser Support

  • overflow-y: auto
    All browsers

  • -webkit-overflow-scrolling
    Safari/iOS (ignored elsewhere)

  • overscroll-behavior
    Chrome 63+, Firefox 59+, Safari 16+, Edge 18+

  • { passive: false }
    All modern browsers