遇到的问题
前两天在回顾埋点监控项目的时候,突然记起一个问题。就是怎么监控全局点击事件问题,当时的做法是在document上通过冒泡的形式在这里拦截所有的点击事件,然后上报。后来遇到有人阻止冒泡。。。当时这个项目不是我写的,我也没有多想。然后这两天就突然记起来了,怎么也想不出来解决办法。就想着还是翻翻红宝书吧。然后就解决了。真是学艺不精。其实很简单,既然它阻止冒泡,我们就在捕获阶段拦截点击事件。
事件流
直接照搬红宝书上的例子: 在一张纸上画几个同心圆。把手指放到圆心上,则手指不仅是在一个圆圈里,而且是在所有的圆圈里。问题来了,你说他是指着最大的圆呢还是最小的圆呢,亦或者中间的圆圈。放到页面上则是,如果三个嵌套的div(div1,div2,div3),点div3,你说到底是点在div3上了,还是div2或者div1呢。如果都点上了,那点击的顺序呢?是从1=>3 还是 3=>1。
<div>
1
<div>
2
<div>3</div>
</div>
</div>
事件流就是描述页面接受事件顺序的。而对于这个顺序,IE和Netscape刚好都有自己的理解。
- IE事件流(事件冒泡)IE认为事件触发的顺序应该是从最具体的元素(目标元素)开始触发,然后向上传播至没有那么具体的元素(文档)
-
Netscape事件(流事件捕获)意思是从最不具体的节点应该先收到事件,而最具体的节点应该最后收到事件。事件捕获实际上是为了在事件到达最终目标前拦截事件。事件捕获得到了所有现代浏览器的支持,所有浏览器都是从window对象开始捕获事件,而DOM2 Events规范规定的是从document开始
由于旧版本浏览器不支持事件捕获,因此实际当中几乎不会使用事件捕获,通常都是使用事件冒泡
DOM 事件流
DOM2 Events 规范规定事件流分为3个阶段: 事件捕获,到达目标和事件冒泡。事件捕获最先发生,为提前拦截事件提供了可能。然后是目标元素接收到事件,最后阶段是冒泡阶段,最迟要在这个阶段响应事件。
所有浏览器都支持DOM事件流,只有IE8及更早版本不支持
事件处理程序
DOM2 Events为事件处理程序新增了两个方法:addEventListener()和 removeEventListener()。他们接受三个参数
- 事件名,2,事件处理函数,3,一个布尔值,true表示捕获阶段调用事件处理程序,false(默认值)表示在冒泡阶段调用事件处理程序。
回到开头说到的问题
因为是在document拦截所有的点击,所有当有元素进行了阻止冒泡,document上就拦截不到了
box3.onclick = function (e) {
// 使用了阻止冒泡,document自然就接受不到冒泡阶段的了
e.stopPropagation()
console.log('===>', 3, e);
}
// 所以我们只要让document在捕获阶段拦截就行了
document.addEventListener('click', (e) => {
console.log('document===>', e)
}, true)