Предотвратить прокрутку родительского элемента, когда положение прокрутки внутреннего элемента достигает верхнего / нижнего?

у меня есть небольшая "плавающая коробка инструментов" - div с position:fixed; overflow:auto. Прекрасно работать.

но при прокрутке внутри этого поля (с колесиком мыши) и достижении нижней или верхней части родительский элемент "берет на себя" "запрос прокрутки" : документ за ящиком инструментов прокручивается.
- Что раздражает, а не то, что пользователь "просит".

Я использую jQuery и думал, что могу остановить это поведение с событием.это происходит():
$("#toolBox").scroll( function(event){ event.stoppropagation() });

Это входит ли функция, но все равно распространение происходит в любом случае (прокручивается документ)
- Удивительно сложно искать эту тему на SO (и Google), поэтому я должен спросить:
Как предотвратить распространение / пузырение события scroll ?

Edit:
Рабочее решение благодаря amustill (и Брендону Аарону для mousewheel-плагина here:
https://github.com/brandonaaron/jquery-mousewheel/raw/master/jquery.mousewheel.js

$(".ToolPage").bind('mousewheel', function(e, d)  
    var t = $(this);
    if (d > 0 && t.scrollTop() === 0) {
        e.preventDefault();
    }
    else {
        if (d < 0 && (t.scrollTop() == t.get(0).scrollHeight - t.innerHeight())) {
            e.preventDefault();
        }
    }
});

29 ответов


это возможно с использованием Брэндона Аарона плагин колесо.

вот демо:http://jsbin.com/jivutakama/edit?html,js, выход


я добавляю этот ответ, для полноты, так как принятый ответ @amustill неправильно решает проблему в Internet Explorer. Пожалуйста, смотрите комментарии в мой оригинальный пост для сведения. Кроме того, это решение не требует каких - либо плагинов-только jQuery.

по сути, код работает, обрабатывая mousewheel событие. Каждое такое событие содержит wheelDelta равно числу px который он собирается переместить прокручиваемая область. Если это значение >0, тогда мы прокручиваем up. Если wheelDelta и <0 тогда мы прокручиваем down.

в Firefox: FireFox использует DOMMouseScroll как событие, так и населяет originalEvent.detail, которого +/- противоположное от того, что описано выше. Обычно он возвращает интервалы 3, в то время как другие браузеры возвращают прокрутку с интервалами 120 (по крайней мере на моей машине). Чтобы исправить, мы просто обнаруживаем его и умножаем на -40 нормализовать.

@amustill это работает путем отмены события, если <div>прокручиваемая область уже находится либо в верхней, либо в нижней максимальной позиции. Однако,Internet Explorer игнорирует отмененное событие в ситуациях, когда delta больше, чем оставшееся прокручиваемое пространство.

другими словами, если у вас есть 200px высотой <div> содержащих 500px прокручиваемого содержимого и текущего scrollTop is 400, a mousewheel событие, которое сообщает браузеру прокручивать 120px далее приведет к обоим <div> и <body> скролл, потому что 400 + 120>500.

Итак-чтобы решить проблему, мы должны сделать что-то немного другое, как показано ниже:

реквизит jQuery код:

$(document).on('DOMMouseScroll mousewheel', '.Scrollable', function(ev) {
    var $this = $(this),
        scrollTop = this.scrollTop,
        scrollHeight = this.scrollHeight,
        height = $this.innerHeight(),
        delta = (ev.type == 'DOMMouseScroll' ?
            ev.originalEvent.detail * -40 :
            ev.originalEvent.wheelDelta),
        up = delta > 0;

    var prevent = function() {
        ev.stopPropagation();
        ev.preventDefault();
        ev.returnValue = false;
        return false;
    }

    if (!up && -delta > scrollHeight - height - scrollTop) {
        // Scrolling down, but this will take us past the bottom.
        $this.scrollTop(scrollHeight);
        return prevent();
    } else if (up && delta > scrollTop) {
        // Scrolling up, but this will take us past the top.
        $this.scrollTop(0);
        return prevent();
    }
});

по сути, этот код отменяет любое событие прокрутки, которое создаст нежелательное условие ребра, а затем использует jQuery для установки scrollTop на <div> до максимального или минимального значения, в зависимости от направления mousewheel событие просит.

поскольку событие полностью отменяется в любом случае, оно никогда не распространяется на body вообще, и поэтому решает проблему в IE, а также во всех других браузерах.

