当内部元素滚动位置到达顶部/底部时,防止滚动父元素? [重复]

问题描述 投票:182回答:30

这个问题在这里已有答案:

我有一个小“浮动工具箱” - 与position:fixed; overflow:auto的div。工作得很好。

但是当在该框内滚动(使用鼠标滚轮)并到达底部或顶部时,父元素“接管”“滚动请求”:工具框后面的文档滚动。 - 哪个很烦人而不是用户“要求”。

我正在使用jQuery并认为我可以使用event.stoppropagation()来阻止此行为: $("#toolBox").scroll( function(event){ event.stoppropagation() });

它确实进入了函数,但仍然无论如何传播(文档滚动) - 在SO(和Google)上搜索这个主题非常困难,所以我不得不问: 如何防止滚动事件的传播/冒泡?

编辑: 工作解决方案多亏了amustill(以及Brandon Aaron的鼠标插件: 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();
        }
    }
});
javascript jquery scroll event-bubbling event-propagation
30个回答
40
投票

使用Brandon Aaron的Mousewheel plugin是可能的。

这是一个演示:http://jsbin.com/jivutakama/edit?html,js,output


7
投票

使用带有mousewheel插件的delta值的本机元素滚动属性:

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

6
投票

如果有人仍在为此寻找解决方案,以下插件可以完成http://mohammadyounes.github.io/jquery-scrollLock/的工作

它完全解决了在给定容器内锁定鼠标滚轮滚动的问题,防止它传播到父元素。

它不会改变滚轮滚动速度,用户体验也不会受到影响。无论操作系统鼠标滚轮垂直滚动速度如何,您都可以获得相同的行为(在Windows上,它可以设置为一个屏幕,也可以设置为每行最多100行的一行)。

但是:Kua zxsw指出

资料来源:http://mohammadyounes.github.io/jquery-scrollLock/example/


4
投票

amustill作为淘汰赛处理者的答案:

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

4
投票

上面的方法不是那么自然,经过一些谷歌搜索我发现一个更好的解决方案,而不需要jQuery。见[1]和演示[2]。

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

[1] 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; }

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


4
投票

这实际上适用于AngularJS。在Chrome和Firefox上测试过。

http://jsfiddle.net/dima_k/5mPkB/1/

2
投票

我有类似的情况,这是我如何解决它: 我的所有可滚动元素都可以滚动类。

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

stopImmediatePropagation()确保不从可滚动子区域滚动父可滚动区域。

这是它的一个vanilla JS实现:$(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(); } });


2
投票

这里有新的网络开发。在IE和Chrome上,这对我来说就像是一种魅力。

http://jsbin.com/lugim/2/edit?js,output

不要让行数欺骗你,这很简单 - 只是有点冗长的可读性(自我记录代码ftw对吗?)

另外我应该提到这里的语言是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(); } } } ,但是一如既往,将它转换为JS很简单。


1
投票

对于那些使用MooTools的人来说,这里是等效的代码:

TypeScript

请记住,我和其他人一样,必须通过几个px调整一个值,这就是高度 - = 2的含义。

基本上主要区别在于,在MooTools中,delta信息来自event.wheel,而不是传递给事件的额外参数。

此外,如果我将此代码绑定到任何内容,我遇到了问题(对于绑定函数,event.target.scrollHeight不等于this.scrollHeight用于非绑定函数)

希望这有助于某人,就像这篇文章对我有所帮助;)


1
投票

我的jQuery插件:

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

1
投票

查看$('.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; }); } 代码。

基本思想是将转向事件绑定到子元素,然后使用本机javascript属性Leland Kwong's和子元素的jquery属性scrollHeight来检测滚动的结束,在outerHeight上转向事件以防止任何滚动。

return false

168
投票

我添加此答案是为了完整性,因为@amustill接受的答案无法正确解决Internet Explorer中的问题。有关详细信息,请参阅my original post中的评论。此外,这个解决方案不需要任何插件 - 只有jQuery。

本质上,代码通过处理mousewheel事件来工作。每个这样的事件都包含一个wheelDelta,它等于将可滚动区域移动到的px的数量。如果这个值是>0,那么我们滚动up。如果wheelDelta<0然后我们滚动down

FireFox:FireFox使用DOMMouseScroll作为事件,并填充originalEvent.detail,其+/-与上述内容相反。它通常返回3的间隔,而其他浏览器以120的间隔返回滚动(至少在我的机器上)。为了纠正,我们只需检测它并乘以-40进行标准化。

@amustill's answer的工作原理是,如果<div>的可滚动区域已经位于顶部或底部最大位置,则取消该事件。但是,在delta大于剩余可滚动空间的情况下,Internet Explorer忽略已取消的事件。

换句话说,如果你有一个200px<div>包含500px的可滚动内容,并且当前的scrollTop400,一个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以及所有其他浏览器中的问题。

我也提出了一个working example on jsFiddle


1
投票

我从选择的图书馆里找到了这个: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; } });

https://github.com/harvesthq/chosen/blob/master/coffee/chosen.jquery.coffee

这适合我。


0
投票

jQuery插件,模拟Internet Explorer的自然滚动

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)

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


0
投票

我能找到的最佳解决方案是在窗口上监听滚动事件,如果子div可见,则将scrollTop设置为上一个scrollTop。

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

如果你发射了很多滚动事件,那么子div的背景会有一个闪烁,所以这可以调整,但很难被注意到,这对我的用例来说已经足够了。


0
投票

不要在prevScrollPos = 0 $(window).scroll (ev) -> if $('#mydiv').is(':visible') document.body.scrollTop = prevScrollPos else prevScrollPos = document.body.scrollTop 上使用overflow: hidden;。它会自动将所有内容滚动到顶部。也不需要JavaScript。利用body

HTML Structure

overflow: auto;

Styling

<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; }


0
投票

当鼠标悬停在可滚动元素上时,还有一个有趣的技巧来锁定父级的here。这样您就不必实现自己的滚轮滚动。

这是一个防止文档滚动的示例,但可以针对任何元素进行调整。

scrollTop

0
投票

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'); }); 在他的回答中提供了一个很棒的插件。插件可以找到M.K.。但是,为了完成,我认为将它放在AngularJS的一个答案中是个好主意。

  1. 首先注入凉亭或npm(取决于哪个) here
  2. 添加以下指令。我选择将其添加为属性 bower install jquery-scrollLock --save npm install jquery-scroll-lock --save
  3. 插件无法在其网站中记录的重要部分是它必须遵循的HTML结构。 (function() { 'use strict'; angular .module('app') .directive('isolateScrolling', isolateScrolling); function isolateScrolling() { return { restrict: 'A', link: function(sc, elem, attrs) { $('.scroll-container').scrollLock(); } } } })();

属性<div class="scroll-container locked"> <div class="scrollable" isolate-scrolling> ... whatever ... </div> </div> 必须包含isolate-scrolling类,它们都需要在scrollable类或你选择的任何类中,并且必须级联scroll-container类。


0
投票

值得一提的是,使用reactJS,AngularJS,VueJS等现代框架,在处理固定位置元素时,可以轻松解决这个问题。例子是侧板或重叠元件。

该技术被称为“门户”,这意味着应用程序中使用的组件之一,无需从您使用它的地方实际提取它,将其子项安装在body元素的底部,在父母,你试图避免滚动。

请注意,它不会避免滚动body元素本身。您可以结合使用此技术并在滚动div中安装应用程序以获得预期结果。

React的材料-ui中的Portal实现示例:locked


0
投票

有ES6跨浏览器+移动香草js决定:

https://material-ui-next.com/api/portal/

0
投票

