在iframe中调用element.focus()将父页面滚动到Safari iOS10中的随机位置

时间:2016-11-08 12:43:07

标签: javascript iframe mobile-safari ios10

在iOS 10.1.1上的Safari似乎在设置焦点在iframe中的元素时出错。

当我们在iframe内的元素上调用element.focus()时,Safari会立即向下滚动父页面并将焦点元素移出屏幕(而不是将焦点元素滚动到视图中)。

但是,只有当元素位于比设备屏幕高度更高的iframe时才会出现(更短的iframe可以)。

因此,如果有两个元素,一个位于iframe顶部,另一个位于页面下方,第一个将精确对焦,但第二个将在我们设置焦点时跳出屏幕。

对我而言,Safari似乎尝试将元素滚动到视图中,但数学错误并最终滚动到页面下方的随机位置。在iOS9中一切正常,所以我认为这是iOS10中的一个新bug。

是否有某种方法可以阻止父页面滚动或以其他方式避免此错误?

我已经整理了一个单页的要点,可以在iOS 10设备或模拟器上复制该问题。

以下是您可以在手机上使用的网址: goo.gl/QYi7OE

这是一个plunker,因此您可以检查桌面行为:https://embed.plnkr.co/d61KtGlzbVtVYdZ4AMqb/

该弹药的主要版本:https://gist.github.com/Coridyn/86b0c335a3e5bf72e88589953566b358

这是gist的可运行版本(此处缩短的URL指向此处): https://rawgit.com/Coridyn/86b0c335a3e5bf72e88589953566b358/raw/62a792bfec69b2c8fb02b3e99ff712abda8efecf/ios-iframe-bug.html

的index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <meta name="apple-mobile-web-app-capable" content="yes" />

    <script>
    document.addEventListener('DOMContentLoaded', function(){
        document.querySelector('#frame').srcdoc = document.querySelector('#frameContent').innerHTML;
    });
    </script>
</head>
<body>
    <iframe id="frame" frameborder="1"
        style="width: 100%; height: 1200px;"
        width="100%" height="1200">
    </iframe>

    <!--
    To keep this a one-page example, the content below will be inserted into the iframe using the `srcdoc` attribute.

    The problem occurs in iOS10 regardless of using `srcdoc` or `src` (and regardless of same-domain or cross-domain content).
    -->
    <script id="frameContent" type="text/template">
<div>
    <style>
        .spacer {
            padding-top: 400px;
            padding-bottom: 400px;
        }
        .green-block {
            width: 20px;
            height: 20px;
            background-color: green;
        }
        .red-block {
            width: 20px;
            height: 20px;
            background-color: red;
        }
    </style>

    <div class="green-block" tabindex="0"></div>

    <p>Scroll down and press the 'Focus Green' or 'Focus Red' button.</p>

    <h2>'Focus Green'</h2>
    <p><b>Expected:</b> should set focus on '.green-block' and scroll to the top of the page.</p>
    <p><b>Actual:</b> sets focus but does not scroll page.</p>

    <h2>'Focus Red'</h2>
    <p><b>Expected:</b> should set focus on '.red-block' and not scroll page (because element is already on-screen).</p>
    <p><b>Actual:</b> sets focus and scrolls down to the bottom of the host page.</p>

    <hr/>

    <p class="spacer">1 Filler content to force some visible scrolling 1</p>

    <div class="red-block" tabindex="0"></div>

    <div>
        <button type="button" onclick="document.querySelector('.green-block').focus();">Focus Green</button>
        <p>'Focus Green' should go to top of page, but on iOS10 the view doesn't move.</p>
    </div>
    <div>
        <button type="button" onclick="document.querySelector('.red-block').focus();">Focus Red</button>
        <p>'Focus Red' should stay here, but on iOS10 the view scrolls to the bottom of the page</p>
    </div>

    <p class="spacer">20 Filler content to force some visible scrolling 20</p>
    <p>Bottom of iframe</p>

</div>
    </script>

</body>
</html>

我们发现的事情:

  • 问题出现在Safari和Chrome的iOS 10+上
  • Safari iOS 9.3.2上出现 问题(行为与桌面浏览器匹配,正如预期的那样)
  • 仅在关注非输入HTML 元素时才会出现问题,例如DIV,SPAN,锚标签等;设置焦点INPUT元素正常工作
  • Desktop Safari正常运行
  • 这是滚动的父页面,而不是iframe(iframe的大小为其内容的100%,以避免在框架内滚动)
  • 我们尝试使用:window.onscroll = (event) => event.preventDefault();在父网页上调用preventDefault,但这不起作用,因为the scroll event is not cancelable
  • 父页面上出现的scroll事件似乎来自Safari / Webkit本身,因为callstack中没有其他函数(当使用DevTools和Error.stack进行检查时)

2 个答案:

答案 0 :(得分:2)

我在iOS上滚动跳转时遇到了类似的问题。它出现在所有iOS版本上,对我来说,8,9和10。

在真正的iOS 10设备中测试你的plunker并没有给我带来麻烦。我不确定我是否正确测试过。

你可以在iOS上试试这个版本的plunker吗? https://embed.plnkr.co/NuTgqW/

我的问题的解决方法是确保iframe内容中的body和html标记具有定义的100%高度和宽度,以及溢出滚动。这能够强制iframe的维度。它阻止了滚动跳跃。

如果有帮助,请告诉我。

答案 1 :(得分:0)

在我的情况下,页面包含带有输入的iframe。在页面加载时,输入得到了关注。这导致父页面滚动到输入的位置。它发生在所有浏览器中。

解决方案:当页面开始滚动时,将滚动位置设置回顶部。

因为该问题仅发生一次,所以我使用了一个命名空间事件来在防止了不需要的滚动后立即删除侦听器。

$(function() {
    if ($('#iframe').length) {
        var namespace = 'myNamespace';
        $(window).on('scroll.' + namespace, function() {
            $(window).scrollTop(0).off('scroll.' + namespace);
        });
    }
});
相关问题