事件

121 阅读3分钟

遇到的问题

前两天在回顾埋点监控项目的时候,突然记起一个问题。就是怎么监控全局点击事件问题,当时的做法是在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认为事件触发的顺序应该是从最具体的元素(目标元素)开始触发,然后向上传播至没有那么具体的元素(文档)

image.png

  • Netscape事件(流事件捕获)意思是从最不具体的节点应该先收到事件,而最具体的节点应该最后收到事件。事件捕获实际上是为了在事件到达最终目标前拦截事件。事件捕获得到了所有现代浏览器的支持,所有浏览器都是从window对象开始捕获事件,而DOM2 Events规范规定的是从document开始

image.png 由于旧版本浏览器不支持事件捕获,因此实际当中几乎不会使用事件捕获,通常都是使用事件冒泡

DOM 事件流

DOM2 Events 规范规定事件流分为3个阶段: 事件捕获,到达目标和事件冒泡。事件捕获最先发生,为提前拦截事件提供了可能。然后是目标元素接收到事件,最后阶段是冒泡阶段,最迟要在这个阶段响应事件。

image.png

所有浏览器都支持DOM事件流,只有IE8及更早版本不支持

事件处理程序

DOM2 Events为事件处理程序新增了两个方法:addEventListener()和 removeEventListener()。他们接受三个参数

  1. 事件名,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)