я также поставил рабочий пример на jsFiddle.


Я знаю, это довольно старый вопрос, но так как это один из лучших результатов в Google... Мне пришлось как-то отменить пузырящийся свиток без jQuery, и этот код работает для меня:

function preventDefault(e) {
  e = e || window.event;
  if (e.preventDefault)
    e.preventDefault();
  e.returnValue = false;  
}

document.getElementById('a').onmousewheel = function(e) { 
  document.getElementById('a').scrollTop -= e. wheelDeltaY; 
  preventDefault(e);
}

EDIT:пример CodePen

для AngularJS, я определил следующую директиву:

module.directive('isolateScrolling', function () {
  return {
    restrict: 'A',
      link: function (scope, element, attr) {
        element.bind('DOMMouseScroll', function (e) {
          if (e.detail > 0 && this.clientHeight + this.scrollTop == this.scrollHeight) {
            this.scrollTop = this.scrollHeight - this.clientHeight;
            e.stopPropagation();
            e.preventDefault();
            return false;
          }
          else if (e.detail < 0 && this.scrollTop <= 0) {
            this.scrollTop = 0;
            e.stopPropagation();
            e.preventDefault();
            return false;
          }
        });
        element.bind('mousewheel', function (e) {
          if (e.deltaY > 0 && this.clientHeight + this.scrollTop >= this.scrollHeight) {
            this.scrollTop = this.scrollHeight - this.clientHeight;
            e.stopPropagation();
            e.preventDefault();
            return false;
          }
          else if (e.deltaY < 0 && this.scrollTop <= 0) {
            this.scrollTop = 0;
            e.stopPropagation();
            e.preventDefault();
            return false;
          }

          return true;
        });
      }
  };
});

а затем добавил его в прокручиваемый элемент (выпадающее меню ul):

<div class="dropdown">
  <button type="button" class="btn dropdown-toggle">Rename <span class="caret"></span></button>
  <ul class="dropdown-menu" isolate-scrolling>
    <li ng-repeat="s in savedSettings | objectToArray | orderBy:'name' track by s.name">
      <a ng-click="renameSettings(s.name)">{{s.name}}</a>
    </li>
  </ul>
</div>

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


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


1. Основная проблема

Scrolling input (т. е.: mousewheel), примененный к модальному элементу, перейдет в элемент предка и прокрутите его в том же направлении, если такой элемент прокручивается:

(все примеры предназначены для просмотра на разрешениях рабочего стола)

https://jsfiddle.net/ybkbg26c/5/

HTML-код:

<div id="parent">
  <div id="modal">
    This text is pretty long here.  Hope fully, we will get some scroll bars.
  </div>
</div>

CSS:

#modal {
  position: absolute;
  height: 100px;
  width: 100px;
  top: 20%;
  left: 20%;
  overflow-y: scroll;
}
#parent {
  height: 4000px;
}

2. Нет родительского свитка на модальном свитке

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

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

обычно можно перепроектировать дерево DOM для поддержки этого поведения, не влияя на то, что видит конечный пользователь.

https://jsfiddle.net/0bqq31Lv/3/

HTML-код:

<div id="context">
  <div id="parent">
  </div>
</div>
<div id="modal">
  This text is pretty long here.  Hope fully, we will get some scroll bars.
</div>

CSS (только новый):

#context {
  position: absolute;
  overflow-y: scroll;
  top: 0;
  bottom: 0;
  left: 0;
  right: 0;
}

3. Нет прокрутите в любом месте, кроме модального, пока он вверх

решение выше по-прежнему позволяет родителю получать события прокрутки, если они не перехватываются модальным окном (т. е. если они запускаются колесиком мыши, пока курсор не находится над модальным). Это иногда нежелательно, и мы можем запретить всю фоновую прокрутку, пока модальный включен. Для этого нам нужно вставить дополнительный контекст укладки, который охватывает весь видовой экран за модальным. Мы можем это сделать. путем отображения абсолютно позиционированного наложения, которое при необходимости может быть полностью прозрачным (но не visibility:hidden).

https://jsfiddle.net/0bqq31Lv/2/

HTML-код:

<div id="context">
  <div id="parent">
  </div>
</div>
<div id="overlay">  
</div>
<div id="modal">
  This text is pretty long here.  Hope fully, we will get some scroll bars.
</div>

