Skip to content

JS 的事件机制

不是事件循环机制,是事件机制

通俗一点的概念是指的是DOM的事件绑定,触发吗?

事件的触发过程是怎么样的?什么是事件代理?

刚被问的时候有点懵,确认了下是不是问关于事件冒泡和捕获方面的知识。 我举了一个点击事件传递的例子,从root往事件触发处传播,遇到注册的捕获事件会触发,传播到事件触发处时触发注册的事件 从事件触发处往root传播,遇到注册的冒泡事件会触发

如果给一个目标节点(触发节点)同时注册冒泡和捕获事件,事件触发会按照注册的顺序执行

html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Demo</title>
</head>
<body>
  <div id="root">root
      <p id="parent">parent
          <span id="child">child</span>
      </p>
  </div>
  <script>
    let parentNode = document.getElementById('parent')
    // 因为触发标签不同,导致输出顺序不同
    // 点击parent会输出冒泡,捕获,因为目标节点按照绑定顺序输出
    // 点击child会输出捕获,冒泡,因为非目标节点正常按照浏览器事件机制执行
    parentNode.addEventListener(
      'click',
      event => {
        console.log('冒泡')
      },
      false
    )
    parentNode.addEventListener(
      'click',
      event => {
        console.log('捕获 ')
      },
      true
    )
  </script>
</body>
</html>

事件绑定(注册)

通常我们使用 addEventListener -MDN 注册事件

该函数的第三个参数可以是布尔值,也可以是对象

对于布尔值 useCapture 参数来说,默认值为 falseuseCapture 决定了注册的事件是捕获事件还是冒泡事件

对于对象参数来说,可以使用以下几个属性

  • capture:布尔值,和 useCapture 作用一样
  • once:布尔值,值为 true 表示该回调只会调用一次,调用后会移除监听
  • passive:布尔值,表示永远不会调用 preventDefault

如果我们希望事件只触发在目标上,可以使用 stopPropagation -MDN 来阻止事件的进一步传播

stopPropagation 用来阻止事件冒泡,也可以阻止捕获事件

stopImmediatePropagation -MDN 同样也能实现阻止事件 还能阻止该事件目标执行别的注册事件

js
dom.addEventListener(
  'click',
  event => {
    event.stopImmediatePropagation()
    console.log('冒泡')
  },
  false
)
// 点击 dom 只会执行上面的函数,该函数不会执行
dom.addEventListener(
  'click',
  event => {
    console.log('捕获 ')
  },
  true
)

事件触发的过程

  • window 往事件触发处传播,遇到注册的捕获事件会触发
  • 传播到事件触发处时触发注册的事件
  • 从事件触发处往 window 传播,遇到注册的冒泡事件会触发
js
// 以下会先打印冒泡然后是捕获
dom.addEventListener(
  'click',
  event => {
    console.log('冒泡')
  },
  false // 默认值false, true则捕获阶段传播到该 EventTarget 时触发
)
dom.addEventListener(
  'click',
  event => {
    console.log('捕获')
  },
  true
)

事件代理

如果一个节点中的子节点是动态(遍历)生成的,想给子节点注册事件可以注册到父节点上

html
<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>

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

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