Как предотвратить прокрутку страницы при прокрутке элемента DIV?


Я рассмотрел и протестировал различные функции для предотвращения способности тела прокручивать в то время как внутри 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;
    });
});
  • это останавливает всю прокрутку, где, как я хочу, прокрутка все еще возможна внутри контейнера
  • Это также не отключается на мыши оставить

есть идеи, или лучшие способы сделать это?

12 83

12 ответов:

обновление 2: мое решение основано на отключении собственной прокрутки браузера в целом (когда курсор находится внутри DIV), а затем вручную прокручивает DIV с помощью JavaScript (установив его .scrollTop свойства). Альтернативным и лучшим подходом IMO было бы только выборочно отключить прокрутку браузера, чтобы предотвратить прокрутку страницы, но не прокрутку DIV. Проверьте ответ Руди ниже, который демонстрирует это решение.


здесь вы вперед:

$( '.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, выход

таким образом, вы вручную устанавливаете положение прокрутки, а затем просто предотвращаете поведение по умолчанию (которое будет прокручивать DIV или всю веб-страницу).

обновление 1: как отметил Крис в комментариях ниже, в более новых версиях jQuery Дельта-информация вложена в .originalEvent объект, т. е. jQuery не предоставляет его в своем пользовательском объекте события больше, и мы должны получить его из собственного объекта события вместо этого.

Если вы не заботитесь о совместимости со старыми версиями IE (

Это решение имеет преимущество перед предложенным Шиме Видасом, поскольку оно не перезаписывает поведение прокрутки - оно просто блокирует его, когда это необходимо.

$.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();

Я думаю, что иногда можно отменить событие 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 является сложным, хотя...

посмотреть, если это поможет вам:

демо: jsfiddle

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

edit:

попробуйте это:

    $("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;
           });
       }
    });

менее хакерское решение, на мой взгляд, состоит в том, чтобы установить переполнение, скрытое на теле, когда вы наводите курсор мыши на прокручиваемый 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"
        });
    })

вы можете сделать свой код умнее, если это необходимо. Например, вы можете проверить, есть ли в теле уже заполнение, и если да, добавьте к нему новое заполнение.

в решении выше есть небольшая ошибка в отношении Firefox. В Firefox событие "DOMMouseScroll" не имеет свойства e.detail,чтобы получить это свойство, вы должны написать следующее 'E.originalEvent.подробно".

вот рабочее решение для 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;
};

вот простое решение без 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/

вот мое решение, которое я использовал в приложениях.

Я отключил переполнение тела и разместил весь сайт HTML внутри div-контейнера по. На сайте контейнеры переполняются и поэтому пользователь может прокручивать страницы, как ожидалось.

затем я создал брат div (#Prevent) с более высоким Z-индексом, который охватывает весь веб-сайт. Поскольку #Prevent имеет более высокий Z-индекс, он перекрывает контейнер веб-сайта. Когда #Prevent отображается, мышь больше не парит контейнеры веб-сайта, поэтому прокрутка невозможна.

вы можете, конечно, разместить другой div, такой как ваш модальный, с более высоким Z-индексом, чем #Prevent в разметке. Это позволяет создавать всплывающие окна, которые не страдают от проблем скроллинга.

это решение лучше, потому что оно не скрывает полосы прокрутки (эффект прыжка). Это не требует прослушивателей событий, и это легко реализовать. Он работает во всех браузерах, хотя с 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

Мне нужно было добавить это событие к нескольким элементам, которые могут иметь полосу прокрутки. В тех случаях, когда полосы прокрутки не было, основная полоса прокрутки не работала должным образом. Поэтому я внес небольшое изменение в код @Š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();
    }       
});

теперь только элементы с полосой прокрутки будут препятствовать остановке основной прокрутки.

чистая версия javascript ответа 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 (...) the removeEventListener не будет работать.

вы можете сделать это без JavaScript. Вы можете установить стиль на обоих дивах в position: fixed и overflow-y: auto. Возможно, вам придется сделать один из них выше, чем другой, установив его z-index (если они пересекаются).

здесь основной пример на CodePen.

вот плагин, который полезен для предотвращения родительской прокрутки при прокрутке определенного div и имеет множество опций для игры.

смотрите здесь:

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

использование

блокировка прокрутки триггера с помощью JavaScript:

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

блокировка прокрутки триггера с помощью разметки:

    <!-- 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>

посмотреть демо