Skip to content

Performance Improvement: Eliminate layout thrashing in popover positioning #124

@akilvishnum

Description

@akilvishnum

Hi @knadh,

Impacted component:
ot-dropdown

Description
The current implementation of the menu popover positioning in ot-dropdown causes layout thrashing (forced synchronous reflow). This occurs because the code interleaves DOM "read" operation (getBoundingClientRect) with DOM "write" operations (setting style.top) within the same execution frame.

Impact

  • Jank: Visible stuttering when opening large dropdowns or scrolling while a popover is active.
  • Main Thread Blocking: On low-end devices or complex pages, the browser is forced to recalculate the layout multiple times per frame, leading to dropped frames.

Technical Analysis
Bottleneck is located in this Anti-pattern position logic.

oat/src/js/dropdown.js

Lines 32 to 42 in 094ec48

this.#position = () => {
// Position has to be calculated and applied manually because
// popover positioning is like fixed, relative to the window.
const r = this.#trigger.getBoundingClientRect();
const m = this.#menu.getBoundingClientRect();
// Flip if menu overflows viewport.
this.#menu.style.top = `${r.bottom + m.height > window.innerHeight ? r.top - m.height : r.bottom}px`;
this.#menu.style.left = `${r.left + m.width > window.innerWidth ? r.right - m.width : r.left}px`;
};
}

Proposed Solution

  1. We should wrap the positioning logic in requestAnimationFrame and migrate from absolute positioning (top/left) to CSS transform (translate). This moves the workload to the compositor thread, leveraging GPU acceleration to avoid triggering expensive layout and paint cycles.
  2. Leverage the CSS Anchor Positioning API (position-anchor, position-area, and position-try-fallbacks) to handle popover placement. By moving positioning logic from the main thread to the browser's internal engine, we eliminate forced reflows and significantly reduce our JavaScript footprint

Browser Compatibility for CSS API
position-anchor
position-area
position-try-fallbacks

I prefer going with CSS APIs focusing on the goal to use minimal JS.
Does that align with the intended direction?

I have made the changes and tried out in my local. I will raise a PR for review.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions