카테고리 없음

내부 스크롤을 할 때 외부 스크롤은 움직이지 않게 하기 (Vue)

곽빵 2023. 9. 10. 15:07

개요

화면내에 화면 전체의 스크롤이 있고 어떤 작은 컨텐츠 박스에도 스크롤이 있는 상황인데 마우스를 갖다대는 영역이 컨텐츠박스 내부라면 컨텐츠박스만 스크롤이 되고 외부의 영역은 스크롤 안되게끔 하기

 

해결방법

<div @wheel.prevent :class="$style.contentBox">
  ...생략
</div>

wheel 이벤트의 기본동작을 방지하면 되는거였다.

처음에는 wheel 이벤트의 버블링에 의해 외부스크롤이 움직이는거라 생각해서 @wheel.stop으로 대응하려 했지만, 여전히 외부스크롤이 조금씩 움직이거나 내부 컨텐츠 박스의 최대 스크롤에 도달 했을때 외부 스크롤이 움직이고 있었다. 그래서 챗 지피티한테 이런 저런 질문을 날린 결과 휠 이벤트의 기본동작에 의해 이벤트가 버블링 되므로 기본동작을 막아야 한다고 한다. 휠 이벤트의 기본동작을 보면

  • 사용자가 스크롤 가능한 영역에 마우스 휠을 돌리거나 터치스크롤을 사용하여 스크롤을 시도합니다.
  • 해당 영역에 스크롤 여백이 있으면 해당 영역이 스크롤됩니다.
  • 만약 해당 영역의 스크롤이 최대 또는 최소에 도달했으면, 브라우저는 이벤트 버블링을 통해 상위 엘리먼트에 스크롤 이벤트를 전파합니다. (여기서 기본동작에 의해 이벤트 버블링이 일어난다. 이 부분은 이벤트의 기본동작에 의한 것으로 .stop과 같은 수식어로 방지할 수 없는 영역)
  • 이런 방식으로 상위 엘리먼트에서도 스크롤 여백이 있다면 해당 엘리먼트가 스크롤됩니다. 만약 스크롤 여백이 없으면 더 상위의 엘리먼트로 이벤트가 전파됩니다.
  • 이런 동작이 최상위의 <html> or <body>까지 이루어져, 사용자는 페이지 전체를 스크롤하는 경험을 합니다.

바닐라스크립트로는

document.addEventListener('DOMContentLoaded', function() {
    const contentsBox = document.querySelector('.contentsBox');

    if (contentsBox) {
        contentsBox.addEventListener('wheel', function(event) {
            event.preventDefault();
        }, { passive: false }); // passive 옵션을 false로 설정하여 preventDefault가 제대로 작동하게 합니다.
    }
});

 

참고로

event.preventDefault() (또는 Vue에서의 @wheel.prevent)는 wheel 이벤트의 기본 동작을 완전히 막는 것은 아니다. 이는 wheel 이벤트의 기본 동작 중 "스크롤 이외의" 부분을 막는 것이다. 즉, 스크롤 가능한 영역에서 최대 또는 최소에 도달했을 때 발생하는 추가적인 스크롤을 방지하는 기능을 한다.

예를 들어, 스크롤 가능한 내부 박스가 있고 해당 박스가 최대 스크롤에 도달했을 때 이벤트의 기본 동작을 막지 않으면 박스 외부의 페이지나 상위 스크롤 가능한 요소가 스크롤 되버린다. event.preventDefault()를 사용하면 이런 추가적인 스크롤 동작을 방지할 수 있다.

하지만 내부 스크롤 박스 내에서의 실제 스크롤 동작 자체는 preventDefault로 막을 수 없다

요약하면 event.preventDefault()는 wheel 이벤트의 스크롤 이외의 기본 동작을 방지하는 것이며 스크롤 가능한 영역 내에서의 실제 스크롤은 여전히 동작한다.