浏览器事件机制


作者:Seiya

时间:2019年09月02日


事件触发三阶段


  • window 往事件触发处传播,遇到注册的捕获事件会触发(事件捕获);

  • 传播到事件触发处时触发注册的事件;

  • 从事件触发处往 window 传播,遇到注册的冒泡事件会触发(事件冒泡);





事件注册


通常我们使用 addEventListener 注册事件,该函数的第三个参数可以是布尔值,也可以是对象。对于布尔值 useCapture 参数来说,该参数默认值为 false ,useCapture 决定了注册的事件是捕获事件还是冒泡事件。对于对象参数来说,可以使用以下几个属性:

  • capture:布尔值,和 useCapture 作用一样;

  • once:布尔值,值为 true 表示该回调只会调用一次,调用后会移除监听;

  • passive:布尔值,表示永远不会调用 preventDefault


一般来说,如果我们只希望事件只触发在目标上,这时候可以使用 stopPropagation 来阻止事件的进一步传播。通常我们认为 stopPropagation 是用来阻止事件冒泡的,其实该函数也可以阻止捕获事件。stopImmediatePropagation 同样也能实现阻止事件,但是还能阻止该事件目标执行别的注册事件。

node.addEventListener(
  'click',
  event => {
    event.stopImmediatePropagation()
    console.log('冒泡')
  },
  false
)
// 点击 node 只会执行上面的函数,该函数不会执行
node.addEventListener(
  'click',
  event => {
    console.log('捕获 ')
  },
  true
)
<div class="md-section-divider"></div>




事件代理


有时候我们需要给 不存在的(可能将来会有)的一段 DOM 元素绑定事件,比如给一段 Ajax 请求完成后渲染的 DOM 节点绑定事件。

一般绑定的逻辑会在渲染前执行,绑定的时候找不到元素所以并不能成功,当然你也可以把绑定事件的代码放在 Ajax 请求之后。这样做在一些事件逻辑简单的应用里面没问题,但是会加重数据渲染逻辑和事件处理的逻辑耦合。一但事件处理程序特别多的时候,我们通常建议把事件的逻辑和其它代码逻辑分离,这样方便维护。


为了解决这个问题,我们通常使用事件代理/委托(Event Delegation )。而且通常来说使用 事件代理的性能会比单独绑定事件高很多,我们来看个例子:

<ul id="ul">
  <li>1</li>
  <li>2</li>
  <li>3</li>
  <li>4</li>
  <li>5</li>
</ul>
<script>
  let ul = document.querySelector('#ul')
  ul.addEventListener('click', (event) => {
      console.log(event.target);
  })
</script>

<div class="md-section-divider"></div>

事件代理的方式相较于直接给目标注册事件来说,有以下优点:

  • 节省内存

  • 不需要给子节点注销事件

最后更新时间: 2019-9-2 11:36:33