モーダル ダイアログの下のページがスクロールされないようにする

showModal() を使用して <dialog> 要素をモーダルとして開くと、ブラウザはそれを最上位レイヤに昇格させ、ドキュメントの残りの部分を不活性としてマークします。これにより、基盤となるページでのポインタ クリックとキーボード フォーカスはブロックされますが、スクロールはブロックされません。

  1. ポインタまたはタップ操作が ::backdrop の上にあるときにスクロールすると、その下のページがスクロールされます。
  2. <dialog> 内のスクロールは、ダイアログがスクロール境界に達すると、基になるページに連鎖します。ダイアログのコンテンツがオーバーフローしない場合は、すぐに連鎖します。

CSS Overscroll Behavior の仕様に従い、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 は適用されません。
  • ダイアログが開いたときに背景のスクロールをロックするために、<html> または <body> に overflow: hidden または position: fixed を適用しないでください。ルート オーバーフローを変更すると、ページ スクロールバーが削除され(水平方向のレイアウト シフトが発生)、ページ スクロール位置がリセットまたはジャンプし、基盤となるページの 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 要素を破損したりしないためです。