DOM的事件传递机制 | 您所在的位置:网站首页 › js冒泡事件和捕获事件技巧分析图解 › DOM的事件传递机制 |
DOM的事件传递机制 —— 捕获与冒泡
在用react写form表单的时候,看到代码里面有一句e.preventdafault(),因为不知道这个函数什么意思,就查了一下,才发现和Event事件有关系,并且也逃不掉事件的捕获与冒泡。为此,借此机会顺便了解学习了一些相关知识。 文章参考: https://blog.techbridge.cc/2017/07/15/javascript-event-propagation/https://yeungkc.com/2019/06/14/native-js-event/#preventDefault Demo Document google.com其DOM树基本结构如下: 在Event中,一个有三个阶段(Phase),分别是捕获、目标和冒泡。 而在Event的参数当中,也有参数eventPhase是用来专门表示这三个事件的,通过这个参数我们就可以知道事件处于哪个阶段了。 // Webside: // https://www.w3.org/TR/DOM-Level-2-Events/events.html#Events-interface // PhaseType const unsigned short CAPTURING_PHASE = 1; const unsigned short AT_TARGET = 2; const unsigned short BUBBLING_PHASE = 3;DOM在事件传递中,可以概括为三个阶段: 首先是从根节点出发,到达目标节点,若此过程中触发了事件,则被称为捕获(CAPTURING)事件;当节点到达定义点击事件的目标节点时,此时则处于目标(TARGET)阶段。而在目标节点触发事件后,最后就会一路往上回到根节点,此时若有事件发生,则是冒泡(BUBBLING)事件。附(W3C讲Event flow的图): 小总结: 先捕获后冒泡 如何决定当前处于捕获阶段还是冒泡阶段? 通过addEventListener这个函数定义事件的类型和监听阶段。 target.addEventListener(type, listener[, useCapture]); 其中,这个函数的第三个参数就是用来表示监听的阶段, 若true则表示处于捕获阶段;false表示冒泡阶段。 默认是冒泡阶段。 实际案例 // DOMEvent.js const get = (id) => document.getElementById(id); const list = get('list') const listItem = get('list-item') const listItemLink = get('list-item-link') // list的捕获 list.addEventListener('click', (e) => { console.log('我是list的捕获事件') }, true) // list的冒泡 list.addEventListener('click', (e) => { console.log('我是list的冒泡事件') }, false) // listItem的捕获 listItem.addEventListener('click', (e) => { console.log('我是listItem的捕获事件') }, true) // listItem的冒泡 listItem.addEventListener('click', (e) => { console.log('我是listItem的冒泡事件') }, false) // listItemLink的捕获 listItemLink.addEventListener('click', (e) => { console.log('我是listItemLink的捕获事件') }, true) // listItemLink的冒泡 listItemLink.addEventListener('click', (e) => { console.log('我是listItemLink的冒泡事件') }, false)通过Chrome开发者工具,可以看到结果如下: DOMEvent.js:16 我是list的捕获事件,phase value: 1 DOMEvent.js:26 我是listItem的捕获事件,phase value: 1 DOMEvent.js:36 我是listItemLink的捕获事件,phase value: 2 DOMEvent.js:41 我是listItemLink的冒泡事件,phase value: 2 DOMEvent.js:31 我是listItem的冒泡事件,phase value: 3 DOMEvent.js:21 我是list的冒泡事件,phase value: 3由此可以看出和刚刚的结论是符合的,先进行捕获事件,随后到达target,最后就从下往上走。但这里需要的是: 在目标事件这里,不管addEventListenter的第三个参数是true还是false,都没有先捕获后冒泡的说法,既这里会按顺序执行。 下面看看我们将顺序倒转之后代码和结果: // DOMEvent.js const get = (id) => document.getElementById(id); const list = get('list') const listItem = get('list-item') const listItemLink = get('list-item-link') // list的冒泡 list.addEventListener('click', (e) => { console.log('我是list的冒泡事件,phase value:', e.eventPhase) }, false) // list的捕获 list.addEventListener('click', (e) => { console.log('我是list的捕获事件,phase value:', e.eventPhase) }, true) // listItem的冒泡 listItem.addEventListener('click', (e) => { console.log('我是listItem的冒泡事件,phase value:', e.eventPhase) }, false) // listItem的捕获 listItem.addEventListener('click', (e) => { console.log('我是listItem的捕获事件,phase value:', e.eventPhase) }, true) // listItemLink的冒泡 listItemLink.addEventListener('click', (e) => { console.log('我是listItemLink的冒泡事件,phase value:', e.eventPhase) }, false) // listItemLink的捕获 listItemLink.addEventListener('click', (e) => { console.log('我是listItemLink的捕获事件,phase value:', e.eventPhase) }, true)结果: DOMEvent.js:21 我是list的捕获事件,phase value: 1 DOMEvent.js:31 我是listItem的捕获事件,phase value: 1 DOMEvent.js:36 我是listItemLink的冒泡事件,phase value: 2 DOMEvent.js:41 我是listItemLink的捕获事件,phase value: 2 DOMEvent.js:26 我是listItem的冒泡事件,phase value: 3 DOMEvent.js:16 我是list的冒泡事件,phase value: 3可以看到listItemLink部分会按照代码顺序执行。 因此,最后得出我们结论 先捕获,后冒泡当事件传递到target时,无捕获冒泡之分 stopPropagation、prenventDefault、return false stopPropagation()当你想要取消事件传递时,就可以选择使用这个函数。函数加在哪里,事件就会在哪里停止传递。 const get = (id) => document.getElementById(id); const list = get('list') const listItem = get('list-item') const listItemLink = get('list-item-link') // list的捕获 list.addEventListener('click', (e) => { console.log('我是list的捕获事件,phase value:', e.eventPhase) }, true) // list的冒泡 list.addEventListener('click', (e) => { console.log('我是list的冒泡事件,phase value:', e.eventPhase) }, false) // listItem的捕获 listItem.addEventListener('click', (e) => { // e.stopPropagation() console.log('我是listItem的捕获事件,phase value:', e.eventPhase) }, true) // listItem的冒泡 listItem.addEventListener('click', (e) => { // e.preventDefault() // 链接不跳转 e.stopPropagation() // 跳转还在,但不进行冒泡了 console.log('我是listItem的冒泡事件,phase value:', e.eventPhase) }, false) // listItemLink的捕获 listItemLink.addEventListener('click', (e) => { console.log('我是listItemLink的捕获事件,phase value:', e.eventPhase) }, true) // listItemLink的冒泡 listItemLink.addEventListener('click', (e) => { console.log('我是listItemLink的冒泡事件,phase value:', e.eventPhase) }, false)结果如下: DOMEvent.js:16 我是list的捕获事件,phase value: 1 DOMEvent.js:27 我是listItem的捕获事件,phase value: 1 DOMEvent.js:39 我是listItemLink的捕获事件,phase value: 2 DOMEvent.js:44 我是listItemLink的冒泡事件,phase value: 2 DOMEvent.js:34 我是listItem的冒泡事件,phase value: 3可以看到,当我在listItem的冒泡过程调用了这个函数之后,事件就没有再继续传递下去了。 但是这里需要注意,这里说的事件传递被停止,是指事件不会被传递到下一个节点,若在同一个节点中不止一个listener,还是会被执行的。 例如:在上述代码中添加这段代码: // listItem 冒泡事件2 listItem.addEventListener('click', (e) => { console.log('我是listItem的冒泡事件2 ,phase value:', e.eventPhase) })最终结果则是: DOMEvent.js:16 我是list的捕获事件,phase value: 1 DOMEvent.js:27 我是listItem的捕获事件,phase value: 1 DOMEvent.js:44 我是listItemLink的捕获事件,phase value: 2 DOMEvent.js:49 我是listItemLink的冒泡事件,phase value: 2 DOMEvent.js:34 我是listItem的冒泡事件,phase value: 3 DOMEvent.js:39 我是listItem的冒泡事件2 ,phase value: 3可以看到,另一个listener的监听事件还是会被正常执行。 假如你需要同一层级的其它listener也不会被执行,可以改用e.stopImmediatePropagation();这样,其余listener也会被禁止。 prenventDefault()它的作用是取消浏览器的预设行为,但是并不会对事件传递造成影响。但是一旦开始了prebentDefault()函数,在它往下传递的事件当中也会有效果。 例如:在listItem中添加prenventDefault(),可以看到此时a链接已经不能正常跳转了,但是,此时Chrome的输出栏里仍然可以看到这样的结果: DOMEvent.js:16 我是list的捕获事件,phase value: 1 DOMEvent.js:27 我是listItem的捕获事件,phase value: 1 DOMEvent.js:44 我是listItemLink的捕获事件,phase value: 2 DOMEvent.js:49 我是listItemLink的冒泡事件,phase value: 2 DOMEvent.js:34 我是listItem的冒泡事件,phase value: 3 DOMEvent.js:21 我是list的冒泡事件,phase value: 3但是这里我有一个疑问 —— 就是我是在listItem的冒泡阶段停止这个事件的,为什么还是不能跳转呢? 按理 #list (捕获) -> #listItem(捕获) -> listItemLink(捕获) -> listItemLink(冒泡) -> listItem(冒泡) -> list(冒泡),按这样的顺序进行,那么不应该也可以进行跳转吗? return false它可以看作前面两个函数的综合,当你调用它时,它既可以阻止传递同时还可以阻止默认行为。它在内部实际进行了三件事: event.preventDefault();event.preventDefault();停止回调函数执行并立即返回。 |
今日新闻 |
推荐新闻 |
专题文章 |
CopyRight 2018-2019 实验室设备网 版权所有 |