Прокрутка дочернего div прокручивает окно, как мне это остановить?


96

У меня есть div с полосой прокрутки. Когда он доходит до конца, моя страница начинает прокручиваться. Как я могу остановить такое поведение?


2
@NGLN не дубликат, другой вопрос.
sidewinderguy

Ответы:


68

Вы можете отключить прокрутку всей страницы, сделав что-то вроде этого:

<div onmouseover="document.body.style.overflow='hidden';" onmouseout="document.body.style.overflow='auto';"></div>

33
Хорошо, но при наведении курсора на div полоса прокрутки в браузере исчезает.
cstack

@cstack, вы правы, поэтому собственный ответ OP (Jeevan) лучше.
Imran Bughio 04

2
Многие колеса прокрутки браузера исчезают и появляются снова, когда пользователь перемещает мышь, поэтому приведенный выше комментарий больше не является проблемой.
Кейт Холлидей,

Однако это решение не работает, если страница встроена в iframe.
Gautam Krishnan

5
не решение. установка скрытого переполнения скрывает полосу прокрутки, которая внезапно переформатирует все содержимое внешнего элемента. также предполагается, что внешний элемент - это тело, но это может быть еще один прокручиваемый div.
robisrob

43

Нашел решение.

http://jsbin.com/itajok

Это то, что мне было нужно.

А это код.

http://jsbin.com/itajok/edit#javascript,html

Использует подключаемый модуль jQuery.


Обновление в связи с уведомлением о прекращении поддержки

Из jquery-mousewheel :

Старое поведение добавления трех аргументов ( delta, deltaXи deltaY) в обработчик событий теперь не рекомендуется и будет удалено в более поздних выпусках.

Затем, event.deltaYтеперь необходимо использовать:

var toolbox = $('#toolbox'),
    height = toolbox.height(),
    scrollHeight = toolbox.get(0).scrollHeight;

toolbox.off("mousewheel").on("mousewheel", function (event) {
  var blockScrolling = this.scrollTop === scrollHeight - height && event.deltaY < 0 || this.scrollTop === 0 && event.deltaY > 0;
  return !blockScrolling;
});

Демо


6
Ваша ссылка использует github для js и прерывается, потому что здесь работает тип контента
Майкл Дж. Калкинс,

Оба jsbins, похоже, не работают в эмуляторе устройства Chrome и на iphone. Кто-нибудь знает, работает ли это еще?
Dirk Boer

2
Ваше «решение» работает только для событий прокрутки мыши, а не для страниц вверх / вниз или клавиш со стрелками.
Скотт

Почему здесь требуется "выключено"?
Брэд Джонсон

16

Выбранное решение - произведение искусства. Думал, что это достойный плагин ....

$.fn.scrollGuard = function() {
    return this
        .on( 'wheel', function ( e ) {
            var event = e.originalEvent;
            var d = event.wheelDelta || -event.detail;
            this.scrollTop += ( d < 0 ? 1 : -1 ) * 30;
            e.preventDefault();
        });
};    

Это доставляло мне постоянные неудобства, и это решение настолько чистое по сравнению с другими хаками, которые я видел. Любопытно узнать, как еще это работает и насколько широко это будет поддержано, но приветствую Дживана и тех, кто изначально это придумал. Кстати, это нужно редактору ответов stackoverflow!

ОБНОВИТЬ

Я считаю, что это лучше, потому что он вообще не пытается манипулировать DOM, а только условно предотвращает пузырение ...

$.fn.scrollGuard2 = function() {
  return this
    .on( 'wheel', function ( e ) {
      var $this = $(this);
      if (e.originalEvent.deltaY < 0) {
        /* scrolling up */
        return ($this.scrollTop() > 0);
      } else {
        /* scrolling down */
        return ($this.scrollTop() + $this.innerHeight() < $this[0].scrollHeight);
      }
    })
  ;
};    

Отлично работает в Chrome и намного проще, чем другие решения ... дайте мне знать, как это работает в других местах ...

FIDDLE


3
добавление события DOMMouseScroll заставляет его работать с Firefox → jsfiddle.net/chodorowicz/egqy7mbz/1
chodorowicz

Я удивлен, что это не написано в jquery. Я думал, что одним из главных моментов было не заботиться о том, какой браузер вы используете.
robisrob

на самом деле похоже, что оба являются устаревшими ... wheelсобытие
robisrob

Это приводит к заметно более прерывистой прокрутке в Chrome на моем Macbook. В принятом решении этой проблемы нет.
danvk

разочаровывает. есть так много разных версий этого. кажется важным аспектом пользовательского интерфейса, и действительно удивительно, что это поведение по умолчанию, когда оно настолько неинтуитивно. Очевидно, что браузер оборудован, чтобы знать, что внутренний прокручиваемый div находится на пределе, чтобы иметь возможность вместо этого применить событие к окну, так почему же нет доступных дескрипторов для его запроса?
robisrob

