防止从元素到窗口的滚动冒泡

Posted

技术标签:

【中文标题】防止从元素到窗口的滚动冒泡【英文标题】:prevent Scroll bubbling from element to window 【发布时间】:2010-11-30 09:19:15 【问题描述】:

我有一个包含 iframe 的模态框窗口(弹出窗口), 在那个 iframe 里面有一个可滚动的 div

当我滚动 iframe 的内部 DIV 并达到其上限或下限时,浏览器本身的窗口开始滚动。这是一种不受欢迎的行为

我试过这样的方法,当鼠标进入弹出框区域时, onMouseEnter 会杀死主窗口滚动:

e.preventDefault() 由于某种原因无法正常工作...

$("#popup").mouseenter(function()
   $(window).bind("scroll", function(e)
        e.preventDefault();
   ); 
).mouseleave(function()
    $(window).unbind("scroll");
);

更新

似乎现在在 2013 年 e.preventDefault(); 就足够了......

【问题讨论】:

@RoatinMarth - 因为没有人问我他们何时创建了浏览器,所以我只能自己修复他们糟糕的用户体验。如果不清楚为什么不需要这个问题,那么我不知道该告诉你什么.. 【参考方案1】:

抱歉,据我所知,无法取消任何类型的滚动事件。

W3 和 MSDN 都说:

Cancelable  No
Bubbles     No

我认为您必须将这个问题留给浏览器作者来解决。 Firefox(Linux 上的 3.5,无论如何)对我来说似乎有更好的行为:只有当您使用滚轮开始时孩子已经在顶部/底部时,它才会滚动父级。

【讨论】:

可以做到——而且相对容易:***.com/questions/5802467/… 根据您的布局,您可以在鼠标进入可滚动目标时将容器设置为 position:fixed,并在鼠标离开可滚动目标时将其重置为原来的状态。但请记住,触摸设备不能悬停。 现在可以取消chrome和FF中的事件了。 @FlavorScape 怎么样? event.preventDefault() 是否适用于滚动事件? MSDN 另有说法... 对于较旧的 IE,您必须以不同的方式进行操作。 ***.com/questions/1000597/… 否则,请记住您在窗口上阻止它。【参考方案2】:

已解决(对于一些浏览器)使用简单的 CSS 属性:overscroll-behavior:

自动 默认滚动溢出行为正常发生。

包含 在设置此值的元素内部观察到默认滚动溢出行为(例如“反弹”效果或刷新),但相邻滚动区域不会发生滚动链接,例如底层元素不会滚动。

相邻滚动区域不会发生滚动链接,并防止默认滚动溢出行为。

body
  height: 600px;
  overflow: auto;


section
  width: 50%;
  height: 50%;
  overflow: auto;
  background: lightblue;
  overscroll-behavior: none; /*   <--- the trick    */


section::before
  content: '';
  height: 200%;
  display: block;
<section>
 <input value='end' />
</section>

只需将该样式属性应用于应该“锁定”滚动的元素,滚动事件就不会冒泡到任何可能也有滚动的父元素。


与上面相同的演示,但没有诀窍:

body
  height: 600px;
  overflow: auto;


section
  width: 50%;
  height: 50%;
  overflow: auto;
  background: lightblue;


section::before
  content: '';
  height: 200%;
  display: block;
<section>
 <input value='end' />
</section>

【讨论】:

overscroll-behavior 可以成为救星。不幸的是,Safari 尚不支持它。 在 18 号边缘。还有其他人可以提供的吗? 太棒了!感谢您提供出色的解决方案。 查看更多浏览器兼容性:caniuse.com/?search=overscroll-behavior 适用于我的元素有足够的高度来触发渲染滚动条。但是对于具有相同类且不那么高的元素,它们仍然会冒泡滚动事件!也在尝试解决这个问题。【参考方案3】:

如果我们不能阻止窗口滚动,为什么不撤消它呢? 即捕获滚动事件,然后滚动回固定位置。

