修复 ng-blur 不适用于 iOS 的空白空间和大多数元素
目标
在我的应用中,我们希望在点击离开时隐藏打开的菜单或搜索结果
菜单或搜索框。
问题
当点击离开当前元素时,iOS Safari 无法正确模糊。
注意
您不需要特殊指令。问题不在于 ng-blur,它可以正常工作。问题是由于 Apple 在 iOS 上的不寻常设计,无论您使用 ng-blur 还是原生 DOM 模糊事件,都不会发生模糊事件。
问题原因
Apple 已将移动 Safari 设计为执行与桌面浏览器不同的事件冒泡。根据 Apple 官方文档,如果单击的元素没有附加单击侦听器,则不会触发单击事件。如果没有点击事件,焦点不会改变,也不会发生模糊事件,因此即使用户确实点击了页面上的其他位置,当前元素也不会失去焦点。
只有当用户点击带有点击事件监听器的元素时才会发生模糊。某些元素(如超链接和输入)具有用于单击的“内置”事件侦听器,因此始终会注册单击,从而导致模糊工作。
通常在桌面浏览器中,无论元素的 DOM 层次结构中是否存在已注册的侦听器,都会盲目触发 click 事件。这就是为什么 ng-blur 在桌面浏览器中可以正常工作的原因,即使在“空白空间”上也是如此。
Apple 表示,对于 iOS Safari,他们只有在找到注册监听的元素时才会调度该事件。
Apple Docs - go to Figure 6-4
Apple 的文档听起来好像他们正在寻找已注册的事件侦听器并在目标元素上进行处理,但事实并非如此。我在下面的文章中找到了答案,更重要的是,找到了答案。
Quirksmode Article on iOS event delegation
首先,如果目标元素没有点击事件侦听器和处理程序,请观察在文档或正文上放置侦听器不会导致在 iOS Safari 上调度点击事件。它适用于大多数浏览器,但不适用于 iOS。
Apple 似乎在调度点击事件之前检查 DOM 层次结构直到<body> 标记,但不检查<body> 或更高标记.因此,您需要在<body> 下方的某个元素上附加一个单击事件侦听器。
解决方案/解决方法
那么解决方案非常简单。您需要做的就是将所有页面内容包装在一个“主”容器元素中,就在<body> 元素下方,并将侦听器放在那里而不是放在正文或文档上。它甚至可以是一个空的处理函数。 Apple 只是检查一个是否已注册。包装所有内容的原因是,无论用户点击页面中的哪个位置,冒泡过程最终都会到达主容器。
如果你这样做,那么 ng-blur 将在 iOS 中按预期工作,因为空白空间上的点击事件(没有点击事件监听器的元素)将在找到父容器的点击事件监听器时通过 iOS 检查并且click 事件会像在其他浏览器中一样正常发送。
注意
此解决方案有效地使 iOS Safari 在每个 DOM 元素的层次结构上看到一个单击事件侦听器,从而诱使它在每个元素上分派单击事件,就像普通浏览器一样。我不知道 Apple 是否出于性能原因在 iOS 中避免了这种情况,或者这是否只是审美/开发人员类型偏好(参见示例)。您将使用此解决方法更改应用程序中的默认 iOS 行为。
例如,iOS 用户可能不小心在您的页面上选择了他们无意中的文本,并且如果没有单击并按住手势,通常不会发生这种情况。