8

Вы можете использовать событие mouseover в div, чтобы отключить полосу прокрутки тела, а затем событие mouseout, чтобы активировать его снова?

Например, HTML

<div onmouseover="disableBodyScroll();" onmouseout="enableBodyScroll();">
    content
</div>

А потом такой javascript:

var body = document.getElementsByTagName('body')[0];
function disableBodyScroll() {
    body.style.overflowY = 'hidden';
}
function enableBodyScroll() {
    body.style.overflowY = 'auto';
}

12
Вы тоже можете использовать document.bodyвместо него.
Ry-

8

Вот кроссбраузерный способ сделать это по оси Y, он работает на настольных компьютерах и мобильных устройствах. Проверено на OSX и iOS.

var scrollArea = this.querySelector(".scroll-area");
scrollArea.addEventListener("wheel", function() {
    var scrollTop = this.scrollTop;
    var maxScroll = this.scrollHeight - this.offsetHeight;
    var deltaY = event.deltaY;
    if ( (scrollTop >= maxScroll && deltaY > 0) || (scrollTop === 0 && deltaY < 0) ) {
        event.preventDefault();
    }
}, {passive:false});

scrollArea.addEventListener("touchstart", function(event) {
    this.previousClientY = event.touches[0].clientY;
}, {passive:false});

scrollArea.addEventListener("touchmove", function(event) {
    var scrollTop = this.scrollTop;
    var maxScroll = this.scrollHeight - this.offsetHeight;
    var currentClientY = event.touches[0].clientY;
    var deltaY = this.previousClientY - currentClientY;
    if ( (scrollTop >= maxScroll && deltaY > 0) || (scrollTop === 0 && deltaY < 0) ) {
        event.preventDefault();
    }
    this.previousClientY = currentClientY;
}, {passive:false});

Это прекрасно работает! Вау, именно то, что я искал. Определенно нужно больше голосов.
delato468 01

Это решение работает довольно хорошо, но нужно немного подправить: нужно изменить scrollTop === maxScroll by scrollTop >= maxScroll. Вроде бы странно на 1 случай, это было нужно
tchiot.ludo

7

Я написал решение этой проблемы

  var div;
  div = document.getElementsByClassName('selector')[0];

  div.addEventListener('mousewheel', function(e) {
    if (div.clientHeight + div.scrollTop + e.deltaY >= div.scrollHeight) {
      e.preventDefault();
      div.scrollTop = div.scrollHeight;
    } else if (div.scrollTop + e.deltaY <= 0) {
      e.preventDefault();
      div.scrollTop = 0;
    }
  }, false);

Одно из немногих решений, которое работает без раздражающих скачков элементов страницы из-за установки скрытого переполнения.
Skeptic

+1 это твердый раствор. если вы прикрепите это к прокручиваемому контейнеру, вы захотите, чтобы div в этом примере был e.currentTarget.
Мэтт

очень чистое решение. Спасибо
Thinh Bui

Работал в Chrome 61.0.3163.100 и Safari 11.0, но не в FireFox 56.0 (Mac OS El Capitan)
BigJ

Это лучшее нативное решение. Однако falseуказывать его не нужно, addEventListenerпоскольку это значение по умолчанию. Причем сравнения должны быть простыми неравенствами ( >и <), а не >=или <=.
Вопросы

7

Как здесь сказано , большинство современных браузеров теперь поддерживают overscroll-behavior: none;свойство CSS, которое предотвращает цепочку прокрутки. И все, всего одна строчка!


2
Это должен быть правильный ответ в 2020 году. Для моего случая использования лучшим решением было overscroll-behavior: contain;.
Пирс

это не полностью поддерживается для некоторых версий Edge. есть идеи, как решить это по-другому?
YTG,

6

Если я правильно понимаю ваш вопрос, вы хотите предотвратить прокрутку основного содержимого, когда указатель мыши находится над div (скажем, боковой панелью). Для этого боковая панель может не быть дочерним элементом контейнера прокрутки основного содержимого (которым было окно браузера), чтобы предотвратить всплытие события прокрутки до его родительского элемента.

Возможно, это потребует некоторых изменений разметки следующим образом:

<div id="wrapper"> 
    <div id="content"> 
    </div> 
</div> 
<div id="sidebar"> 
</div> 

Посмотрите, как он работает в этом образце скрипки, и сравните его с этим образцом скрипта, который имеет несколько иное поведение боковой панели при отпускании мыши.

См. Также прокрутку только одного конкретного div с помощью основной полосы прокрутки браузера .


