JS事件冒泡与捕获

JavaScript011

JS事件冒泡与捕获,第1张

        以click点击事件为例。假如我们有一个多层结构标签。如下图,是4个div嵌套。每个div都有点击的监听事件,分别输出1234。当我们点击最里面的div时,点击事件开始传递,传递的 全过程是1-2-3-4-4-3-2-1 。

前半部分,事件从最外面的父div依次传递到最里面的后代div, 1-2-3-4这部分我们叫捕获过程 。

之后事件又从最里层的后代div逐层传出, 4-3-2-1这部分我们叫冒泡过程 。

如果我把捕获监听器和冒泡监听器都加上,如下图这样。

这时我们有8个监听器了,在捕获过程和冒泡过程都有监听。输出如下图。

如果我去除几个监听器如下图。为了容易理解我调整了格式

仍然是按照箭头顺序,明显应该输出1423。

        在不使用任何框架的情况下,我们在js中通过addEventListener方法给Dom添加事件监听。这个方法有三个参数可以传递addEventListener(event,fn,useCapture)。event是事件类型click,focus,blur等;fn是事件触发时将执行的函数方法(function);第三个参数可以不传,默认是false,这个参数控制是否捕获触发。所以我们只穿两个参数时,这个事件是冒泡传递触发的,当第三个参数存在且为true时,事件是捕获传递触发的。

        使用框架时可使用对应的框架提供的方法。如上面我使用了Vue框架,通过事件装饰来区分捕获与冒泡。

        在不使用任何框架的情况下,我们在js中通过stopPropagation方法阻止事件继续传递。

        使用框架时可使用对应的框架提供的方法。接下来我将了Vue框架的stop修饰符来阻止事件传递。

        我们可以在传递过程中阻止事件继续传递,防止触发不需要的事件。

如图我在第三层捕获后阻止事件继续传递,那么后面的log方法不会继续执行。最终只输出1-2-3。

        我们在使用中多数情况下只使用冒泡监听。例如一条购物车信息,在这条信息中,右下角有一个删除按钮。点击这条消息可查看详情,点击删除按钮可将此商品移除。我们会分别给信息的div和删除button添加一个冒泡的click事件监听。如果不做阻止传递,点击删除button后,会显示商品详情。显然这不是我们想看到的。这时我们给button一个阻止事件传递的功能,点击删除按钮后,事件就会结束,就不再显示商品详情。

前端豆知识,很小却有用

首先,事件对象 event 是一个对象,包含着跟事件有关的所有信息,包括触发事件的 dom 以及 每种事件的一些特征。当dom操作触发某个事件时,会产生事件对象 event, 当事件被触发时产生,事件执行完毕时销毁。

获取鼠标在那个标签上 e.target

2.1. 事件捕获: 当时事件被触发的时候,系统会从 window 开始 依次向下遍历,遍历的过程中,遇到没有触发的相同事件便触发。

(window =>document =>html =>body =>祖先元素 =>父元素 =>目标元素)

2.2 事件触发: 直到找到触发这个事件的元素,触发元素绑定的事件方法

2.3 事件冒泡: 触发目标事件之后,然后会一层一层向上遍历,遍历的过程中,遇到没有触发的相同事件便触发。

直到返回 window 结束 这整个过程叫做事件流。

事件捕获 和 事件冒泡 同一事件流 只能有一个生效,JS中默认执行事件冒泡 (false)。

3.1 Dom 0级

写法: ele.on事件名 = function(){}

特点: js 与 html 完全分离 便于封装

缺点:同一个DOM对象 同时间只能绑定一个 同事件

    也就是说 一个 dom对象 只能执行一个事件处理函数

    如果给 这个事件 绑定了多个事件处理函数, 则执行最后一个事件处理函数

3.2 Dom 2级(事件监听)

写法:

    ele.addEventListener("事件类型",事件处理函数,bool)

    事件处理函数可以是: 匿名函数、 命名函数、 对象

    bool: 布尔值 (只有true 和 false) 不写默认为 false

    true: 触发 事件捕获   false:触发事件冒泡

特点:

    1.DOM 2级不会与DOM 0级事件冲突 同时存在时两个都执行

    2.可以绑定多个事件函数 不会起冲突

    Dom2级 可以删除命名函数 事件移除 removeEventListener

注意: 事件名中没有 on, on是DOM0级的写法

4.1 鼠标事件

 1. 单机事件 click

 2. 双击事件 dblclick

 3. 右键事件 contextmenu   默认事件:右击打开默认菜单栏

 4. 移入事件 mouseenter

 5. 移出事件 mouseoutmouseover

 6. 移动事件 mouseenter mouseleave

 7. 按下事件 mousedown

 8. 释放事件 mouseup

 9. 滚轮事件 mousewheel   e.wheelDelta>0 向上滑 <0 向下滑

4.2 键盘事件

 1. 键盘按下事件 只要按下就一直触发 keydown 获取键码: e.keyCode

 2. 键盘抬起事件keyup

4.3 UI事件(窗口事件)

 1. 资源加载完毕 load

 2. 窗口大小发生改变 resize

 3. 窗口滚动条 scroll

 4. 资源加载完毕 error

4.4 表单事件

 1. 获取焦点focus

 2. 失去焦点blur

 3. 正在输入input

 4. 内容发生改变 change

1.获取鼠标坐标:

 1. 在浏览器的可视窗口的坐标 --- e.clientX / e.clientY

 2. 在标签内的坐标 ----- e.offsetX / e.offsetY

 3. 在屏幕内的坐标 ----- e.screenX / e. screenY

 4. 在文档中内的坐标 -- e.pageX / e.pageY

2. 阻止默认事件:

如下一段代码:有2个div,外部是div1,内部是div2,

 <div id="div1" onclick="alert('1')">

         <div id="div2" onclick="alert('2')">

test

         </div>

     </div>

假设在div2内部进行点击,那么:冒泡事件认为:你点击了div2,而div2在div1里内部,所以,也点击了div1,因此执行顺序是 “div2-div1-body” ,由内往外一步步向上,这很像水里有一个鱼吐了一个水泡,这个水泡往上飘一样,所以叫做“冒泡事件”

而事件捕获认为:你点击div2,因为div1在div2外部,所以,div1先捕获到,然后再传递给div2,所以,事件执行顺序是“body-div1-div2”,这就像你进去房间,必须先进入大院,再进入门一样。

其实,2个都有一定的道理,目前,泡沫事件更合理,所以浏览器默认使用冒泡顺序,但是可以通过JS参数设置到底是用冒泡还是捕捉。