DIV 요소를 스크롤 할 때 페이지 스크롤을 방지하는 방법은 무엇입니까?


94

div 내부에서 스크롤하는 신체 기능을 방지하기 위해 다양한 기능을 검토하고 테스트했으며 작동해야하는 기능을 결합했습니다.

$('.scrollable').mouseenter(function() {
    $('body').bind('mousewheel DOMMouseScroll', function() {
        return false;
    });
    $(this).bind('mousewheel DOMMouseScroll', function() {
        return true;
    });
});
$('.scrollable').mouseleave(function() {
    $('body').bind('mousewheel DOMMouseScroll', function() {
        return true;
    });
});
  • 컨테이너 내에서 스크롤이 가능하기를 원하는 모든 스크롤을 중지합니다.
  • 마우스를 놓아도 비활성화되지 않습니다.

어떤 아이디어 나 더 나은 방법이 있습니까?


u는, 어쩌면이 문제입니다, 당신의 용기가 몸 오른쪽 안에 가정 마우스 휠의 리턴 거짓으로 몸을 설정
리카르도 빈스

@ric_bfa 예,하지만 해결 방법
RIK

대신 본문, 클래스 / ID 컨테이너 설정
Ricardo Binns

분명히해야 겠네요. 마우스가 '.scrollable'요소 안에 있으면 본문의 스크롤 기능이 비활성화되어야합니다.
RIK

4
모든 CSS와 JavaScript없이이를 수행 할 수있는 방법이 없습니까?
chharvey 2010 년

답변:


165

업데이트 2 : 내 솔루션은 브라우저의 기본 스크롤을 모두 비활성화 한 다음 (커서가 DIV 내부에있을 때) JavaScript로 DIV를 수동으로 스크롤 ( .scrollTop속성 을 설정하여 )하는 것입니다. 대안 및 IMO 더 나은 접근 방식은 DIV 스크롤이 아닌 페이지 스크롤을 방지하기 위해 브라우저의 스크롤을 선택적으로 비활성화하는 것입니다. 이 솔루션을 보여주는 Rudie의 답변을 아래에서 확인하십시오.


여기 있습니다 :

$( '.scrollable' ).on( 'mousewheel DOMMouseScroll', function ( e ) {
    var e0 = e.originalEvent,
        delta = e0.wheelDelta || -e0.detail;

    this.scrollTop += ( delta < 0 ? 1 : -1 ) * 30;
    e.preventDefault();
});

라이브 데모 : https://jsbin.com/howojuq/edit?js,output

따라서 수동으로 스크롤 위치를 설정 한 다음 기본 동작 (DIV 또는 전체 웹 페이지 스크롤)을 방지합니다.

업데이트 1 : Chris가 아래 주석에서 언급했듯이 최신 버전의 jQuery에서는 델타 정보가 .originalEvent객체 내에 중첩됩니다. 즉, jQuery는 더 이상 사용자 정의 Event 객체에 정보를 노출하지 않으며 대신 기본 Event 객체에서 검색해야합니다. .


2
@RobinKnight 아니, 그렇게 보이지 않습니다. 다음은 작업 데모입니다 : jsfiddle.net/XNwbt/1 그리고 여기에 해당 줄이 제거 된 데모가 있습니다 : jsfiddle.net/XNwbt/2
Šime Vidas

1
최소한 Linux 및 Mac에서는 Firefox 10에서 수동 스크롤이 거꾸로됩니다. -e.detailFirefox (Mac, Linux), Safari (Mac) 및 Chromium (Linux)에서 테스트 한 경우 제대로 작동하는 것 같습니다 .
Anomie

1
@Anomie 맞습니다. Mozilla의 .detail기호는 .wheelDataChrome / IE 의 기호와 일치하지 않으므로 수동으로 반전해야합니다. 내 대답을 수정 해 주셔서 감사합니다.
Šime Vidas 2012

8
나는 이것을 사용했다, 감사합니다! 나는 이것을 추가해야했다 :if( e.originalEvent ) e = e.originalEvent;
Nikso

