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

Calling element.focus() in iframe scrolls parent page to random position in Safari iOS10

将焦点放在iframe中的元素上时,iOS 10.1.1上的Safari似乎有一个错误。

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

但是,仅当元素所在的iframe高于设备屏幕高度时才发生(较短的iframe是可以的)。

因此,如果有两个元素,一个元素位于iframe的顶部,另一个元素位于页面的下方,则第一个元素将聚焦良好,但是第二个元素将在设置焦点时跳出屏幕。

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

是防止父页面滚动的某种方法还是避免此错误的其他某种方法?

我整理了一页的要点,将问题复制到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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
<!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" />

   
    document.addEventListener('DOMContentLoaded', function(){
        document.querySelector('#frame').srcdoc = document.querySelector('#frameContent').innerHTML;
    });
   
</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">

    <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>

   

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

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

    'Focus Red'
    <p>
Expected: should set focus on '.red-block' and not scroll page (because element is already on-screen).
</p>
    <p>
Actual: 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>

   

   
        <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>
   
   
        <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>
   

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


   

</body>
</html>

我们发现的事情:

  • 此问题发生在Safari和Chrome上的iOS 10+上
  • 在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上滚动滚动时遇到了类似的问题。对于我来说,分别是8、9和10,所有iOS版本都在发生这种情况。

在真正的iOS 10设备上测试您的插头不会对我造成问题。我不确定测试是否正确。

您可以在iOS上尝试使用此版本的插件吗? https://embed.plnkr.co/NuTgqW/

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

让我知道是否有帮助。


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

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

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

1
2
3
4
5
6
7
8
$(function() {
    if ($('#iframe').length) {
        var namespace = 'myNamespace';
        $(window).on('scroll.' + namespace, function() {
            $(window).scrollTop(0).off('scroll.' + namespace);
        });
    }
});