【问题标题】: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 - 因为没有人问我他们何时创建了浏览器,所以我只能自己修复他们糟糕的用户体验。如果不清楚为什么不需要这个问题,那么我不知道该告诉你什么..

标签: javascript jquery html events scroll


【解决方案1】:

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

W3MSDN 都说:

Cancelable  No
Bubbles     No

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

【讨论】:

  • 可以做到——而且相对容易:stackoverflow.com/questions/5802467/…
  • 根据您的布局,您可以在鼠标进入可滚动目标时将容器设置为 position:fixed,并在鼠标离开可滚动目标时将其重置为原来的状态。但请记住,触摸设备不能悬停。
  • 现在可以取消chrome和FF中的事件了。
  • @FlavorScape 怎么样? event.preventDefault() 是否适用于滚动事件? MSDN 另有说法...
  • 对于较旧的 IE,您必须以不同的方式进行操作。 stackoverflow.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() 将如何工作......
        • 您不能取消或阻止滚动事件。
        猜你喜欢
        • 2012-05-18
        • 1970-01-01
        • 1970-01-01
        • 2011-04-28
        • 1970-01-01
        • 2019-08-13
        • 1970-01-01
        • 2023-03-24
        • 1970-01-01
        相关资源
        最近更新 更多