我正在为MooTools寻找这个,这是第一个出现的。原始的MooTools示例可以向上滚动,但不能向下滚动,所以我决定写这个。

  • MooTools 1.4.5:function stopParentScroll(selector) { let last_touch; let MouseWheelHandler = (e, selector) => { let delta; if(e.deltaY) delta = e.deltaY; else if(e.wheelDelta) delta = e.wheelDelta; else if(e.changedTouches){ if(!last_touch){ last_touch = e.changedTouches[0].clientY; } else{ if(e.changedTouches[0].clientY > last_touch){ delta = -1; } else{ delta = 1; } } } let prevent = function() { e.stopPropagation(); e.preventDefault(); e.returnValue = false; return false; }; if(selector.scrollTop === 0 && delta < 0){ return prevent(); } else if(selector.scrollTop === (selector.scrollHeight - selector.clientHeight) && delta > 0){ return prevent(); } }; selector.onwheel = e => {MouseWheelHandler(e, selector)}; selector.onmousewheel = e => {MouseWheelHandler(e, selector)}; selector.ontouchmove = e => {MouseWheelHandler(e, selector)}; }
  • MooTools 1.3.2:http://jsfiddle.net/3MzFJ/
  • MooTools 1.2.6:http://jsfiddle.net/VhnD4/

http://jsfiddle.net/xWrw4/

用法:

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;
};

-1
投票

鼠标滚轮事件的简单解决方案:

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

20
投票

我知道这是一个相当古老的问题,但由于这是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);
}

-2
投票

你可以这样试试:

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

19
投票

编辑:CodePen example

对于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的平滑滚动会破坏此黑客攻击。


14
投票

此线程中给出的所有解决方案都没有提及现有的 - 本机 - 解决此问题的方法,无需重新排序DOM和/或使用事件阻止技巧。但有一个很好的理由:这种方式是专有的 - 只能在MS网络平台上使用。引用MSDN

-ms-scroll-chaining属性 - 指定用户在操作期间达到滚动限制时发生的滚动行为。物业价值:

链式 - 初始值。当用户在操纵期间点击滚动限制时,最近的可滚动父元素开始滚动。没有显示弹跳效果。

none - 当用户在操作期间达到滚动限制时,会显示跳出效果。

当然,IE10 + / Edge仅支持此属性。仍然,这是一个telling quote

为了让您了解防止滚动链接的流行程度,根据我的快速http-archive搜索,“-ms-scroll-chaining:none”用于0.4%的前300K页面,尽管功能有限且仅支持IE /边缘。

现在好消息,大家好!从Chrome 63开始,我们终于可以为基于Blink的平台提供本机解决方案 - 这既是Chrome(显然)也是Android WebView(很快)。

引用the introducing article

overscroll-behavior属性是一个新的CSS功能,它控制当您过度滚动容器(包括页面本身)时发生的行为。您可以使用它来取消滚动链接,禁用/自定义拉动刷新操作,禁用iOS上的橡皮筋效果(当Safari实现过度滚动行为时)等等。[...]

该属性有三个可能的值:

自动 - 默认。源自元素的滚动可以传播到祖先元素。

包含 - 防止滚动链接。滚动不会传播到祖先,但会显示节点内的局部效果。例如,Android上的过度滚动发光效果或iOS上的橡皮筋效果会在用户达到滚动边界时通知用户。注意:使用overscroll-behavior:包含在html元素上可以防止过度滚动导航操作。

none - 与contains相同,但它也可以防止节点本身内的过度滚动效应(例如Android过度滚动发光或iOS橡皮筋)。

[...]最好的部分是使用过度滚动行为不会像介绍中提到的黑客那样对页面性能产生负面影响!

这是这个feature in action。这里是相应的CSS Module document

更新:Firefox自59版以来已加入俱乐部,MS Edge预计将在版本18中实现此功能。这是相应的caniusage


11
投票

有很多像这样的问题,有很多答案,但我找不到一个不涉及事件,脚本,插件等的令人满意的解决方案。我想在HTML和CSS中保持直接。我终于找到了一个有效的解决方案,虽然它涉及重组标记以打破事件链。


1.基本问题

如果某些此类元素可滚动,则应用于模态元素的滚动输入(即: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;
}

9
投票

作为变体,为了避免scrollmousewheel处理的性能问题,您可以使用如下代码:

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


9
投票

Angular JS Directive

我必须包装一个角度指令。以下是其他答案的混搭。在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>

希望这有助于下一个从谷歌搜索到这里的人。


8
投票

这是一个简单的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);
© www.soinside.com 2019 - 2024. All rights reserved.