只要将鼠标悬停在$("#popup") 上,以下代码就会锁定 Y 轴:

// here we store the window scroll position to lock; -1 means unlocked
var forceWindowScrollY = -1;

$(window).scroll(function(event) 
  if(forceWindowScrollY != -1 && window.scrollY != forceWindowScrollY) 
    $(window).scrollTop(forceWindowScrollY);    
  
);

$("#popup").hover(function() 
  if(forceWindowScrollY == -1) 
    forceWindowScrollY = $(window).scrollTop();
  
, function() 
  forceWindowScrollY = -1;
);

我将它用于http://bundestube.de/ 上的查询建议框(在顶部搜索框中输入一些字符以使可滚动窗格可见):

这在 Chrome/Safari (Webkit) 中完美运行,在 Firefox 和 Opera 中存在一些滚动故障。由于某种原因,它不适用于我的 IE 安装。我想这与 jQuery 的悬停方法有关,它似乎在 100% 的情况下都无法正常工作。

【讨论】:

我尝试了您的网站,但它不适用于 Mac 上的 Chrome / Firefox / Safari 这很奇怪。在我的 Mac (10.6.6) 上完美运行。我们在谈论同一件事吗?为了清楚起见,我添加了一个屏幕截图。仅当鼠标指针悬停在建议框上时,窗口的滚动条才会被禁用。 @zaius 你能再试一次吗? 啊抱歉 - 我试图在结果页面上的框中滚动。是的,那现在对我有用。干得好!不知道是不是故意的,但它似乎也没有抓住向上的滚动。 @zaius 非常适合您。是的,向上滚动是按原样设计的。我使用了一个额外的“if(window.scrollY > forceWindowScrollY)”条件。【参考方案4】:

我知道这是一个很老的问题,但由于这是谷歌的***结果之一......我不得不在没有 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);

【讨论】:

@vsync 当然它不起作用,因为您示例中的 iframe 是跨域的,您无法在跨域 iframe 上获取滚动和内容事件。【参考方案5】:

我的 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;
    );

【讨论】:

似乎至少是this.on('mousewheel DOMMouseScroll', function()) 部分(注意on 而不是bind【参考方案6】:

截至 2018 年及以后,e.preventDefault 就足够了。

$('.elementClass').on("scroll", function(e)
    e.preventDefault();
 ); 

这将阻止滚动到父级。

【讨论】:

【参考方案7】:

这就是我解决问题的方法:

我在打开弹出窗口时调用以下内容:

$('body').css('overflow','hidden');

然后,当我关闭弹出窗口时,我称之为:

$('body').css('overflow','auto');

弹出窗口是模态的,因此不需要与底层主体交互

效果不错

【讨论】:

这将导致滚动条突然隐藏,内容扩展约 16px(滚动条之前占用的空间),这不是所需的 UX 行为。 这里不会发生。请jsfiddle 很明显,如果你有一个滚动条然后它消失了,就必须有东西占据空间,你不同意吗? 当然。让我仔细检查一下,因为在这里它工作正常【参考方案8】:

显然,您可以设置overflow:hidden to prevent scrolling。如果文档已经滚动,不确定会怎样。我也在使用无鼠标笔记本电脑,所以今晚我没有滚轮测试 :-) 不过这可能值得一试。

【讨论】:

我需要它来滚动..(长文本)问题不围绕这个问题。 是的,我的想法是溢出:隐藏在父窗口上。不是 iframe 本身。因此,父窗口不再可滚动,但 iframe 的内容可以。 好吧,那会让滚动条突然消失,事情就会转移到一边……不是个好主意【参考方案9】:

您可以尝试 iframe 内的 jscroll 窗格来替换默认滚动。

http://www.kelvinluck.com/assets/jquery/jScrollPane/jScrollPane.html

我不确定,但试试看

【讨论】:

这很有趣,谢谢。我以前用过这个插件,它是一个丑陋的解决方案,但对于我的这个难题来说是一个很好的解决方案。【参考方案10】:

这是我的工作:

  $('.noscroll').on('DOMMouseScroll mousewheel', function(ev) 
     var prevent = function() 
         ev.stopPropagation();
         ev.preventDefault();
         ev.returnValue = false;
         return false;
     
     return prevent();
  ); 

demo fiddle

使用 CSS overflow:hidden 隐藏滚动条,因为如果他们拖动它,这将无济于事。

跨浏览器工作

【讨论】:

在 cb 函数中包含 prevent 函数是没有意义的。如果您必须使用 prevent 函数编写代码,则将其带到外面以便任何其他人甚至可以将其用于 DRY 目的,并将您的 ev 参数传递给它。无论如何,您的回答对于已经解决的问题没有新的信息或角度。【参考方案11】:

这里有新的网络开发者。这在 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 对吗?)

【讨论】:

这是什么语言? 打字稿。正如我所说,新的 Web 开发 - 我刚开始时并没有意识到 javascript 是事实上的语言,而 TypeScript 到目前为止看起来还不错。它实际上被编译成 JavaScript 供浏览器使用。 我相信编码语言的意义不仅在于与机器交流,还在于让尽可能多的人理解,恕我直言,javascripttypescript 更好的选择。 您能否详细说明为什么您认为 javascript 比 typescript 更易于人类理解?我是 Web 开发新手,但总的来说我是一位经验丰富的开发人员,而且我注意到类型化语言往往比它们的非类型化祖先更具可读性。 我不得不同意 Vivek,打字稿甚至被用作 Angular2 的主要语言。【参考方案12】:

我想添加一些我认为效果最好的更新代码:

var yourElement = $('.my-element');

yourElement.on('scroll mousewheel wheel 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).outerHeight())
        return false;

    return true;
);

这个和上面已经提到的区别在于增加了更多的事件和使用 outerHeight() 而不是 height() 来避免崩溃如果元素有填充!

【讨论】:

【参考方案13】:
$('.scrollable').on('DOMMouseScroll mousewheel', function (e) 
    var up = false;
    if (e.originalEvent) 
        if (e.originalEvent.wheelDelta) up = e.originalEvent.wheelDelta / -1 < 0;
        if (e.originalEvent.deltaY) up = e.originalEvent.deltaY < 0;
        if (e.originalEvent.detail) up = e.originalEvent.detail < 0;
    

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

    if (!up && this.scrollHeight <= $(this).innerHeight() + this.scrollTop + 1) 
        return prevent();
     else if (up && 0 >= this.scrollTop - 1) 
        return prevent();
    
);

【讨论】:

【参考方案14】:

试试下面的代码:

var container = document.getElementById('a');
container.onwheel = (e) => 
    const deltaY = e.wheelDeltaY || -(e.deltaY * 25); // Firefox fix
    container.scrollTop -= deltaY;
    e.preventDefault();
    e.stopPropagation();
    e.returnValue = false;
;

【讨论】:

如果您可以在代码中添加一些解释,那就太好了。【参考方案15】:
function stopPropogation(e)

    e = e || window.event;
    e.cancelBubble = true;
    if (e.stopPropagation) e.stopPropagation();
    if (e.preventDefault) e.preventDefault();

这应该可行。

【讨论】:

它没有。我不明白当 e.preventDefault() 失败时 e.stopPropogation() 将如何工作...... 您不能取消或阻止滚动事件。

以上是关于防止从元素到窗口的滚动冒泡的主要内容,如果未能解决你的问题,请参考以下文章

js 事件流

防止键盘事件在 Flutter 中冒泡(传播给父母)

有啥办法可以防止 FormBuilder 元素滚动到页面顶部?

滑动页面时防止touch事件的误触问题

knockoutjs:防止没有处理程序的元素的事件冒泡

阻止网页内部滚动条mousewheel事件冒泡