CSS (новый поверх #2):

#overlay {
  background-color: transparent;
  position: absolute;
  top: 0;
  bottom: 0;
  left: 0;
  right: 0;
}

угловая директива JS

мне пришлось обернуть угловой директивы. Ниже приведен Mashup других ответов здесь. протестировано в Chrome и Internet Explorer 11.

var app = angular.module('myApp');

app.directive("preventParentScroll", function () {
    return {
        restrict: "A",
        scope: false,
        link: function (scope, elm, attr) {
            elm.bind('mousewheel', onMouseWheel);
            function onMouseWheel(e) {
                elm[0].scrollTop -= (e.wheelDeltaY || (e.originalEvent && (e.originalEvent.wheelDeltaY || e.originalEvent.wheelDelta)) || e.wheelDelta || 0);
                e.stopPropagation();
                e.preventDefault();
                e.returnValue = false;
            }
        }
    }
});

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

<div prevent-parent-scroll>
    ...
</div>

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


как вариант, чтобы избежать проблем с производительностью с scroll или mousewheel обращение, Вы можете использовать код, как показано ниже:

css:

body.noscroll {
    overflow: hidden;
}
.scrollable {
    max-height: 200px;
    overflow-y: scroll;
    border: 1px solid #ccc;
}

HTML-код:

<div class="scrollable">
...A bunch of items to make the div scroll...
</div>
...A bunch of text to make the body scroll...

js:

var $document = $(document),
    $body = $('body'),
    $scrolable = $('.scrollable');

$scrolable.on({
          'mouseenter': function () {
            // add hack class to prevent workspace scroll when scroll outside
            $body.addClass('noscroll');
          },
          'mouseleave': function () {
            // remove hack class to allow scroll
            $body.removeClass('noscroll');
          }
        });

пример работы:http://jsbin.com/damuwinarata/4


использование свойств прокрутки собственного элемента с Дельта-значением из плагина mousewheel:

$elem.on('mousewheel', function (e, delta) {
    // Restricts mouse scrolling to the scrolling range of this element.
    if (
        this.scrollTop < 1 && delta > 0 ||
        (this.clientHeight + this.scrollTop) === this.scrollHeight && delta < 0
    ) {
        e.preventDefault();
    }
});

все решения, приведенные в этом потоке, не упоминают существующий и собственный способ решения этой проблемы без переупорядочения DOM и/или использования трюков предотвращения событий. Но есть веская причина:Этот способ является собственностью-и доступен только на веб-платформе MS. Цитата MSDN:

- ms-scroll-chaining свойство-задает поведение прокрутки, которое возникает, когда пользователь нажимает ограничение прокрутки во время манипуляция. Значения свойств:

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

нет - эффект отскока отображается, когда пользователь нажимает ограничение прокрутки во время манипуляции.

предоставлено, это свойство поддерживается только на IE10+ / Edge. Тем не менее, вот говорю цитата:

чтобы дать вам представление о том, насколько популярным может быть предотвращение цепочки прокрутки, согласно моему быстрому поиску http-архива " - ms-scroll-chaining: нет" используется в 0.4% от лучших 300K страниц, несмотря на ограниченность функциональность и поддерживается только на IE / Edge.

а теперь хорошие новости, все! Начиная с Chrome 63, у нас, наконец, есть собственное лекарство для платформ на основе Blink-и это Chrome (очевидно) и Android WebView (скоро.)

цитирую введение статьи:

на overscroll-поведение свойство-это новая функция CSS, которая управляет поведение того, что происходит при прокрутке контейнера (включая саму страницу). Вы можете использовать его для отмены цепочки прокрутки, отключить / настроить действие pull-To-refresh, отключить rubberbanding эффекты на iOS (когда Safari реализует overscroll-поведение) и многое другое.[...]

свойство принимает три возможных значения:

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

содержат - предотвращает сцепление прокрутки. Свитки не распространяются на предков, но локальные эффекты внутри узла показаны. Например, эффект свечения overscroll на Android или rubberbanding эффект на iOS, который уведомляет пользователя, когда они попали в граница прокрутки. Отмечать: использование overscroll-behavior: contain на html элемент предотвращает действия навигации overscroll.

нет - то же, что содержит, но также предотвращает эффекты overscroll внутри самого узла (например, Android overscroll glow или iOS rubberbanding).