2
@ ŠimeVidas 소프트웨어에 구현 한 후이를 조정했습니다. 중요하지는 않지만 요소에 스크롤이 없을 때 교착 상태 스크롤을 방지하기 위해 scroll 속성에 대한 온 전성 검사를 추가 할 수 있습니다. if ( $(this)[0].scrollHeight !== $(this).outerHeight() ) { //yourcode }스크롤바가있을 때만 실행됩니다.
user0000001 2013-12-12

30

이전 IE 버전 (<8)과의 호환성에 관심이 없다면 사용자 지정 jQuery 플러그인을 만든 다음 오버플로 요소에서 호출 할 수 있습니다.

이 솔루션은 Šime Vidas가 제안한 것보다 장점이 있습니다. 스크롤 동작을 덮어 쓰지 않고 적절할 때 차단하기 때문입니다.

$.fn.isolatedScroll = function() {
    this.bind('mousewheel DOMMouseScroll', function (e) {
        var delta = e.wheelDelta || (e.originalEvent && e.originalEvent.wheelDelta) || -e.detail,
            bottomOverflow = this.scrollTop + $(this).outerHeight() - this.scrollHeight >= 0,
            topOverflow = this.scrollTop <= 0;

        if ((delta < 0 && bottomOverflow) || (delta > 0 && topOverflow)) {
            e.preventDefault();
        }
    });
    return this;
};

$('.scrollable').isolatedScroll();

2
감사! 기본 동작을 덮어 쓰지 않는 것이 더 적절하다고 생각합니다. 브라우저 간 지원을 위해 jQuery Mouse Wheel 플러그인을 사용할 수 있습니다 .
Meettya

불행히도 이것은 Chrome 52 (OSX 10.10)에서 결함이있는 것 같습니다. 하지만 Safari에서 완벽하게 작동합니다.
서리가 내린

1
감사합니다! 다른 솔루션은 스크롤링을 매우 느리고 버그로 만들었습니다
agrublev

@wojcikstefan, 크롬에서이 경고를 받고 있습니다 :[Violation] Added non-passive event listener to a scroll-blocking 'mousewheel' event. Consider marking event handler as 'passive' to make the page more responsive.
Syed

21

가끔 mousescroll 이벤트를 취소 할 수 있다고 생각합니다. http://jsfiddle.net/rudiedirkx/F8qSq/show/

$elem.on('wheel', function(e) {
    var d = e.originalEvent.deltaY,
        dir = d < 0 ? 'up' : 'down',
        stop = (dir == 'up' && this.scrollTop == 0) || 
               (dir == 'down' && this.scrollTop == this.scrollHeight-this.offsetHeight);
    stop && e.preventDefault();
});

이벤트 핸들러 내에서 알아야 할 사항 :


  • d = e.originalEvent.deltaY, dir = d < 0 ? 'up' : 'down' 양수는 아래로 스크롤을 의미하므로 스크롤 방향

  • scrollTop위쪽, scrollHeight - scrollTop - offsetHeight아래쪽 스크롤 위치

당신이

  • 위로 스크롤 및 top = 0, 또는
  • 아래로 스크롤 및 bottom = 0,

이벤트 취소 : e.preventDefault()(그리고 어쩌면 e.stopPropagation()).

브라우저의 스크롤 동작을 무시하지 않는 것이 좋습니다. 해당되는 경우에만 취소하십시오.

완벽하게 xbrowser는 아니지만 매우 어렵지는 않습니다. Mac의 이중 스크롤 방향이 까다로울 수도 있습니다.


2
나는 장치 (정제 / 전화) 내가하는 TouchMove 바인딩 이벤트 생각에 대한 동일한 기능 구현 어떻게
ViVekH

6

overscroll-behavior: contain;하위 요소에 CSS 속성 아래 사용


이 기본 CSS 규칙에 비해 다른 모든 솔루션은 과잉입니다. 잘하셨습니다.
TechWisdom

3

이것이 도움이되는지 확인하십시오.

데모 : jsfiddle

$('#notscroll').bind('mousewheel', function() {
     return false
});

편집하다:

이 시도:

    $("body").delegate("div.scrollable","mouseover mouseout", function(e){
       if(e.type === "mouseover"){
           $('body').bind('mousewheel',function(){
               return false;
           });
       }else if(e.type === "mouseout"){
           $('body').bind('mousewheel',function(){
               return true;
           });
       }
    });

당신의 요소는 분리되어 있지만 내 요소 중 하나는 다른 요소에 포함되어 있습니다.
RIK

3

덜 해키하지 않는 해결책은 스크롤 가능한 div 위로 마우스를 가져갈 때 본문에 오버플로를 숨기도록 설정하는 것입니다. 이렇게하면 본문이 스크롤되지 않지만 원하지 않는 "점프"효과가 발생합니다. 다음 솔루션이이를 해결합니다.

jQuery(".scrollable")
    .mouseenter(function(e) {
        // get body width now
        var body_width = jQuery("body").width();
        // set overflow hidden on body. this will prevent it scrolling
        jQuery("body").css("overflow", "hidden"); 
        // get new body width. no scrollbar now, so it will be bigger
        var new_body_width = jQuery("body").width();
        // set the difference between new width and old width as padding to prevent jumps                                     
        jQuery("body").css("padding-right", (new_body_width - body_width)+"px");
    })
    .mouseleave(function(e) {
        jQuery("body").css({
            overflow: "auto",
            padding-right: "0px"
        });
    })

필요한 경우 코드를 더 똑똑하게 만들 수 있습니다. 예를 들어 본문에 이미 패딩이 있는지 테스트하고 예인 경우 새 패딩을 추가 할 수 있습니다.


3

위의 솔루션에는 Firefox와 관련된 약간의 실수가 있습니다. Firefox에서 "DOMMouseScroll"이벤트에는 e.detail 속성이 없습니다.이 속성을 얻으려면 다음 'e.originalEvent.detail'을 작성해야합니다.

다음은 Firefox 용 작업 솔루션입니다.

$.fn.isolatedScroll = function() {
    this.on('mousewheel DOMMouseScroll', function (e) {
        var delta = e.wheelDelta || (e.originalEvent && e.originalEvent.wheelDelta) || -e.originalEvent.detail,
            bottomOverflow = (this.scrollTop + $(this).outerHeight() - this.scrollHeight) >= 0,
            topOverflow = this.scrollTop <= 0;

        if ((delta < 0 && bottomOverflow) || (delta > 0 && topOverflow)) {
            e.preventDefault();
        }
    });
    return this;
};

이것은 첫 번째 이벤트가 본문으로 전달되는 div의 상단 또는 하단에 충돌하는 경우를 제외하고는 매우 잘 작동합니다. osx의 크롬에서 두 손가락 터치로 테스트했습니다.
johnb003

3

여기에 브라우저 네이티브 스크롤을 파괴하지 않는 jQuery가없는 간단한 솔루션이 있습니다.

var scrollable = document.querySelector('.scrollable');

scrollable.addEventListener('wheel', function(event) {
    var deltaY = event.deltaY;
    var contentHeight = scrollable.scrollHeight;
    var visibleHeight = scrollable.offsetHeight;
    var scrollTop = scrollable.scrollTop;

    if (scrollTop === 0 && deltaY < 0)
        event.preventDefault();
    else if (visibleHeight + scrollTop === contentHeight && deltaY > 0)
        event.preventDefault();
});

라이브 데모 : http://jsfiddle.net/ibcaliax/bwmzfmq7/4/


방금 위 코드를 구현하는 NPM 라이브러리를 게시했습니다. npmjs.com/package/dontscrollthebody
Iñaki Baz Castillo

2

다음은 응용 프로그램에서 사용한 솔루션입니다.

본문 오버플로를 비활성화하고 전체 웹 사이트 html을 컨테이너 div 안에 배치했습니다. 웹 사이트 컨테이너에 오버플로가 있으므로 사용자가 예상대로 페이지를 스크롤 할 수 있습니다.

그런 다음 전체 웹 사이트를 포괄하는 더 높은 Z- 색인으로 형제 div (#Prevent)를 만들었습니다. #Prevent는 Z- 색인이 더 높기 때문에 웹 사이트 컨테이너와 겹칩니다. #Prevent가 표시되면 마우스가 더 이상 웹 사이트 컨테이너를 가리 키지 않으므로 스크롤이 불가능합니다.

물론 마크 업에서 #Prevent보다 Z- 색인이 더 높은 모달과 같은 다른 div를 배치 할 수 있습니다. 이를 통해 스크롤 문제가없는 팝업 창을 만들 수 있습니다.

이 솔루션은 스크롤바를 숨기지 않기 때문에 더 좋습니다 (점프 효과). 이벤트 리스너가 필요하지 않으며 구현하기 쉽습니다. 모든 브라우저에서 작동하지만 IE7 및 8에서는 플레이해야합니다 (특정 코드에 따라 다름).

HTML

<body>
  <div id="YourModal" style="display:none;"></div>
  <div id="Prevent" style="display:none;"></div>
  <div id="WebsiteContainer">
     <div id="Website">
     website goes here...
     </div>
  </div>
</body>

CSS

body { overflow: hidden; }

#YourModal {
 z-index:200;
 /* modal styles here */
}

#Prevent {
 z-index:100;
 position:absolute;
 left:0px;
 height:100%;
 width:100%;
 background:transparent;
}

