当 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 问题

当您运行此程序时,您的窗口将滚动,以便窗口中的片段位于页面顶部:

&lt;iframe src="https://news02ycombinator02com2.mentionusercontent.net/item?id=8357207#up_8360397" width="100%" height="100%"&gt;&lt;/iframe&gt;

您可以想象,这会导致某些固定高度网站(如 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%;
&lt;iframe src="https://news02ycombinator02com2.mentionusercontent.net/item?id=8357207#up_8360397" width="100%" height="100%"&gt;&lt;/iframe&gt;

更简单的 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>

heightwidth 属性无关紧要,因为没有它们,问题仍然存在。

JSFiddle demo.


解决方法

这里的另一个答案建议修复是修改 JSFiddle 自己的 HTML 以便正确地重新呈现页面。这不是一个很好的解决方案,因为在现实世界中(例如,将某人链接到 JSFiddle 演示时),他们可能不具备实现此目的的专业知识。

我们可以通过使用 JavaScript 来解决这个问题。由于我创建的新 HTML 文档位于同一域 (http://jshell.net) 上,我们可以在 iframe 本身内执行 JavaScript。手动设置location.hash 属性也会触发这个问题,所以我们可以做的是将iframe 直接滚动到我们希望关注的元素。

首先,从我们iframesrc属性中的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() 将页面自己滚动到这个位置(在这种情况下,我们的iframewindow 保存在我们的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 上向上滚动的主要内容,如果未能解决你的问题,请参考以下文章

如何重新刷新iframe层不刷新父页面

当 iframe 的表单验证失败时,让父页面滚动到顶部

从 iFrame 更改父窗口 URL

如何获取页面iframe元素内容的高、宽?

当用户在 iframe 中导航时,强制 iframe 的父页面自动滚动到顶部?

为啥父元素不包含边距?