웹사이트를 이용하다 보면 가끔 모달 창을 띄었을 때 뒷배경이 스크롤되는 경우가 있습니다. 한번 같이 보시죠. 난이도: ★☆☆☆☆ (개발 관련 게시글 난이도 정리 참고) 1. 현상See the Pen scroll_block-1 by chansoo (@chansoo1280) on CodePen. (맨 위에 있는 '모달 창 열기'버튼을 누르시면 모달 창이 열립니다.) 문제는 오른쪽의 스크롤입니다. 콘텐츠의 크기 때문에 자동으로 body에 스크롤이 생겼습니다. 모달 창을 열었을 때 모달 창의 콘텐츠만 작동하고 body스크롤은 멈추게 하고 싶습니다. 2. 여러 가지 방법1. body에 overflow-y주기가장 먼저 떠올린 방법입니다. overflow가 스크롤을 만드니까 그냥 hidden으로 설정해서 스크롤이 안 생기도록 하는 방법이죠. 한번 적용해보겠습니다. See the Pen scroll_block-2 by chansoo (@chansoo1280) on CodePen. 보시면 스크롤이 사라졌군요! 높이도 유지돼서 정말 좋습니다. 하지만 오른쪽의 스크롤만큼 밀려있던 콘텐츠가 원래대로 돌아오면서 약간 왔다 갔다 합니다.(0.5배로 봐보세요!) 이 이슈는 overflow-y:overlay;를 통해 기존 스크롤바의 영역을 없애서 해결 가능하네요. 또는 body에 `padding:16px;`을 줘서 스크롤바의 영역을 유지할 수도 있습니다. 2. 전체를 div로 한번 감싸기겉을 div로 감싸고 그 wrap에 `overflow:auto;`를 주었습니다. 이러면 모달을 fixed로 띄었을 때 wrap을 가려서 스크롤을 자연스럽게 막을 수 있습니다. See the Pen scroll_block-3 by chansoo (@chansoo1280) on CodePen. 잘 막혔나요? 3. 마무리스크롤 막는 방법은 이것 이외에도 다양하게 있을 수 있습니다. (좋은 아이디어가 있다면 댓글로!) 일단 저는 div가 하나 늘어나더라도 2번 방법이 최선인 것 같습니다. 무엇보다 스크립트가 간단한 점이 마음에 듭니다. 이상으로 마치겠습니다. 궁금한 점이나 피드백은 언제나 환영합니다! 감사합니다. 모달 컴포넌트 스크롤을 막으면서 발생한 문제에 대한 기록입니다. 첫 번째 문제e.preventDefault()와 e.stopPropagation()을 통해 이벤트를 막는 방법으로 스크롤을 막아야겠다는 생각을 했습니다. 하지만 어림도 없이 스크롤은 잘 되었습니다. 고민을 하다 실행 시점에서의 문제가 있다는 것을 깨닫게 되었습니다. mdn한글 문서를 보게 된다면 scroll은 view나 element가 스크롤될 때 scroll이벤트가 발생한다라고 되어있습니다. 정말 모호한 말이 아닐 수가 없습니다. 영어로 된 mdn을 보게 된다면 scroll event라고 명시가 되었는데, The scrollevent fires when an element has been scrolled. 즉, 요소가 스크롤된 다음 이벤트가 발생한다.라고 명시되어 있습니다. 때문에 이미 스크롤이 일어난 곳에서 이벤트를 막아봤자 동작하지 않았던 것입니다. 따라서 계속 scroll을 대체할 만한 이벤트를 찾던 와중 wheel과 mousewheel을 발견하게 됩니다. mousewheel의 경우 비표준이며 지원하지 않는 브라우저도 있으며, deprecated 되었다고 합니다. 이에 따라 대체된 것이 wheel이벤트입니다. wheel이벤트를 본다면 Thewheelevent fires when the user rotates a wheel button on a pointing device (typically a mouse). 즉, 사용자가 휠 버튼을 돌릴 때 발생한다고 합니다. 따라서 wheel이벤트에 이벤트 전파를 방지하고, 기본 동작을 막는다면, 잘 동작하게 됩니다. 두 번째 문제하지만 이렇게 하다 보니 휠 이벤트 자체를 막게 되었고, Modal컴포넌트에서 overflow가 발생했을 때도 scroll이 되지 않았습니다. 따라서 결국 body에 overflow:hidden속성을 주는 것으로 해결을 했습니다. 또한 touch-action:none을 한다면 모바일에서도 막을 수 있습니다. 세 번째 문제하지만 모바일의 특징상 끝까지 아래로 내린다면 url창이 보이며 위로 올리면 hidden이 되었던 요소들이 보이게 됩니다. 저는 모달의 Dim에 height:100vh와 background색을 변경하였는데, 끝까지 overflow 된 모달을 위로 스크롤한다면 화면에서 숨겨져 있던 여백이 흰색으로 보이게 됩니다. 따라서 모달의 height를 100%로 수정하여 추가적인 여백이 위로 올라오더라도 background색이 변하도록 했습니다. 결론overflow는 reflow를 일으키기 때문에 이벤트로 막으려고 했지만... 복잡도를 생각했을 때 이것이 최선의 방법인것 같습니다. overflow로 인해 성능적 이슈가 발생하면 다른 방법을 다시 생각해보아야할것 같습니다. 크롬 / Safari web/mobile에서 잘 동작하는 것을 확인할 수 있습니다.
별도로 빼두어 훅으로 사용할 수도 있습니다. 참고 https://developer.mozilla.org/en-US/docs/Web/API/Element/wheel_event https://developer.mozilla.org/en-US/docs/Web/API/Element/scroll_event https://developer.mozilla.org/en-US/docs/Web/API/Element/mousewheel_event
스크롤 방지 모달 스크롤 방지 스크롤 안되게 리액트 모달 스크롤 안되게 import React, { useEffect } from 'react'; export const Modal = (props) => { // 모달 오버레이에서 스크롤 방지 useEffect(() => { document.body.style.cssText = ` position: fixed; top: -${window.scrollY}px; overflow-y: scroll; width: 100%;`; return () => { const scrollY = document.body.style.top; document.body.style.cssText = ''; window.scrollTo(0, parseInt(scrollY || '0', 10) * -1); }; }, []); return ( <> {isDisplay && ( `<Container> <ModalOverlay /> <ModalWindow /> </Container>` )} </> ); }; 참고
|