내부 스크롤을 할 때 외부 스크롤은 움직이지 않게 하기 (Vue)
개요
화면내에 화면 전체의 스크롤이 있고 어떤 작은 컨텐츠 박스에도 스크롤이 있는 상황인데 마우스를 갖다대는 영역이 컨텐츠박스 내부라면 컨텐츠박스만 스크롤이 되고 외부의 영역은 스크롤 안되게끔 하기
해결방법
<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 이벤트의 스크롤 이외의 기본 동작을 방지하는 것이며 스크롤 가능한 영역 내에서의 실제 스크롤은 여전히 동작한다.