[...] лучшая часть заключается в том, что использование overscroll-behavior не отрицательно влияет на производительность страницы, как хаки, упомянутые в вступлении!

вот это функция. И вот соответствующий документ модуля CSS.

обновление: Firefox, начиная с версии 59, присоединился к клубу, и MS Edge, как ожидается, реализует эту функцию в версии 18. Вот соответствующее caniusage.


в случае, если кто-то все еще ищет решение для этого, следующий плагин делает работу http://mohammadyounes.github.io/jquery-scrollLock/

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

Он не изменяет скорость прокрутки колеса, пользовательский опыт не будет затронут. и вы получаете такое же поведение независимо от вертикали колеса мыши OS скорость прокрутки (в Windows она может быть установлена на один экран или одну строку до 100 строк на выемку).

демо:http://mohammadyounes.github.io/jquery-scrollLock/example/

источник:https://github.com/MohammadYounes/jquery-scrollLock


вот простая версия JavaScript:

function scroll(e) {
  var delta = (e.type === "mousewheel") ? e.wheelDelta : e.detail * -40;
  if (delta < 0 && (this.scrollHeight - this.offsetHeight - this.scrollTop) <= 0) {
    this.scrollTop = this.scrollHeight;
    e.preventDefault();
  } else if (delta > 0 && delta > this.scrollTop) {
    this.scrollTop = 0;
    e.preventDefault();
  }
}
document.querySelectorAll(".scroller").addEventListener("mousewheel", scroll);
document.querySelectorAll(".scroller").addEventListener("DOMMouseScroll", scroll);

amustill's answer as a knockout handler:

ko.bindingHandlers.preventParentScroll = {
    init: function (element, valueAccessor, allBindingsAccessor, context) {
        $(element).mousewheel(function (e, d) {
            var t = $(this);
            if (d > 0 && t.scrollTop() === 0) {
                e.preventDefault();
            }
            else {
                if (d < 0 && (t.scrollTop() == t.get(0).scrollHeight - t.innerHeight())) {
                    e.preventDefault();
                }
            }
        });
    }
};

это действительно работает в AngularJS. Проверено на Chrome и Firefox.

.directive('stopScroll', function () {
    return {
        restrict: 'A',
        link: function (scope, element, attr) {
            element.bind('mousewheel', function (e) {
                var $this = $(this),
                    scrollTop = this.scrollTop,
                    scrollHeight = this.scrollHeight,
                    height = $this.height(),
                    delta = (e.type == 'DOMMouseScroll' ?
                    e.originalEvent.detail * -40 :
                        e.originalEvent.wheelDelta),
                    up = delta > 0;

                var prevent = function() {
                    e.stopPropagation();
                    e.preventDefault();
                    e.returnValue = false;
                    return false;
                };

                if (!up && -delta > scrollHeight - height - scrollTop) {
                    // Scrolling down, but this will take us past the bottom.
                    $this.scrollTop(scrollHeight);
                    return prevent();
                } else if (up && delta > scrollTop) {
                    // Scrolling up, but this will take us past the top.
                    $this.scrollTop(0);
                    return prevent();
                }
            });
        }
    };
})

