事件流

事件流分为两种:捕获事件流和冒泡事件流

  • 捕获事件流从根节点开始执行,一直往子节点查找执行,直到查找执行到目标节点
  • 冒泡事件流从目标节点开始执行,一直往父节点冒泡查找执行,直到查到到根节点
  • image.png
    • 事件流分为三个阶段:捕获阶段 -> 目标阶段 -> 冒泡阶段
    • DOM事件流:事件触发一般来说会按照上面的顺序进行,但是也有特例,如果给一个目标节点同时注册冒泡和捕获事件,事件触发会按照注册的顺序执行

“捕获”和“冒泡”执行顺序和事件的执行次数

  • 按照W3C标准的事件:首先进入捕获阶段,直到达到目标元素,再进入冒泡阶段
  • 事件执行次数(DOM2-addEventListener):元素上绑定事件的个数
    • 注意1:前提是事件被确实触发
    • 注意2:事件绑定几次就算几个事件,即使类型和功能完全一样也不会“覆盖”
  • 事件执行顺序:判断的关键是否目标元素
    • 非目标元素:根据W3C的标准执行:捕获->目标元素->冒泡(不依据事件绑定顺序)
    • 目标元素:依据事件绑定顺序:先绑定的事件先执行(不依据捕获冒泡标准)
    • 最终顺序:父元素捕获->目标元素事件1->目标元素事件2->子元素捕获->子元素冒泡->父元素冒泡
    • 注意:子元素事件执行前提 事件确实“落”到子元素布局区域上,而不是简单的具有嵌套关系

DOM0,DOM2,DOM3 事件处理方式

  • DOM0级事件处理方式:
    • btn.onclick = func;
    • btn.onclick = null;
  • DOM2级事件处理方式:
    • btn.addEventListener(‘click’, func, false);
    • btn.removeEventListener(‘click’, func, false);
    • btn.attachEvent(“onclick”, func);
    • btn.detachEvent(“onclick”, func);
  • DOM3级事件处理方式:
    • eventUtil.addListener(input, “textInput”, func);
    • eventUtil 是自定义对象,textInput 是DOM3级事件

注册事件

通常我们使用 addEventListener 注册事件

target.addEventListener(type, listener, options/useCapture);

事件代理

当一个元素接收到事件的时候,会把他接收到的事件传给自己的父级,一直到window,当然其传播的是事件,绑定的执行函数并不会传播,如果父级没有绑定事件函数,就算传递了事件,也不会有什么表现,但事件确实传递了。 事件冒泡的原因是事件源本身可能没有处理事件的能力,即处理事件的函数并未绑定在该事件源上。它本身并不能处理事件,所以需要将事件传播出去,从而能达到处理该事件的执行函数。

  • 事件代理(Event Delegation),又称之为事件委托。是 JavaScript 中常用绑定事件的常用技巧。顾名思义,“事件代理”即是把原本需要绑定的事件委托给父元素,让父元素担当事件监听的职务。事件代理的原理是DOM元素的事件冒泡。
  • 使用事件代理的好处是
    • 可以大量节省内存占用,减少事件注册,比如在table上代理所有td的click事件就非常棒
    • 可以将事件应用于动态添加的子元素上
  • 缺点: 使用不当会造成事件在不应该触发时触发

如果一个节点中的子节点是动态生成的,那么子节点需要注册事件的话应该注册在父节点上,示例

  1. <ul>
  2. <li>1</li>
  3. <li>2</li>
  4. <li>3</li>
  5. <li>4</li>
  6. <li>5</li>
  7. </ul>
  8. <script>
  9. let ul = document.querySelector('##ul')
  10. ul.addEventListener('click', (event) => {
  11. console.log(event.target);
  12. })
  13. </script>

W3C事件的 target 与 currentTarget 的区别?

  • target 只会出现在事件流的目标阶段
  • currentTarget 可能出现在事件流的任何阶段
  • 当事件流处在目标阶段时,二者的指向相同
  • 当事件流处于捕获或冒泡阶段时:currentTarget 指向当前事件活动的对象(一般为父级)

《JavaScript高级程序设计》对这两个属性是这样解释的
event.target为事件的目标
event.currentTarget为事件处理程序正在处理的元素

个人理解:
event.target: 获取触发事件的标签元素(比如:点击了哪个元素,就是哪个元素);
event.currentTarget: 获取发起事件的标签元素(比如:那个元素绑定了事件,就是那个元素

我们为一个元素绑定一个点击事件的时候,可以指定是要在捕获阶段绑定或者换在冒泡阶段绑定。 当addEventListener的第三个参数为true的时候,代表是在捕获阶段绑定,当第三个参数为false或者为空的时候,代表在冒泡阶段绑定。

示例;

  1. <div id="a">
  2. <div id="b">
  3. <div id="c">
  4. <div id="d"></div>
  5. </div>
  6. </div>
  7. </div>
  8. <script>
  9. document.getElementById('a').addEventListener('click', function(e) {
  10. console.log('aa', 'target:' + e.target.id + '&currentTarget:' + e.currentTarget.id);
  11. });
  12. document.getElementById('b').addEventListener('click', function(e) {
  13. console.log('bb', 'target:' + e.target.id + '&currentTarget:' + e.currentTarget.id);
  14. });
  15. document.getElementById('c').addEventListener('click', function(e) {
  16. console.log('cc', 'target:' + e.target.id + '&currentTarget:' + e.currentTarget.id);
  17. });
  18. document.getElementById('d').addEventListener('click', function(e) {
  19. console.log('dd', 'target:' + e.target.id + '&currentTarget:' + e.currentTarget.id);
  20. });

上面例子绑定是在冒泡阶段,如果我们点击了id 是 d 的元素输出下面的结果:
image.png
因为点击的是id = ‘d’ , 的元素,所以target 一直是 d,哪个元素绑定了事件 currentTarget 就是哪个元素。