Как предотвратить прокрутку страницы при прокрутке элемента DIV? - PullRequest
89 голосов
/ 29 сентября 2011

Я рассмотрел и протестировал различные функции для предотвращения способности тела прокручиваться, находясь внутри блока, и объединил функцию, которая должна работать.

$('.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 ]

159 голосов
/ 29 сентября 2011

Обновление 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,output

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

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

28 голосов
/ 05 апреля 2013

Если вас не заботит совместимость со старыми версиями IE (<8), вы можете создать собственный плагин jQuery и затем вызвать его для переполненного элемента. </p>

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

$.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();
21 голосов
/ 11 декабря 2013

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

3 голосов
/ 23 апреля 2016

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

3 голосов
/ 21 октября 2015

В приведенном выше решении есть небольшая ошибка в отношении 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;
};
3 голосов
/ 27 ноября 2013

Менее хакерское решение, на мой взгляд, это установить переполнение, скрытое на теле, когда вы наводите курсор мыши на прокручиваемый элемент 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 голосов
/ 29 сентября 2011

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

демо: 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;
           });
       }
    });
2 голосов
/ 26 августа 2014

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

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

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

Конечно, вы можете поместить другой div, например, ваш модальный, с более высоким z-index, чем #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
1 голос
/ 12 ноября 2016

Мне нужно было добавить это событие к нескольким элементам, которые могут иметь полосу прокрутки. Для случаев, когда полоса прокрутки отсутствовала, основная полоса прокрутки не работала, как должно. Поэтому я внес небольшое изменение в код @ Š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 голосов
/ 22 сентября 2018

Вот плагин, который полезен для предотвращения родительской прокрутки при прокрутке определенного 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>

Просмотр Демо

...