防止模态对话框下方的页面滚动

当使用 showModal() 将 <dialog> 元素作为模态框打开时,浏览器会将其提升到顶层,并将文档的其余部分标记为惰性。虽然这会阻止指针点击和键盘焦点落在底层页面上,但不会阻止滚动:

  1. 当指针或轻触手势位于 ::backdrop 上时,滚动操作会滚动下方的页面。
  2. 在 <dialog> 内滚动时,一旦对话框到达滚动边界,滚动操作就会链接到下层页面;如果对话框的内容没有溢出,则会立即链接到下层页面。

根据 CSS 滚动回弹行为规范,overscroll-behavior 适用于所有滚动容器元素,包括不可滚动的滚动容器(具有 overflow: hidden 或 overflow: auto 且其内容当前未溢出的元素)。通过将 <dialog> 上的 overscroll-behavior: contain 与 dialog::backdrop 上的 overflow: hidden 和 overscroll-behavior: contain 结合使用,您可以使用纯 CSS 阻止底层页面滚动。

实现

1. 在 <dialog> 上包含滚动链

用户代理样式表在 <dialog> 上设置 overflow: auto,使其默认成为滚动容器。将 overscroll-behavior: contain(或 none)应用于 dialog:modal(或 dialog)。

2. 将 ::backdrop 转换为滚动容器并包含其滚动

默认情况下,dialog::backdrop 具有 overflow: visible,这意味着它不是滚动容器,并且会忽略 overscroll-behavior。您必须在 dialog::backdrop 上设置 overflow: hidden,使其成为不可滚动的滚动容器,并将其与 overscroll-behavior: contain 配对,以便捕获在背景上执行的滚动动作,而不会到达其下方的文档。

示例代码

dialog:modal {
  /* MANDATORY: Prevent scroll gestures inside the modal (even when its content
     does not overflow) from chaining to the underlying page. */
  overscroll-behavior: contain;
}

dialog::backdrop {
  /* MANDATORY: ::backdrop defaults to overflow: visible (not a scroll container).
     Set overflow: hidden so it becomes a non-scrollable scroll container that
     honors overscroll-behavior. */
  overflow: hidden;
  overscroll-behavior: contain;
}

最佳实践和限制

  • 强制性:在 <dialog> 元素本身(使用 dialog:modal 选择器)和 dialog::backdrop 上设置 overscroll-behavior: contain。如果没有此声明,在背景上或对话框内执行的滚动手势仍会链接到页面。
  • 强制性:在 dialog::backdrop 上设置 overflow: hidden(或 overflow: auto),使其成为滚动容器。如果没有此声明,::backdrop 默认为 overflow: visible,并且 overscroll-behavior 将不起作用。
  • 请勿在 dialog::backdrop 或 dialog 上使用 overflow: clip 或 overflow: visible。这两个值都不会建立滚动容器,因此 overscroll-behavior 不会应用。
  • 请勿在对话框打开时将 overflow: hidden 或 position: fixed 应用于 <html> 或 <body> 来锁定背景滚动。更改根溢出属性会移除页面滚动条(导致水平布局偏移),可能会重置或跳过页面滚动位置,并可能会破坏底层页面上的 position: sticky 元素。

后备策略

虽然所有主流浏览器都支持在内容溢出的滚动容器上使用 overscroll-behavior CSS 属性,但在不可滚动的滚动容器上遵循 overscroll-behavior 是一项较新的规范,无法进行功能检测。

请勿尝试使用 @supports (overscroll-behavior: contain) 或 CSS.supports('overscroll-behavior', 'contain') 检测对不可滚动容器的支持。所有现代浏览器都支持 overscroll-behavior: contain 属性语法并返回 true,即使它们仅在内容溢出时才强制执行该属性。

相反,应将不可滚动容器上的滚动包含视为渐进增强:

  • 在支持对不可滚动滚动容器使用 overscroll-behavior 的浏览器中,系统会完全阻止在 dialog::backdrop 上方和 <dialog> 内部(无论其内容是否溢出)滚动背景页面。
  • 在尚不支持对不可滚动滚动容器使用 overscroll-behavior 的浏览器中,只要对话框自身的内容溢出且可滚动,对 dialog 使用 overscroll-behavior: contain 仍可防止滚动链。与此同时,showModal() 仍会捕获键盘焦点,并将背景文档标记为 inert。
  • 建议您采用渐进增强,而不是采用将 overflow: hidden 或 position: fixed 应用于 <html> 或 <body> 的旧版解决方法,因为渐进增强不会移除根滚动条、导致水平布局偏移、跳过页面滚动位置或破坏底层页面上的 position: sticky 元素。