PAYATHON 2026

如何避免 Radix UI Dropdown Menu 打开时锁定页面滚动?

数据小许

在 DropdownMenu.Root 上设置 modal={false},可以关闭下拉菜单自身的模态滚动锁定,让背景页面保持可滚动。如果页面仍受到其他组件或样式的滚动限制,还需要继续排查。

为什么打开菜单会锁定滚动?

页面原本可以滚动,打开 Dropdown Menu 后 body 滚动被锁定,这与组件默认启用模态模式时的行为一致。

Radix UI 的 DropdownMenu.Root 默认使用 modal={true},如官方 API 文档所示。底层 Menu 实现在模态模式下启用外部滚动锁定,在非模态模式下关闭该限制,详见 Radix Menu 源码。

由于问题没有提供组件代码或依赖版本,无法确定项目中是否还有其他滚动锁定逻辑,可以先检查这个配置。

如何修改?

将 modal={false} 放在 DropdownMenu.Root 上。下面以独立包 @radix-ui/react-dropdown-menu 为例:

import * as DropdownMenu from '@radix-ui/react-dropdown-menu';

export default function ActionsMenu() {
  return (
    <DropdownMenu.Root modal={false}>
      <DropdownMenu.Trigger asChild>
        <button type="button">打开菜单</button>
      </DropdownMenu.Trigger>

      <DropdownMenu.Portal>
        <DropdownMenu.Content
          sideOffset={6}
          style={{
            minWidth: 160,
            padding: 8,
            backgroundColor: 'white',
            border: '1px solid #ddd',
            borderRadius: 6,
          }}
        >
          <DropdownMenu.Item>查看详情</DropdownMenu.Item>
          <DropdownMenu.Item>编辑</DropdownMenu.Item>
        </DropdownMenu.Content>
      </DropdownMenu.Portal>
    </DropdownMenu.Root>
  );
}

关键改动是:

<DropdownMenu.Root modal={false}>

modal 是 Root 的配置项,应传给根组件。如果 Dropdown Menu 经过封装,确认该属性已传给底层 DropdownMenu.Root。

设置后仍然无法滚动怎么办?

可以按下面的顺序排查:

  1. 确认属性已生效:检查封装组件是否遗漏了 modal,或在内部将它固定为 true。
  2. 检查其他模态组件。如果菜单位于 Dialog 等组件中,外层组件可能仍在锁定页面滚动。修改菜单的 modal 不会解除其他组件施加的限制。
  3. 在浏览器开发者工具中,对比菜单打开前后的 html、body 和实际滚动容器,检查是否出现 overflow: hidden、position: fixed 等限制滚动的样式。
  4. 如果项目监听了 wheel 或 touchmove,检查相关逻辑是否调用了 preventDefault(),从而阻止默认滚动。

验证时,使用内容高度超过视口的页面,并将指针放在菜单外的背景区域测试滚动,以区分背景页面滚动和菜单内部滚动。

使用非模态模式需要注意什么?

modal={false} 会影响 body CSS 之外的行为,包括焦点约束和菜单外部交互。相关实现显示,非模态模式也会关闭焦点陷阱和外部指针事件限制。修改后,应检查键盘导航、按 Escape 关闭菜单,以及点击菜单外部时的行为是否符合需求。

优先通过组件提供的 modal 配置解决问题。直接强制覆盖 body 的 overflow,可能无法解除事件层面的滚动拦截,也可能干扰其他弹层的正常行为。

备注:内容仅供参考。