모달 대화상자 아래의 페이지가 스크롤되지 않도록 방지

<dialog> 요소를 showModal()를 사용하여 모달로 열면 브라우저가 이를 최상위 레이어로 승격하고 나머지 문서를 비활성으로 표시합니다. 이렇게 하면 기본 페이지에서 포인터 클릭과 키보드 포커스가 차단되지만 스크롤은 차단되지 않습니다.

  1. 포인터 또는 터치 동작이 ::backdrop 위에 있는 동안 스크롤하면 아래 페이지가 스크롤됩니다.
  2. 대화상자가 스크롤 경계에 도달하면 <dialog> 내에서 스크롤이 기본 페이지로 연결됩니다. 대화상자의 콘텐츠가 오버플로되지 않는 경우에는 즉시 연결됩니다.

CSS 오버스크롤 동작 사양에 따라 overscroll-behavior는 스크롤할 수 없는 스크롤 컨테이너(overflow: hidden가 있는 요소 또는 현재 콘텐츠가 오버플로되지 않는 overflow: auto)를 포함한 모든 스크롤 컨테이너 요소에 적용됩니다. <dialog>의 overscroll-behavior: contain를 overflow: hidden 및 dialog::backdrop의 overscroll-behavior: contain와 결합하면 순수 CSS를 사용하여 기본 페이지가 스크롤되지 않도록 할 수 있습니다.

구현

1. <dialog>에 스크롤 체이닝 포함

사용자 에이전트 스타일시트는 <dialog>에 overflow: auto를 설정하여 기본적으로 스크롤 컨테이너로 만듭니다. dialog:modal(또는 dialog)에 overscroll-behavior: contain(또는 none)를 적용합니다.

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를 표시합니다.
  • <html> 또는 <body>에 overflow: hidden 또는 position: fixed를 적용하는 기존 해결 방법보다는 점진적 개선을 사용하는 것이 좋습니다. 루트 스크롤바를 삭제하거나, 가로 레이아웃 변경을 일으키거나, 페이지 스크롤 위치를 이동하거나, 기본 페이지의 position: sticky 요소를 손상시키지 않기 때문입니다.