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

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

Ответ 1

Обновление 2: Мое решение основано на отключении прокрутки на основе браузера вообще (когда курсор находится внутри DIV), а затем вручную прокручивает DIV с помощью JavaScript (путем установки его свойства .scrollTop). Альтернативный подход и лучший подход ИМО должны были бы лишь выборочно отключить прокрутку браузера, чтобы предотвратить прокрутку страницы, но не прокрутку 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: Как отметил Крис в комментариях ниже, в новых версиях jQuery информация о треугольниках вложена в объект .originalEvent, то есть jQuery не раскрывает ее в своем настраиваемом объекте Event и мы должны извлечь его из собственного объекта Event.

Ответ 2

Если вам не нужна совместимость со старыми версиями 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();

Ответ 3

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

Ответ 4

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

demo: 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;
           });
       }
    });

Ответ 5

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

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

Ответ 6

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

Ответ 7

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

Live demo: http://jsfiddle.net/ibcaliax/bwmzfmq7/4/

Ответ 8

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

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

Затем я создал 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

Ответ 9

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

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

Ответ 10

Чистая версия ответа Vidas на javascript, el$ - это DOM node плоскости, которую вы прокручиваете.

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 не будет работать.

Ответ 11

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

Здесь базовый пример в CodePen.

Ответ 12

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

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

Ответ 13

Используйте ниже CSS свойство overscroll-behavior: contain; к дочернему элементу