метод выше не является естественным, после некоторого гугления я нахожу более приятное решение и не нуждаюсь в jQuery. см. [1] и демо [2].

  var element = document.getElementById('uf-notice-ul');

  var isMacWebkit = (navigator.userAgent.indexOf("Macintosh") !== -1 &&
    navigator.userAgent.indexOf("WebKit") !== -1);
  var isFirefox = (navigator.userAgent.indexOf("firefox") !== -1);

  element.onwheel = wheelHandler; // Future browsers
  element.onmousewheel = wheelHandler; // Most current browsers
  if (isFirefox) {
    element.scrollTop = 0;
    element.addEventListener("DOMMouseScroll", wheelHandler, false);
  }
  // prevent from scrolling parrent elements
  function wheelHandler(event) {
    var e = event || window.event; // Standard or IE event object

    // Extract the amount of rotation from the event object, looking
    // for properties of a wheel event object, a mousewheel event object 
    // (in both its 2D and 1D forms), and the Firefox DOMMouseScroll event.
    // Scale the deltas so that one "click" toward the screen is 30 pixels.
    // If future browsers fire both "wheel" and "mousewheel" for the same
    // event, we'll end up double-counting it here. Hopefully, however,
    // cancelling the wheel event will prevent generation of mousewheel.
    var deltaX = e.deltaX * -30 || // wheel event
      e.wheelDeltaX / 4 || // mousewheel
      0; // property not defined
    var deltaY = e.deltaY * -30 || // wheel event
      e.wheelDeltaY / 4 || // mousewheel event in Webkit
      (e.wheelDeltaY === undefined && // if there is no 2D property then 
        e.wheelDelta / 4) || // use the 1D wheel property
      e.detail * -10 || // Firefox DOMMouseScroll event
      0; // property not defined

    // Most browsers generate one event with delta 120 per mousewheel click.
    // On Macs, however, the mousewheels seem to be velocity-sensitive and
    // the delta values are often larger multiples of 120, at 
    // least with the Apple Mouse. Use browser-testing to defeat this.
    if (isMacWebkit) {
      deltaX /= 30;
      deltaY /= 30;
    }
    e.currentTarget.scrollTop -= deltaY;
    // If we ever get a mousewheel or wheel event in (a future version of)
    // Firefox, then we don't need DOMMouseScroll anymore.
    if (isFirefox && e.type !== "DOMMouseScroll") {
      element.removeEventListener("DOMMouseScroll", wheelHandler, false);
    }
    // Don't let this event bubble. Prevent any default action.
    // This stops the browser from using the mousewheel event to scroll
    // the document. Hopefully calling preventDefault() on a wheel event
    // will also prevent the generation of a mousewheel event for the
    // same rotation.
    if (e.preventDefault) e.preventDefault();
    if (e.stopPropagation) e.stopPropagation();
    e.cancelBubble = true; // IE events
    e.returnValue = false; // IE events
    return false;
  }

[1] https://dimakuzmich.wordpress.com/2013/07/16/prevent-scrolling-of-parent-element-with-javascript/

[2] http://jsfiddle.net/dima_k/5mPkB/1/


для тех, кто использует MooTools, вот эквивалентный код:

            'mousewheel': function(event){
            var height = this.getSize().y;
            height -= 2;    // Not sure why I need this bodge
            if ((this.scrollTop === (this.scrollHeight - height) && event.wheel < 0) || 
                (this.scrollTop === 0 && event.wheel > 0)) {
                event.preventDefault();
            }

имейте в виду, что я, как и некоторые другие, должен был настроить значение на пару px, для чего предназначена высота -= 2.

в основном основное различие заключается в том, что в MooTools Дельта-информация поступает из события.колесо вместо дополнительного параметра передается событию.

кроме того, у меня были проблемы, если я привязан этот код к чему-либо (событию.цель.scrollHeight для связанной функции не равен этот.scrollHeight для не связанного)

надеюсь, это поможет кому-то так же, как этот пост помог мне;)


мой плагин jQuery:

$('.child').dontScrollParent();

$.fn.dontScrollParent = function()
{
    this.bind('mousewheel DOMMouseScroll',function(e)
    {
        var delta = e.originalEvent.wheelDelta || -e.originalEvent.detail;

        if (delta > 0 && $(this).scrollTop() <= 0)
            return false;
        if (delta < 0 && $(this).scrollTop() >= this.scrollHeight - $(this).height())
            return false;

        return true;
    });
}

новый веб-разработчика здесь. Это сработало как шарм для меня как на IE, так и на Chrome.

static preventScrollPropagation(e: HTMLElement) {
    e.onmousewheel = (ev) => {
        var preventScroll = false;
        var isScrollingDown = ev.wheelDelta < 0;
        if (isScrollingDown) {
            var isAtBottom = e.scrollTop + e.clientHeight == e.scrollHeight;
            if (isAtBottom) {
                preventScroll = true;
            }
        } else {
            var isAtTop = e.scrollTop == 0;
            if (isAtTop) {
                preventScroll = true;
            }
        }
        if (preventScroll) {
            ev.preventDefault();
        }
    }
}

Не позволяйте количеству строк обмануть вас, это довольно просто - просто немного многословно для читаемости (само документирование кода ftw правильно?)

также я должен упомянуть, что здесь язык машинопись, но, как всегда, его легко преобразовать в JS.


Я йоинк это из выбранной библиотеки:https://github.com/harvesthq/chosen/blob/master/coffee/chosen.jquery.coffee

