防止从元素到窗口的滚动冒泡
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 供浏览器使用。 我相信编码语言的意义不仅在于与机器交流,还在于让尽可能多的人理解,恕我直言,javascript比 typescript 更好的选择。 您能否详细说明为什么您认为 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() 将如何工作...... 您不能取消或阻止滚动事件。以上是关于防止从元素到窗口的滚动冒泡的主要内容,如果未能解决你的问题,请参考以下文章