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



Safari在iOS 10.1.1上设置焦点在iframe内的元素时似乎有一个错误。

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

但是,只有当元素在一个高于设备屏幕高度的iframe中时才会发生这种情况(较短的iframe是可以的)。

如果有两个元素,一个在iframe的顶部,另一个在页面的下方,第一个会很好地聚焦,但是当我们设置焦点时,第二个会跳出屏幕。

对我来说,它看起来像Safari是尝试滚动元素进入视图,但数学是错误的,他们最终滚动到一个随机位置进一步向下的页面。在iOS9中一切正常,所以我认为这是iOS10的一个新bug。

是否有一些方法可以防止父页面滚动或其他一些方法来避免这个错误?

我整理了一个一页的要点,在iOS 10设备或模拟器上复制了这个问题。

这是一个你可以在手机上使用的URL: goo.gl/QYi7OE

这是一个活塞,你可以检查桌面行为:https://embed.plnkr.co/d61KtGlzbVtVYdZ4AMqb/

以及该活塞的主要版本:https://gist.github.com/Coridyn/86b0c335a3e5bf72e88589953566b358

这里是要点的可运行版本(上面的缩短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>

我们发现的事情:

  • 这个问题发生在iOS 10+中Safari和Chrome
  • 问题发生在Safari iOS 9.3.2(行为匹配桌面浏览器,如预期)
  • 问题只发生在关注非输入HTML元素时,例如div, span,锚标记等;将焦点设置在INPUT元素上工作正常
  • 桌面Safari工作正常
  • 滚动的是父页面,而不是iframe (iframe的大小为其内容的100%,以避免在框架内滚动)
  • 我们已经尝试在父页面上使用:window.onscroll = (event) => event.preventDefault();调用preventDefault,但这不起作用,因为scroll事件是不可取消的
  • 在父页面上引发的scroll事件似乎来自Safari/Webkit本身,因为在调用堆栈中没有其他函数(当使用DevTools和Error.stack检查时)

我在iOS上也遇到过类似的滚动条跳跃问题。对于我来说,所有iOS版本,8,9和10都是如此。

在真正的iOS 10设备上测试你的活塞没有引起我的问题。我不确定我的测试是否正确。

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

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

如果有帮助,请告诉我。

在我的情况下,一个页面包含一个带有输入的iframe。在页面加载时,输入得到了焦点。这将导致父页面滚动到输入的位置。在所有浏览器中都发生过。

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

因为这个问题只发生了一次,所以我使用了一个命名空间事件,在防止了不希望出现的滚动后立即删除侦听器。

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

最新更新