1. 들어가기 전에…
- 모달창(
modal
)은 사용자가 특정 작업을 수행하도록 유도하는 팝업창이다. - 모달은 창 이외의 영역을 흐리게 만들어 사용자가 창을 닫거나 작업을 완료하기 전까지 다른 부분을 조작할 수 없도록 만든다. 아래 gif는 부트스트랩의 모달 예시이다.
https://getbootstrap.kr/docs/5.0/components/modal/#모달-간-토글
앞서 언급했듯이, 모달은 사용자가 특정 작업을 수행하도록 유도하는 역할을 한다.
그런데 만약 모달이 열릴 때 창 이외 영역에서 스크롤이 가능하면 어떨까?
아래 gif는 모달이 열려있을 때, 창 이외 영역에서 스크롤이 발생하는 예시이다.
예시를 보면, 모달 이외의 영역으로 주의가 분산되기에 사용자 경험이 저하될 수 있다.
- 그럼 어떻게 해야, 모달 이외의 영역에서 스크롤이 발생하는 걸 막을 수 있을까? 단순히 body 영역에
overflow: hidden
을 적용하면 될까?
2. 외부 스크롤을 막는 방법
- 외부 스크롤을 막는 가장 간단한 방법은,
body
에overflow: hidden
속성을 적용하는 것이다. 하지만 이것만으로 충분할까? 아니다. - 만약 스크롤바가 공간을 차지하는 경우 모달이 열릴 때마다
body
의 너비가 변경된다. - 이 경우, 모달 이외의 영역으로 사용자의 시선이 옮겨지거나,
body
의 레이아웃이 깨질 수 있다! - 아래 gif는 모달 등장시 컨텐츠 박스의 배열이 변경되는 예시이다.
- 모달이 뜨기 전에는 콘텐츠 레이아웃이 3x3이었으나, 모달 등장시 콘텐츠 레이아웃이 3x4로 변한다.
- 이처럼 모달이 등장할 때,
overflow: hidden
만 적용하면 레이아웃이 변경되는 문제가 발생한다.
- 그럼 어떻게 해야 레이아웃 깨짐 없이 스크롤을 막을 수 있을까? JS로 추가 처리하면 가능하다!
1) 결과 미리보기
💡 해당 포스팅을 다 읽고나면, 아래 gif처럼 스크롤 막기는 물론 body 영역의 너비가 변경되는 문제를 해결할 수 있다.
2) JS 코드 살펴보기
(1) 전체 코드 보기
- 아래 코드는 레이아웃 깨짐을 방지하면서 스크롤을 막는 로직이다.
- 레이아웃 깨짐을 방지하는 방법은 간단한데, 바로
overflow: hidden
이 될 때body
의paddingRight
에 스크롤바 너비를 추가하는 것이다. - 방법도 알았으니, 이제 각 함수 별로 어떤 역할을 하는지 살펴보자 🙂
function getBodyScrollbarWidth() {
return window.innerWidth - document.documentElement.offsetWidth;
}
function blockBodyScroll(className = 'overflow-hidden') {
const isBlocked = document.body.classList.contains(className);
if (isBlocked) return;
document.body.style.setProperty('--scrollbar-width', `${getBodyScrollbarWidth()}px`);
document.body.classList.add(className);
}
function unblockBodyScroll(className = 'overflow-hidden') {
const isBlocked = document.body.classList.contains(className);
if (!isBlocked) return;
document.body.style.removeProperty('--scrollbar-width');
document.body.classList.remove(className);
}
(2) getBodyScrollbarWidth(), 스크롤바 너비 계산하기
- 이 함수는 브라우저의 스크롤바 너비를 계산한다.
window.innerWidth
는 창의 전체 너비를,document.documentElement.offsetWidth
는 스크롤바를 제외한 너비를 나타낸다.
- 이 둘의 차이를 이용하면 스크롤바 너비를 계산할 수 있다.
function getBodyScrollbarWidth() {
return window.innerWidth - document.documentElement.offsetWidth;
}
(3) blockBodyScroll(), body의 스크롤 막기
- 이 함수는 body 영역의 스크롤을 막는 역할을 한다.
- 우선 스크롤 차단을 위해
overflow-hidden
클래스를 추가하고, - 스크롤바 너비를 계산하여 CSS 변수로 설정한다. 그리고 이미 차단된 경우에는 함수를 early return한다.
function blockBodyScroll(className = 'overflow-hidden') {
const isBlocked = document.body.classList.contains(className);
if (isBlocked) return;
document.body.style.setProperty('--scrollbar-width', getBodyScrollbarWidth() + 'px');
document.body.classList.add(className);
}
(4) unblockBodyScroll(), body의 스크롤 허용하기
- 이 함수는 body 영역의 스크롤을 다시 허용하는 역할을 한다.
- 스크롤 차단을 해제하기 위해
overflow-hidden
클래스를 제거하고, CSS 변수를 삭제한다. - 만약 이미 차단 해제된 경우에는 함수를 early return한다.
function unblockBodyScroll(className = 'overflow-hidden') {
const isBlocked = document.body.classList.contains(className);
if (!isBlocked) return;
document.body.style.removeProperty('--scrollbar-width');
document.body.classList.remove(className);
}
3) CSS 코드 살펴보기
- 모달이 열릴 때, body에
overflow-hidden
클래스가 추가된다. overflow-hidden
클래스에,overflow: hidden
과padding-right
를 적용하자.
body.overflow-hidden {
overflow: hidden; // 스크롤 막기
padding-right: var(--scrollbar-width); // 스크롤바 너비를 우측 여백에 추가
}
4) 결과보기
- 앞선 처리를 완료하면, 경우에 따라 body의 레이아웃을 유지하면서 스크롤을 막을 수 있다.
- 아래 playground의 [스크롤 막기], [스크롤 허용하기] 버튼을 클릭해 테스트해보자 🙂
See the Pen block body scroll by KumJungMin (@kumjungmin) on CodePen.
3. 마치며…
- 이번 시간에는 모달이 열릴 때 스크롤을 막으면서 레이아웃 깨짐을 방지하는 방법을 알아보았다.
- 사소한 동작처럼 보이지만, 해당 처리를 통해 긍정적인 사용자 경험은 물론 레이아웃이 깨지는 현상을 막을 수 있다.
- 대부분의 UI 라이브러리에서는 이를 기본적으로 제공하지만, 만약 자체 구현을 고려한다면 사용자 경험도 지켜주면 어떨까?
반응형
'개발 기술 > 사소하지만 놓치기 쉬운 개발 지식' 카테고리의 다른 글
[CSS] clip-path로 별점(Rating) UI 구현하는 법 (with. vue 컴포넌트 예시 포함) (13) | 2024.08.28 |
---|---|
[JS] RangeSlider를 구현하는 법(feat. 중첩 range input) (2) | 2024.07.14 |
[TS] 입력 유효성을 체크하는 여러 정규식(feat. 한글, 전화번호, 사업자등록번호, 이메일 등) (0) | 2024.05.13 |
[CSS] 확장자 제외하고, 파일명만 말줄임하는 방법(with. input file) (2) | 2024.05.05 |
콘텐츠 접근이 가능한 아코디언을 만들어보자! (with. until-found, details) (0) | 2024.02.13 |
댓글