TIL 89 | dimmed 영역 스크롤 막기

meow·2020년 12월 14일
6

React

목록 보기
27/33

modal(popup) 에서 외부(body or parentElement) 스크롤 막기
Disable Scrolling with JavaScript

햄버거 버튼을 눌렀을때 사이드에서 네비게이션 모달이 나오는 페이지의 형태는 쉽게 찾아볼 수 있다. 이때 사용성을 위해서 모달창은 스크롤이 가능하지만(화면보다 네비게이션의 높이가 긴 경우), 그 외에 dimmed 영역은 스크롤이나 터치 이벤트가 불가능하게 하는 것이 좋다.

disableScroll

아래의 코드는 간단하게 스크롤 이벤트를 막을 수 있지만 현재의 위치를 기억하지 못하고 Y의 위치가 0으로 이동해버린다는 단점이 있다.

function noScroll() {
  window.scrollTo(0, 0);
}

// add listener to disable scroll
window.addEventListener('scroll', noScroll);

// Remove listener to re-enable scroll
window.removeEventListener('scroll', noScroll);

위의 코드를 보완하기 위해서는 현재의 scrollY 위치를 기억하게끔 해야한다. 방법은 아래와 같다.

function disableScroll() {
document.body.style.overflow = 'hidden';
document.querySelector('html').scrollTop = window.scrollY;
}

function enableScroll() {
document.body.style.overflow = null;
}

touchmove

const [showModal, setShowModal] = useState(false);
useEffect(() => {
  function handleTouchMove(event) {
    if (showModal) {
      event.preventDefault();
    }
  }
  window.addEventListener("touchmove", handleTouchMove, {
    passive: false
  });
  return () =>
    window.removeEventListener("touchmove", handleTouchMove);
}, [showModal]);

event.preventDefault()를 호출하는 이유는 터치 이벤트의 기본 동작인 scroll을 막기 위함이고, addEventListener의 세번째 인자로 주어진 passive: false는 touch 이벤트 발생시 preventDefault가 호출되면 scroll을 막겠다는 의도이다. 반대로 passive: true가 되면 preventDefault 함수를 무시하고 scroll을 하게 된다.

이렇게 이벤트를 막으면 발생하는 문제점, 바로 모달의 내부 스크롤까지 동작하지 않는다는 것이다. 이벤트는 부모 노드로부터 타겟노드로 전파(캡쳐링)되고 그 다음에 타겟 노드에서 부모 노드로 전파(버블링)되기 때문이다. 이때 stopPropagation 함수를 추가하면 더 이상 이벤트가 전파되지 않도록 막는다.

<ModalWrap onTouchMove={e => e.stopPropagation()} />

활용 코드

useEffect(() => {
  function handleTouchMove(event) {
    if (NavOn) { event.preventDefault(); }
  }
  function disableScroll() {
    document.body.style.overflow = 'hidden';
    document.querySelector('html').scrollTop = window.scrollY; // dimmed 되기 전 스크롤 위치 고정
  }
  window.addEventListener('touchmove', handleTouchMove, { passive: false })
  window.addEventListener('scroll', disableScroll);
  
  return () => {
    window.removeEventListener('touchmove', handleTouchMove);
    window.removeEventListener('scroll', disableScroll);
    document.body.style.overflow = 'visible';
  }
}, [NaveOn]);

간단하쥬~?

profile
🌙`、、`ヽ`ヽ`、、ヽヽ、`、ヽ`ヽ`ヽヽ` ヽ`、`ヽ`、ヽ``、ヽ`ヽ`、ヽヽ`ヽ、ヽ `ヽ、ヽヽ`ヽ`、``ヽ`ヽ、ヽ、ヽ`ヽ`ヽ 、ヽ`ヽ`ヽ、ヽ、ヽ`ヽ`ヽ 、ヽ、ヽ、ヽ``、ヽ`、ヽヽ 🚶‍♀ ヽ``ヽ``、ヽ`、

0개의 댓글