使用 iframe 和 ontouchstart 的 Mobile Safari 中的文本选择错误
在我的网络应用程序中,我有不同 z 索引的 iframe。我正在检测 iframe 中某些元素的 touchstart
事件。但是,如果我有一个文本输入字段与捕获 touchstart
的元素重叠,则输入字段开始表现不稳定:在该字段中第二次点击它会取消焦点,您无法选择任何文本,但是您可以在文本字段中输入内容。
看来唯一的解决方案可能是停止捕获背景框架上的 touchstart
事件。我更喜欢像透明 div
这样的解决方案来作为中介捕获事件,但我还没有开始工作。还有其他解决方法吗?
示例页面位于 jsfiddle,但代码如下:
<!DOCTYPE html>
<html>
<head>
<style type='text/css'>
iframe {
position:absolute;
}
#background {
border: solid 3px red;
z-index:1;
width: 20em;
height: 20em;
}
#foreground, #foreground2 {
border: solid 2px yellow;
z-index:2;
top: 15em;
height: 5em;
}
#foreground2 {
top: 22em;
}
</style>
<script type='text/javascript'>
window.onload=function(){
document.getElementById("foreground").contentDocument.write("<input type='text' value='text'/><input type='text'/>");
document.getElementById("foreground2").contentDocument.write("<input type='text' value='text'/><input type='text'/>");
document.getElementById("background").addEventListener("touchstart", function() {
console.log("touch");
});
}
</script>
</head>
<body>
<iframe id=background></iframe>
<iframe id=foreground></iframe>
<iframe id=foreground2></iframe>
</body>
</html>
In my web app, I have iframes at varying z-indices. I am detecting touchstart
events on some elements in the iframes. However, if I have a text input field overlapping an element capturing touchstart
, the input field starts behaving erratically: tapping a second time in the field un-focuses it, you are unable to select any text, but you are able to type in the text field.
It appears that the only solution might be to stop capturing touchstart
events on the background frame. I'd prefer a solution like a transparent div
to capture the events as an intermediary, but I haven't yet gotten that working. Are there other workarounds?
Sample page is at jsfiddle, but here's the code:
<!DOCTYPE html>
<html>
<head>
<style type='text/css'>
iframe {
position:absolute;
}
#background {
border: solid 3px red;
z-index:1;
width: 20em;
height: 20em;
}
#foreground, #foreground2 {
border: solid 2px yellow;
z-index:2;
top: 15em;
height: 5em;
}
#foreground2 {
top: 22em;
}
</style>
<script type='text/javascript'>
window.onload=function(){
document.getElementById("foreground").contentDocument.write("<input type='text' value='text'/><input type='text'/>");
document.getElementById("foreground2").contentDocument.write("<input type='text' value='text'/><input type='text'/>");
document.getElementById("background").addEventListener("touchstart", function() {
console.log("touch");
});
}
</script>
</head>
<body>
<iframe id=background></iframe>
<iframe id=foreground></iframe>
<iframe id=foreground2></iframe>
</body>
</html>
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
虽然这个线程现在已经很老了,但我只是想确认这个错误仍然存在于所有设备(iPhone/iPad)上的最新 iOS 版本(8.4)中。
经过一些测试,我可以确认它总是出现在以下情况下:
似乎存在以下变体:
触摸事件直接绑定在输入元素上,导致此错误。
触摸事件绑定在其中包含输入元素的父元素上(不必是直接子元素)。
输入元素在视觉上位于具有触摸事件绑定的元素的顶部。该元素位于 DOM 内的哪个位置或者触摸绑定元素和输入字段之间是否存在重叠元素并不重要。触摸绑定元素也不必可见才能发生这种情况(就像其他元素位于其顶部时一样)。一旦用户触摸触摸绑定元素后面的输入字段区域,就会出现错误。这很有趣,因为如果输入元素仅部分覆盖触摸绑定元素,则触摸触摸绑定元素不在后面的输入部分不会导致此错误。
解决方案:
首先:如果您在触摸绑定元素上尝试 PreventDefault/stopPropagation 和其他类似的东西,它似乎没有帮助。绑定元素上的事件/触摸绑定发生什么并不重要(我测试了它,当错误出现时它不会被调用/触发)。即使触摸绑定元素上的“pointer-events: none”之类的 CSS 解决方案也无济于事。因此,到目前为止,以下解决方案是我们唯一的希望:
按照本线程中的建议动态绑定触摸事件,以便仅在真正需要它们时才将它们激活,并在不需要时将其删除,以防止写入所有 3 种情况
当输入元素位于前面时,将具有触摸事件绑定的元素或容器移开。使用 CSS 转换等就足够了,即使只是在视觉上将它们从输入字段中移开也可以。 (参见上面的错误变体 3)。因此,根据您的布局,例如,如果您有一个带有搜索字段的侧边栏从页面侧面移入并覆盖主要内容,请考虑不要覆盖主要内容,而是将其移至侧面。
在不使用时,在具有触摸事件绑定的元素上使用“display:none”。这是唯一似乎可以解决 bug 变体 3 的 CSS 属性。“可见性:隐藏”在我的测试中没有效果。具有“display:none”的元素不再影响此错误的输入元素。如果您正在构建单页程序,您应该考虑不在视图中显示设置页面或“触摸绑定元素”。这将防止错误变体 3。
当然,只要存在此 bug,最好是避免使用 iframe/object/embed 标签显示其他内容。
While this thread is pretty old now I just wanted to confirm that this bug ist still persistent in the latest iOS Version (8.4) on all devices (iPhone/iPad).
After some testing, I can confirm it always appears under the following circumstance:
The following variants seem to exist:
A touch event is bound directly on the input element leading to this error.
A touch event is bound on a parent element with the input element inside it (doesn't have to be a direct child).
The input element is visually sitting on top of an element with a touch event binding. It doesn't matter where this element is inside the DOM or if there are overlaying elements between the touch bound element and the input field. The touch bound element also doesn't have to be visible for this to happen (like when other elements are on top of it). Once the user touches an area of the input field where the touch bound element is behind, the bug appears. This is interesting, because if the input element only partly overlays the touch bound element, touching the part of the input where the touch bound element is not behind will not lead to this bug.
Solutions:
First: it doesn't seem to help if you experiment with preventDefault/stopPropagation and other things like that on the touch bound element. It doesn't matter what happens with the event/touch binding on the bound element (I tested it, it doesn't get called/triggered when the bug appears). Even CSS solutions like 'pointer-events: none' on the touch bound element don't help. So the following solutions are our only hope so far:
Dynamically bind touch events like suggested in this thread, so that you only have them active when you really need them and remove them when not, to prevent all of the 3 cases written above.
Move elements or containers with touch event bindings out of the way when an input element is up front. It is enough to use CSS transforms etc as even just visually moving them away from inputs fields works. (see bug variant 3 above). So depending on your layout, e.g. if you have a sidebar with a search field which is moving in from the side of your page and overlaying the main content, consider not overlaying the main content but instead moving it to the side.
Use 'display:none' on elements with touch event binding when not in use. This the only css property which seems to solve bug variant 3. 'visibility: hidden' had no effect in my tests. Elements with 'display:none' do not affect inputs elements anymore with this bug. In case you are building a one-pager, you should consider settings pages or 'touch bound elements' that are not in view to display none. This will prevent bug variant 3.
Of course, the best thing is to avoid using iframe/object/embed tags to display other content as long as this bug exists.
没有“好的”解决方案。在我的特殊情况下,由于我不需要监视“后台”iframe 中的触摸事件,因此我只需在后台关闭该事件处理程序,然后将它们添加回前台。不幸的是,Javascript 没有任何方法来找出哪些事件附加到节点。就我而言,我独立地跟踪了这一点,而且效果很好。
有趣的是,即使您只是尝试在单个 iframe 中捕获 touchstart 事件,也会发生这种情况:在此处输入链接描述。我怀疑有解决方法
There was no "nice" solution. In my particular situation, since I didn't need to monitor touch events in the "background" iframes, I simply turned off that event handler when in the background, and added them back in the foreground. Unfortunately, Javascript doesn't have any way to find out what events are attached to a node. In my instance, I kept track of this independently, and it worked okay.
Interestingly, it appears that this occurs even if you simply try capture touchstart events within a single iframe: enter link description here. I suspect a workaround
大卫是对的。我知道这个问题没有办法解决,我花了很多时间试图找到它。唯一要做的就是在 iframe 覆盖层打开时移除所有触摸事件的 EventListener,然后在 iframe 关闭时重新 addEventListener。执行此操作的唯一方法是使用公共命名函数作为事件侦听器,以便您可以将其删除。有一天,DOM level 3 将被实现,你也许能够通过使用 eventListenerList() 来了解匿名或私有事件监听器的函数“名称”,但还没有浏览器使用它。
有趣的是,只有当 iframe 中的表单元素直接位于具有触摸事件侦听器的元素顶部时,才会发生此问题。 iframe 中不在触摸事件侦听元素之上的表单元素将正常工作。在某些情况下(不是我的),替代解决方案可能能够在 iframe 打开时将背景元素的高度更改为 1px,并在 iframe 覆盖层关闭时将其高度重置为完整大小。
David is correct. There is no way around this issue, I know, I spent a lot of time trying to find it. The only thing to do is to removeEventListener for all touch events when the iframe overlay is opened and then to re-addEventListener when the iframe closes. The only way to do this is to use a public, named function as your event listener, so that you can remove it. Some day, DOM level 3 will be implemented and you might be able to grok the function 'name' of an anonymous or private event listener by using eventListenerList(), but no browsers use this yet.
Interestingly enough, this issue only happens when the form elements in the iframe is sitting directly on top of the elements with the touch event listeners. Form elements in the iframe that are not on top of the touch event listened elements will work just fine. In some instances (not mine), and alternate solution might be able to change the height of the background element to 1px while the iframe is open and reset its height to full size when the iframe overlay closes.