function preventParentScroll(evt) {
    var delta = evt.deltaY || -evt.wheelDelta || (evt && evt.detail)
    if (delta) {
        evt.preventDefault()
        if (evt.type ==  'DOMMouseScroll') {
            delta = delta * 40  
        }
        fakeTable.scrollTop = delta + fakeTable.scrollTop
    }
}
var el = document.getElementById('some-id')
el.addEventListener('mousewheel', preventParentScroll)
el.addEventListener('DOMMouseScroll', preventParentScroll)

это работает для меня.


Я ненавижу Necro posting, но я искал это для MooTools, и это было первое, что пришло. Оригинальный пример MooTools будет работать с прокруткой вверх, но не прокруткой вниз, поэтому я решил написать этот.


var stopScroll = function (e) {
    var scrollTo = null;
    if (e.event.type === 'mousewheel') {
        scrollTo = (e.event.wheelDelta * -1);
    } else if (e.event.type === 'DOMMouseScroll') {
        scrollTo = 40 * e.event.detail;
    }
    if (scrollTo) {
        e.preventDefault();
        this.scrollTo(0, scrollTo + this.scrollTop);
    }
    return false;
};

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

(function)($){
    window.addEvent('domready', function(){
        $$('.scrollable').addEvents({
             'mousewheel': stopScroll,
             'DOMMouseScroll': stopScroll
        });
    });
})(document.id);

плагин jQuery с эмуляцией естественной прокрутки для Internet Explorer

  $.fn.mousewheelStopPropagation = function(options) {
    options = $.extend({
        // defaults
        wheelstop: null // Function
        }, options);

    // Compatibilities
    var isMsIE = ('Microsoft Internet Explorer' === navigator.appName);
    var docElt = document.documentElement,
        mousewheelEventName = 'mousewheel';
    if('onmousewheel' in docElt) {
        mousewheelEventName = 'mousewheel';
    } else if('onwheel' in docElt) {
        mousewheelEventName = 'wheel';
    } else if('DOMMouseScroll' in docElt) {
        mousewheelEventName = 'DOMMouseScroll';
    }
    if(!mousewheelEventName) { return this; }

    function mousewheelPrevent(event) {
        event.preventDefault();
        event.stopPropagation();
        if('function' === typeof options.wheelstop) {
            options.wheelstop(event);
        }
    }

    return this.each(function() {
        var _this = this,
            $this = $(_this);
        $this.on(mousewheelEventName, function(event) {
            var origiEvent = event.originalEvent;
            var scrollTop = _this.scrollTop,
                scrollMax = _this.scrollHeight - $this.outerHeight(),
                delta = -origiEvent.wheelDelta;
            if(isNaN(delta)) {
                delta = origiEvent.deltaY;
            }
            var scrollUp = delta < 0;
            if((scrollUp && scrollTop <= 0) || (!scrollUp && scrollTop >= scrollMax)) {
                mousewheelPrevent(event);
            } else if(isMsIE) {
                // Fix Internet Explorer and emulate natural scrolling
                var animOpt = { duration:200, easing:'linear' };
                if(scrollUp && -delta > scrollTop) {
                    $this.stop(true).animate({ scrollTop:0 }, animOpt);
                    mousewheelPrevent(event);
                } else if(!scrollUp && delta > scrollMax - scrollTop) {
                    $this.stop(true).animate({ scrollTop:scrollMax }, animOpt);
                    mousewheelPrevent(event);
                }
            }
        });
    });
};

https://github.com/basselin/jquery-mousewheel-stop-propagation/blob/master/mousewheelStopPropagation.js


лучшее решение я смог найти слушать событие прокрутки окна и установить scrollTop к предыдущему scrollTop, если ребенок див был виден.

prevScrollPos = 0
$(window).scroll (ev) ->
    if $('#mydiv').is(':visible')
        document.body.scrollTop = prevScrollPos
    else
        prevScrollPos = document.body.scrollTop

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


У меня похожая ситуация и вот как я ее решал:
Все мои прокручиваемые элементы получают класс прокручиваемый.

$(document).on('wheel', '.scrollable', function(evt) {
  var offsetTop = this.scrollTop + parseInt(evt.originalEvent.deltaY, 10);
  var offsetBottom = this.scrollHeight - this.getBoundingClientRect().height - offsetTop;

  if (offsetTop < 0 || offsetBottom < 0) {
    evt.preventDefault();
  } else {
    evt.stopImmediatePropagation();
  }
});