В этом случае боковая панель находится за пределами div-оболочки. Так что я не думаю, что свиток пузырится до родителя
Дживан

Под «этим делом» вы имеете в виду ваше дело? Если нет: вы совершенно правы. Если это так: если ваша страница прокручивается в конце прокрутки div, то сообщение прокрутки пузырится к родительскому элементу div, поэтому я думаю, что ваша страница и div не являются братьями и сестрами, а являются родительским и дочерним.
NGLN

Да, Page является родительским элементом моего div, поэтому по достижении конца он начинает прокрутку страницы, чего я не хочу.
Дживан

Если вы не можете сделать свой div аналогом основного содержимого, то эту проблему нельзя решить с помощью только HTML + CSS. Это поведение по умолчанию.
NGLN

1
Я считаю, что это наиболее эффективное решение из всех предоставленных ответов; с хорошей чистой разметкой в ​​основании страницы требуется гораздо меньше хаков CSS и JS для получения желаемого поведения.

3

это отключает прокрутку в окне, если вы вводите элемент селектора. работает как чары.

elements = $(".selector");

elements.on('mouseenter', function() {
    window.currentScrollTop = $(window).scrollTop();
    window.currentScrollLeft = $(window).scrollTop();
    $(window).on("scroll.prevent", function() {
        $(window).scrollTop(window.currentScrollTop);
        $(window).scrollLeft(window.currentScrollLeft);
    });
});

elements.on('mouseleave', function() {
    $(window).off("scroll.prevent");
});

Это действительно рабочее решение. Мне это было нужно на чистом js, поэтому я его переписал. Если кому-то это нужно, то оно здесь . Для тех, кто все еще ищет решение, вы должны проверить мой ответ ниже .
Андрей Столяр

2

Вы можете отключить прокрутку всей страницы, сделав что-то вроде этого, но отобразить полосу прокрутки!

<div onmouseover="document.body.style.overflow='hidden'; document.body.style.position='fixed';" onmouseout="document.body.style.overflow='auto'; document.body.style.position='relative';"></div>

2
$this.find('.scrollingDiv').on('mousewheel DOMMouseScroll', function (e) {
  var delta = -e.originalEvent.wheelDelta || e.originalEvent.detail;
  var scrollTop = this.scrollTop;
  if((delta < 0 && scrollTop === 0) || (delta > 0 && this.scrollHeight - this.clientHeight - scrollTop === 0)) {
    e.preventDefault();
  }
});

1

Основываясь на ответе ceed, вот версия, которая позволяет вкладывать элементы, защищенные прокруткой. Будет прокручиваться только элемент, над которым находится мышь, и он прокручивается довольно плавно. Эта версия также является повторной. Его можно использовать несколько раз для одного и того же элемента, и он будет правильно удалять и переустанавливать обработчики.

jQuery.fn.scrollGuard = function() {
    this
        .addClass('scroll-guarding')
        .off('.scrollGuard').on('mouseenter.scrollGuard', function() {
            var $g = $(this).parent().closest('.scroll-guarding');
            $g = $g.length ? $g : $(window);
            $g[0].myCst = $g.scrollTop();
            $g[0].myCsl = $g.scrollLeft();
            $g.off("scroll.prevent").on("scroll.prevent", function() {
                $g.scrollTop($g[0].myCst);
                $g.scrollLeft($g[0].myCsl);
            });
        })
        .on('mouseleave.scrollGuard', function() {
            var $g = $(this).parent().closest('.scroll-guarding');
            $g = $g.length ? $g : $(window);
            $g.off("scroll.prevent");
        });
};

Один из простых способов использования - добавить класс, например scroll-guard, ко всем элементам на странице, по которым вы разрешаете прокрутку. Тогда используйте $('.scroll-guard').scrollGuard()для их охраны.


0

Если вы примените overflow: hiddenстиль, он должен исчезнуть

изменить: на самом деле я неправильно прочитал ваш вопрос, это скроет только полосу прокрутки, но я не думаю, что вы ищете.


1
Мне нужна как полоса прокрутки. Я говорю о поведении. Когда я прокручиваю с помощью трекбола мыши по достижении конца прокрутки div, он не должен прокручивать всю страницу.
Дживан

0

Мне не удалось получить ни одного ответа для работы в Chrome и Firefox, поэтому я придумал это объединение:

$someElement.on('mousewheel DOMMouseScroll', scrollProtection);

function scrollProtection(event) {
    var $this = $(this);
    event = event.originalEvent;
    var direction = (event.wheelDelta * -1) || (event.detail);
    if (direction < 0) {
        if ($this.scrollTop() <= 0) {
            return false;
        }
    } else {
        if ($this.scrollTop() + $this.innerHeight() >= $this[0].scrollHeight) {
            return false;
        }
    }
}
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.