事件流
事件流一般分为三个部分,以下解析均在DOM level 2事件下,DOM leve 0不支持捕获
先来解释一下addEventListener()
这个方法:
当我们使用addEventListener添加事件时,该方法有四个参数,分别为:
- 事件类型
- listener callback
- options 可选
一个指定有关 listener 属性的可选参数对象。可用的选项如下:
capture: Boolean,表示 listener 会在该类型的事件捕获阶段传播到该 EventTarget 时触发。
once: Boolean,表示 listener 在添加之后最多只调用一次。如果是 true, listener 会在其被调用之后自动移除。
passive: Boolean,表示 listener 永远不会调用 preventDefault()。如果 listener 仍然调用了这个函数,客户端将会忽略它并抛出一个控制台警告。
mozSystemGroup: 只能在 XBL 或者是 Firefox’ chrome 使用,这是个 Boolean,表示 listener 被添加到 system group。 - boolean 可选
该参数就是我们此次关注的重点;
整个事件流过程:
-
捕获阶段
当我们在addEventListener
的参数中添加了true
,如下:// $0是父节点 $0.addEventListener('click', (e) => {console.log('捕获click')}, true);
此时这个事件会在捕获过程被响应
-
目标阶段
该阶段即相应目标节点事件
-
冒泡阶段
// 该参数默认是false; // $0是父节点; // 可以在这里使用 stopPropeagation()阻止继续冒泡,也可以使用preventDefault()阻止默认事件; // 也可以直接 return false; 冒泡和默认事件都会被阻止; $0.addEventListener('click', (e) => {console.log('冒泡click')});
改变捕获和冒泡的顺序
这是一个面试题,主要想考核的其实是你对于js异步的了解和掌握,下一篇会写一个关于事件循环的,把这个弄懂了,这种问题就很好解决了
document.body.addEventListener('click', (e) => {
setTimeout(() => {
console.log('异步 event catch 事件捕获');
}, 0)
}, true)
document.body.addEventListener('click', (e) => {
console.log('event bubble 事件冒泡');
})
结果如下:
未异步结果如下:
应用场景
虽然事件流有这三个过程,但是我们应用场景最多的还是利用冒泡来实现的时间代理或者叫事件委托;即父节点定义事件,通过e.target来找到目标节点实现相应的效果;最常见的就是ul
上定义事件,代理后面所有的li
;
那如果我们只想获取定义事件的节点信息怎么办呢,除了e.target
还有个e.currentTarget
,此属性指向的就是定义了事件的节点。但是当我们打印出来的话可能会出现如下这中情况:
但是当我们直接打印它的话,我们会发现其实是有值的,在Stack Overflow里找到了很形象的解释:
var foo = { };
for (var i = 0; i < 100; i++) {
foo['longprefix' + i] = i;
}
console.log(foo);
foo.longprefix90 = 'abc';
上面这个例子中,虽然在调用console.log(foo)
的时候foo.longprefix90
应该是90
,但是当你在console
里浏览object
的时候你会看到foo.longprefix90
是abc
。
这是JavaScript console
在log
一个对象的机制造成的。log
没有包含对象的所有属性,它只包含了对这个对象的引用。当你点击展开时,他才会给你找那个对象的属性。
所以,之所以为null
的情况是,当调用console.log(e)
时,currentTarget
属性是有值的,但是过后这个值就被重置为null
了。所以当你展开事件对象,看到的就是null
。