stopImmediatePropagation() не прокручивает родительскую прокручиваемую область из прокручиваемой дочерней области.

вот реализация vanilla JS этого: http://jsbin.com/lugim/2/edit?js, выход


не используйте overflow: hidden; on body. Он автоматически прокручивает все вверх. Также нет необходимости в JavaScript. Используйте overflow: auto;:

структура HTML

<div class="overlay">
    <div class="overlay-content"></div>
</div>

<div class="background-content">
    lengthy content here
</div>

стилизация

.overlay{
    position: fixed;
    top: 0px;
    left: 0px;
    right: 0px;
    bottom: 0px;
    background-color: rgba(0, 0, 0, 0.8);

    .overlay-content {
        height: 100%;
        overflow: scroll;
    }
}

.background-content{
    height: 100%;
    overflow: auto;
}

Играть Демо здесь.


проверить Лиланд Квонг код.

основная идея-привязать событие wheeling к дочернему элементу, а затем использовать собственное свойство javascript scrollHeight и свойство jQuery outerHeight дочернего элемента для обнаружения конца прокрутки, на котором return false к событию wheeling, чтобы предотвратить прокрутку.

var scrollableDist,curScrollPos,wheelEvent,dY;
$('#child-element').on('wheel', function(e){
  scrollableDist = $(this)[0].scrollHeight - $(this).outerHeight();
  curScrollPos = $(this).scrollTop();
  wheelEvent = e.originalEvent;
  dY = wheelEvent.deltaY;
  if ((dY>0 && curScrollPos >= scrollableDist) ||
      (dY<0 && curScrollPos <= 0)) {
    return false;
  }
});

есть также забавный трюк, чтобы заблокировать родителя scrollTop при наведении указателя мыши на прокручиваемый элемент. Таким образом, вам не нужно реализовывать собственную прокрутку колеса.

вот пример предотвращения прокрутки документа, но его можно настроить для любого элемента.

scrollable.mouseenter(function ()
{
  var scroll = $(document).scrollTop();
  $(document).on('scroll.trap', function ()
  {
    if ($(document).scrollTop() != scroll) $(document).scrollTop(scroll);
  });
});

scrollable.mouseleave(function ()
{
  $(document).off('scroll.trap');
});

М. К. предложили отличный плагин в своем ответе. Плагин можно найти здесь. Однако, для завершения, я подумал, что было бы хорошей идеей собрать его в одном ответе для AngularJS.

  1. начните с инъекции беседки или НПМ (в зависимости от предпочтения)

    bower install jquery-scrollLock --save
    npm install jquery-scroll-lock --save
    
  2. добавьте следующую директиву. Я выбираю добавить его как атрибут

    (function() {
       'use strict';
    
        angular
           .module('app')
           .directive('isolateScrolling', isolateScrolling);
    
           function isolateScrolling() {
               return {
                   restrict: 'A',
                   link: function(sc, elem, attrs) {
                      $('.scroll-container').scrollLock();
                   }
               }
           }
    })();
    
  3. и важная часть, которую плагин не может документировать на своем веб-сайте, - это структура HTML, которой он должен следовать.

    <div class="scroll-container locked">
        <div class="scrollable" isolate-scrolling>
             ... whatever ...
        </div>
    </div>
    

атрибут isolate-scrolling должны быть scrollable класс и все это должно быть внутри scroll-container класс или любой класс, который вы выбираете, и locked класс должен быть каскадным.


стоит отметить, что с современными фреймворками, такими как reactJS, AngularJS, VueJS и т. д., Существуют простые решения этой проблемы при работе с элементами фиксированной позиции. Примерами являются боковые панели или наложенные элементы.

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

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

пример реализации портала в материале React-ui:https://material-ui-next.com/api/portal/


простое решение с событием mouseweel:

$('.element').bind('mousewheel', function(e, d) {
    console.log(this.scrollTop,this.scrollHeight,this.offsetHeight,d);
    if((this.scrollTop === (this.scrollHeight - this.offsetHeight) && d < 0)
        || (this.scrollTop === 0 && d > 0)) {
        e.preventDefault();
    }
});

вы можете попробовать это так:

$('#element').on('shown', function(){ 
   $('body').css('overflow-y', 'hidden');
   $('body').css('margin-left', '-17px');
});

$('#element').on('hide', function(){ 
   $('body').css('overflow-y', 'scroll');
   $('body').css('margin-left', '0px');
});