当 URL 包含片段时,iframe 会导致父元素在 Google Chrome 上向上滚动
Posted
技术标签:
【中文标题】当 URL 包含片段时,iframe 会导致父元素在 Google Chrome 上向上滚动【英文标题】:iframe causes parent elements to scroll up on Google Chrome when URL contains fragment 【发布时间】:2014-11-19 00:14:56 【问题描述】:在 Google Chrome(37.0.2062.122,OSX / Windows)上,具有包含片段的 URL 的 iframe 会导致父元素向上滚动。
仅在 Chrome 中发生(在 Safari 和 Firefox 中测试)。
这是一个显示问题的小提琴:http://jsfiddle.net/wmz5cu1y/1/(您必须点击Run
两次。)
如您所见,包含 iframe 的整个 <body>
已向上滚动。标题已隐藏,因为它现在位于窗口上方,并且页面底部有一个意外的空白区域。
如何避免这种情况?
为非 js 解决方法或重现问题的更简单的 html 文档提供赏金。
【问题讨论】:
你能发一个截图来显示这个问题吗?对我来说,Chrome 和 Firefox 都将 iframe 滚动到 ID 为 #up_8360397 的内容。 抱歉,刚刚添加了一个。 所以问题是,加载时它从页面的中间开始? 并非如此,当 iframe 加载并滚动到片段时,主体被向上推,由于其溢出被隐藏,它消失在视口之外。将溢出设置为滚动以查看究竟发生了什么 你在 jsfiddle 以外的任何地方都有这个问题吗?例如,它似乎没有发生在 jsbin 上。所以我认为这可能只是 jsfiddle 的一个错误。 【参考方案1】:重现脚本
找到了重现错误的最小方法:http://jsfiddle.net/4oytgwon/1/embedded/result/(您可以在单击“加载 iframe”后看到错误发生)。
它也发生在 jsfiddle 之外,使用以下代码:
<!doctype html>
<html>
<head>
<style>
body, html
padding: 0;
border: 0;
margin: 0;
height: 100%;
overflow: hidden;
#header
height: 50px;
background: blue;
#content
position: absolute;
top: 50px;
right: 0;
bottom: 0;
left: 0;
#overflow
position: absolute;
height: 50px;
bottom: -50px;
</style>
<script>
function loadIframe()
var ifr = document.createElement('iframe');
ifr.src = 'https://en.wikipedia.org/wiki/Stack_overflow#See_also';
var content = document.getElementById('content');
while (content.firstChild)
content.removeChild(content.firstChild);
content.appendChild(ifr);
window.onload = function ()
document.getElementById('btn').onclick = loadIframe;
;
</script>
</head>
<body>
<div id=header>
<button id=btn>load iframe</button>
</div>
<div id=content>
</div>
<div id=overflow>
overflow
</div>
</body>
</html>
【讨论】:
我仍然不相信这会追根究底。将***页面替换为完全不包含 javascript 的页面 - 例如 w3.org/TR/scxml/#scxml,并看不到效果。我想知道的是,导致这种情况发生的***页面是什么。 我的猜测是它与scrollIntoView有关。与此问题大约在同一时间以 chrome 发布的修复程序。我知道这很旧,但我现在看到这个问题,scrollIntoView 导致父级除了 iframe 之外还滚动。这里有一个错误bugs.chromium.org/p/chromium/issues/detail?id=819314【参考方案2】:问题是不要在 iframe url 中包含 #,如果您尝试在示例中使用下一个 url,您将看到没有滚动: http://www.w3.org/TR/scxml/#scxml
所以,我查看了您显示的页面,看看是否有什么奇怪的地方: https://news02ycombinator02com2.mentionusercontent.net/item?id=8357207#up_8360397
如果你检查 JS 它有类似下一个:
var e = window,
g = document,
h = "documentElement",
k = "CriticalImages",
l = "scrollTop",
n = "prototype",
p = "body",
q = "getAttribute",
有一个名为“scrollTop”的函数,我认为它是一个很好的研究对象。
如果你再检查一下代码,你会发现在某些时候 JS 正在获取 offsetTop 和 offsetParent:
u = function(a, b)
var c = b[q]("pagespeed_lazy_position");
if (c) return parseInt(c, 0);
var c = b.offsetTop,
d = b.offsetParent;
d && (c += u(a, d));
c = Math.max(c, 0);
b.setAttribute("pagespeed_lazy_position", c);
return c
,
结论:问题在于您尝试在 iframe 中显示的页面,而不是 fiddle、Chrome 或网址中的 #。
希望对你有所帮助。
编辑
查看 wiki 示例后,我发现这是一些 js 在小提琴中(所以第一个答案是正确的)。
为了避免在 js 中出现这种情况,您只需包含以下内容:
$( window.parent ).scroll(function(event)
// debugger;
console.log('SCROLL',event);
var scrollTop=$(this).scrollTop();
if(scrollTop>0)
console.log('fix scroll');
$(this).scrollTop(0);
);
示例:http://fiddle.jshell.net/xbnt3Lu6/21/
我将尝试查看导致问题的 js。
【讨论】:
如果投反对票的人发表评论就好了。提前致谢。【参考方案3】:修改后的答案
这里显示了两个错误。
-
JSFiddle 在加载带有片段的 iFrame 时会丢失部分标头。这在其他答案和我的“旧答案”部分中进行了讨论。并且提出了一个 github 问题。
chrome 上的 iFrame 对片段的处理效果不佳。它们会导致父级滚动。 (这可能是公平的行为)。
Chrome iFrame 问题
当您运行此程序时,您的窗口将滚动,以便窗口中的片段位于页面顶部:
<iframe src="https://news02ycombinator02com2.mentionusercontent.net/item?id=8357207#up_8360397" width="100%" height="100%"></iframe>
您可以想象,这会导致某些固定高度网站(如 jsfiddle)出现问题。它也是 Chrome 独有的(我只在 FF 上测试过,而不是 IE),Firefox 不会滚动父窗口。有一个关于它不在 FF here 中的讨论。
Chrome iFrame 父级滚动修复
Fiddle
将 onload 事件添加到调用以下函数的 iframe。并且不要使用原文中的片段:
<script>
function loadorder()
document.getElementById("iframename").src= "https://news02ycombinator02com2.mentionusercontent.net/item?id=8357207#up_8360397";
window.scrollTo(0,0);
</script>
<iframe src="https://news02ycombinator02com2.mentionusercontent.net/item?id=8357207" id="iframename" onLoad="loadorder();"></iframe>
正如您在 fiddle 和上面的 sn-p 中看到的,父窗口不再滚动,现在 FF 和 Chrome 的行为相同。
Source for fix.
旧答案(jsfiddle 的替代修复)
好的,所以我做了一些测试:
仅在 Chrome 上出现问题 任何片段链接页面都会出现问题:example 问题是 jsfiddle 特有的。 CodePen 和 jsbin 工作正常。因此,作为临时解决方法,要么使用其中一个,要么注入这个 进入jsfiddle的html:
margin-top: 30px; into the header bar like: <div id="header" style="margin-top: 30px;">
有点痛苦,因为每次加载时都需要完成 jsfiddle(但不是每次运行小提琴时)。
要解决问题,应该在jsfiddle github上报告 页面:https://github.com/jsfiddle/jsfiddle-issues/issues
编辑
这是一种在 chrome 上实现自动化的方法:下载 stylebot 扩展,并将此样式添加到 jsfiddle.com(或特定的 小提琴):
#header margin-top: 30px;
效果很好。
【讨论】:
我不得不同意这个答案,出于某种原因,iFrames 总是在 jsFiddle 中给我带来问题,偶尔在 CodePen 中也是如此。 @Adjit 我相信这是因为他们对 iframe 内容进行了一些与安全相关的处理,这增加了一些限制并导致了各种错误。这就是我通过阅读github上的一些问题所理解的。 对,OP 的问题 jsFiddle 在 Chrome 中是特定的还是只是 Chrome 特定的? @Adjit 他只提到了Chrome,但似乎是jsfiddle+chrome特有的。 @arnaud576875 你说得对,我已经更新了我的答案以更清晰。有两个错误不是一个。【参考方案4】:正如其他答案已经提到的,这里的问题是 JSFiddle 特有的。我可以通过简单地在 *** sn-p 中包含有关此问题的相同代码来轻松地向您展示这一点:
iframe
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
width: 100%;
height: 100%;
<iframe src="https://news02ycombinator02com2.mentionusercontent.net/item?id=8357207#up_8360397" width="100%" height="100%"></iframe>
更简单的 HTML 文档
在 JSFiddle 上传递到 iframe
的每个片段 URL 都会出现此问题。因此,我们可以复制这个问题,只需创建一个单独的 JSFiddle 演示并链接到我们的 iframe
中。
我在这里创建了这个新的 JSFiddle 演示:http://fiddle.jshell.net/JamesD/k5vk2u9h/1/show/light/#test。
如你所见,这里的片段是#test
。现在可以使用以下标记在新的 JSFiddle 演示中复制此问题:
<iframe src="http://fiddle.jshell.net/JamesD/k5vk2u9h/1/show/light/#test"></iframe>
height
和 width
属性无关紧要,因为没有它们,问题仍然存在。
JSFiddle demo.
解决方法
这里的另一个答案建议修复是修改 JSFiddle 自己的 HTML 以便正确地重新呈现页面。这不是一个很好的解决方案,因为在现实世界中(例如,将某人链接到 JSFiddle 演示时),他们可能不具备实现此目的的专业知识。
我们可以通过使用 JavaScript 来解决这个问题。由于我创建的新 HTML 文档位于同一域 (http://jshell.net) 上,我们可以在 iframe
本身内执行 JavaScript。手动设置location.hash
属性也会触发这个问题,所以我们可以做的是将iframe
直接滚动到我们希望关注的元素。
首先,从我们iframe
的src
属性中的URL中删除片段:
<iframe src="http://fiddle.jshell.net/JamesD/k5vk2u9h/1/show/light/"></iframe>
如上所述,我使用的片段是id
为“test”的元素。这意味着获取我们元素的最简单方法是通过其id
属性选择它:
var frame = frames[0],
elem = frame.document.querySelector('#test'),
这里,frame
是对我们自己文档中的iframe
的引用,elem
是我们的#test
元素。我们可以使用getBoundingClientRect()
获取匹配元素的位置:
position = elem.getBoundingClientRect();
这将为我们提供一个如下所示的 ClientRect 对象:
现在我们知道我们的#test
元素位于距离iframe
顶部3034px 处。使用它,我们现在可以使用window.scrollTo()
将页面自己滚动到这个位置(在这种情况下,我们的iframe
的window
保存在我们的frame
变量中):
frame.scrollTo(0, position.top);
JSFiddle demo.
请注意,此解决方案仅在 iframe
指向托管在同一域上的 URL 或在 iframe
嵌入的页面上启用了 CORS 时才有效。
最佳选择
在完成所有这些之后,最好的办法是简单地通知 JSFiddle 的开发人员这是一个错误。我在他们的 GitHub 存储库中提出了这个问题:https://github.com/jsfiddle/jsfiddle-issues/issues/547。
【讨论】:
这不是 jsFiddle 错误。 iframe 会导致其父文档向上滚动。 @arnaud576875 我无法在 JSFiddle 之外复制它。正如其他人在这里提到的那样,CodePen 和 JSBin 中不存在此错误。 这实际上是问题的重点:如何重现这个:) @arnaud576875 这就是重点——你不能因为这是一个 JSFiddle 错误。要重现它,您必须将代码 JSFiddle 复制到其结果窗格中包含的iframe
元素。我认为我不应该为此投反对票,因为我展示了一种更简单的 JSFiddle 重现错误的方法,并且我提供了一种解决方法 - 两者都是问题中提到的内容。在original edit you made 中没有提到必须在 JSFiddle 之外复制这些错误。
在 JSFiddle 之外成功复现 bug:***.com/a/26454250/4159587【参考方案5】:
它不仅是 chrome ,所有浏览器都会有同样的问题,似乎该网站有大量的 js 脚本,不知道如何,但它确实会影响您放置 iframe 的网站,解决它的最简单方法是删除 #up_8360397 来自链接。 第二种方法是在 iframe 中禁用 javascript。但是您将无法将其自动向下滚动到您想要的帖子,您必须手动设置滚动位置。
【讨论】:
到目前为止,我还没有设法在 Chrome 之外重现它,这就是为什么我仍在寻找解决方法或等待 Chrome 中的修复。 我还发现 a) 它只出现在 Chrome 中,b) 只出现在 iframe 中运行 javascript 的地方。以上是关于当 URL 包含片段时,iframe 会导致父元素在 Google Chrome 上向上滚动的主要内容,如果未能解决你的问题,请参考以下文章