#WebsiteContainer {
  z-index:50;
  overflow:auto;
  position: absolute;
  height:100%;
  width:100%;
}
#Website {
  position:relative;
}

jquery / js

function PreventScroll(A) { 
  switch (A) {
    case 'on': $('#Prevent').show(); break;
    case 'off': $('#Prevent').hide(); break;
  }
}

스크롤 비활성화 / 활성화

PreventScroll('on'); // prevent scrolling
PreventScroll('off'); // allow scrolling

2
단순히 코드를 게시하는 것보다 답변에 대한 몇 가지 정보를 포함하는 것이 좋습니다. 우리는 단순히 '수정'을 제공하는 것이 아니라 사람들이 배울 수 있도록 돕습니다. 원래 코드에서 무엇이 잘못되었는지, 무엇을 다르게했는지, 왜 변경 사항이 적용되었는지 설명해야합니다.
Andrew Barber

1

스크롤바가있을 수있는 여러 요소에이 이벤트를 추가해야했습니다. 스크롤바가없는 경우 메인 스크롤바가 제대로 작동하지 않았습니다. 그래서 @ Šime 코드를 다음과 같이 약간 변경했습니다.

$( '.scrollable' ).on( 'mousewheel DOMMouseScroll', function ( e ) {
    if($(this).prop('scrollHeight') > $(this).height())
    {
        var e0 = e.originalEvent, delta = e0.wheelDelta || -e0.detail;

        this.scrollTop += ( delta < 0 ? 1 : -1 ) * 30;
        e.preventDefault();
    }       
});

