竹笋

首页 » 问答 » 环境 » iOSSafari的click事件委托问
TUhjnbcbe - 2023/3/6 9:25:00

上古时代的浏览器就开始支持事件委托了,如果你点击某个元素,事件将一直冒泡到document,并且会执行路径中途径元素的事件监听器。

无数实践已经证实iOSSafari在相当长的时期内不支持click事件的事件委托,除非点击发生在链接或者输入框上。这是一个非常恼人的bug,不过好消息是有一些办法可以解决这个问题。

事件委托

让我们简单回顾一下它是什么。事件委托是指把事件监听器绑定在发生事件的元素的上级元素上,而不是绑定在触发事件的元素自身上。

例如,假设你有一个包含许多菜单项的下拉菜单。需要鼠标悬停和鼠标移出事件来使菜单工作。

现在,你可以将mouseover和mouseout事件绑定到每个单独的菜单项,但是将mouseover和mouseout事件监听器绑定到下拉菜单的最顶层元素要更加高效。伪代码如下:

这里的秘诀是事件冒泡。如果用户将鼠标悬停在li元素上,mouseover事件会一直冒泡到document。也就是说,它首先检查li元素自身是否有onmouseover事件监听器,然后是li的父级元素,然后是它的祖父级元素,等等。一直到document。会执行在此过程中找到的所有事件监听器。

因此,事件冒泡到ul元素,在那里找到了onmouseover事件监听器,然后会执行它。这样我们在下拉菜单中捕获了所有的鼠标悬停和鼠标离开事件,每种事件只有一个事件监听器,这样节省了占用的内存(同时也节省了大量的代码)。

iOSSafari的bug

在iOSSafari上,事件委托不适用于click事件。它对mouseover和mouseout事件支持很好,对touch事件也支持很好,但对click事件就不行了。

不过,如果触发click事件的元素是链接,button按钮或者表单输入框,事件委托可以正常工作。

把上述代码保存为一个html文件,然后在浏览器中打开。在所有其他浏览器中,无论点击a标签还是p标签,都会看到一个弹窗出现,可以证明绑定在document上的click监听器正常工作了。

在一个iOSSafari浏览器中打开这个页面,则只有在点击a标签时才会看到弹窗。点击p标签则不会,可以证明click事件没有冒泡到document。

解决方案

方案1.监听目标元素自身的click事件我们的思路很简单:为目标元素监听它的click事件,这样就告知浏览器此元素是一个可点击元素,它的click事件会冒泡传播到document。你可以这样做,使用元素的onclick属性:

或者使用JavaScript来为它监听click事件,这个监听器可以是一个空的函数:

以上两种方式有效解决了click事件的冒泡问题,不过存在一些缺陷。我们要么需要为目标元素设置onclick属性,要么需要把一个空函数赋值给它的onclick属性。对于静态页面,这不是一个大问题,只需处理少量已有的元素即可。而对于页面中动态生成的元素可能存在问题,你需要不断监听页面元素的变化,然后需要为新的元素执行同样的操作。这样比较繁琐,而且可能存在性能问题。

方案2.使用CSS的cursor属性CSS的cursor属性用于设置鼠标的光标类型,当鼠标置于元素上方时会展示出来。当它的值设置为pointer时,表示元素为链接。

由于未知的原因,当为元素的样式设置cursor:pointer时,它的click事件可以正常冒泡。所以,你可以为要冒泡的元素设置这个属性值,例如我们想为页面中所有的p标签click事件支持冒泡:

这种方式与第一种方案相比更加简洁,而且不必担心动态生成的新元素存在同样的问题。CSS规则会自动应用到页面中所有符合条件的元素上,让一切变得简单起来。

可以把上面的CSS规则写在CSS文件中,或者HTML页面的style标签里。如果需要用JavaScript动态插入到文档中,可以这样做:

结语

在本文中我们了解了iOSSafari在处理click事件时不能冒泡的bug,然后给出两种解决方案。

针对于静态页面或只有少数元素需要处理的情况,建议采用方案1。

如果你要处理的元素众多,或者很多元素是动态生成的,建议采用方案2。

最后是一个好消息,iOS13上的Safari浏览器已经解决了这个问题。所以如果你不需要兼容Safari的旧版本,可以忽略这篇文章。不过这个bug存在了近十年,了解一点苹果的黑历史也是一件有趣的事情。不是么:D

JavaScript权威指南(第6版)京东月销量好评率98%无理由退换京东配送官方店¥.6购买
1
查看完整版本: iOSSafari的click事件委托问