이제 스크롤바가있는 요소 만 기본 스크롤이 중지되는 것을 방지합니다.


0

Vidas의 대답의 순수한 자바 스크립트 버전 el$은 스크롤하는 평면의 DOM 노드입니다.

function onlyScrollElement(event, el$) {
    var delta = event.wheelDelta || -event.detail;
    el$.scrollTop += (delta < 0 ? 1 : -1) * 10;
    event.preventDefault();
}

여러 번 부착하지 마십시오! 여기에 예가 있습니다.

var ul$ = document.getElementById('yo-list');
// IE9, Chrome, Safari, Opera
ul$.removeEventListener('mousewheel', onlyScrollElement);
ul$.addEventListener('mousewheel', onlyScrollElement);
// Firefox
ul$.removeEventListener('DOMMouseScroll', onlyScrollElement);
ul$.addEventListener('DOMMouseScroll', onlyScrollElement);

주의 할 점은 함수를 연결하기 전에 매번 다시 초기화하면 함수가 일정해야합니다. var func = function (...)removeEventListener작동하지 않습니다.


0

JavaScript 없이도 가능합니다. 두 div의 스타일을 position: fixed및로 설정할 수 있습니다 overflow-y: auto. 둘 중 하나를 설정하여 다른 하나보다 높게 만들어야 할 수도 있습니다 z-index(겹치는 경우).

다음 은 CodePen에 대한 기본 예제입니다 .


0

다음은 특정 div를 스크롤하는 동안 부모 스크롤을 방지하는 데 유용한 플러그인이며 여러 가지 옵션이 있습니다.

여기에서 확인하세요 :

https://github.com/MohammadYounes/jquery-scrollLock

용법

JavaScript를 통해 Scroll Lock 트리거 :

$('#target').scrollLock();

마크 업을 통해 Scroll Lock 트리거 :

    <!-- HTML -->
<div data-scrollLock
     data-strict='true'
     data-selector='.child'
     data-animation='{"top":"top locked","bottom":"bottom locked"}'
     data-keyboard='{"tabindex":0}'
     data-unblock='.inner'>
     ...
</div>

<!-- JavaScript -->
<script type="text/javascript">
  $('[data-scrollLock]').scrollLock()
</script>

데모 보기


0

사용자가 명백한 변화에 대해 부정적인 경험을 할 것이라고 생각하지 않는 경우 가능한 솔루션으로 제공합니다. 마우스가 대상 div 위에있을 때 body의 오버플로 클래스를 숨김으로 변경했습니다. 그런 다음 마우스가 떠날 때 body의 div를 hidden overflow로 변경했습니다.

개인적으로 나는 그것이 나쁘게 보이지 않는다고 생각하고, 내 코드는 토글을 사용하여 더 깨끗하게 만들 수 있으며, 사용자가 알지 못하는 사이에이 효과를 가능하게하는 명백한 이점이 있습니다. 그래서 이것은 아마도 hackish-last-resort 대답 일 것입니다.

//listen mouse on and mouse off for the button
pxMenu.addEventListener("mouseover", toggleA1);
pxOptContainer.addEventListener("mouseout", toggleA2);
//show / hide the pixel option menu
function toggleA1(){
  pxOptContainer.style.display = "flex";
  body.style.overflow = "hidden";
}
function toggleA2(){
  pxOptContainer.style.display = "none";
  body.style.overflow = "hidden scroll";
}
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.