浏览器知识点整理(二)事件机制

这是我参与更文挑战的第15天,活动详情查看: 更文挑战

认识 DOM

DOM,即文档对象模型(Document Object Model),W3C 制定的标准接口规范,是一种处理 HTML 和 XML 文件的标准 API。它把文档作为一个树形结构,树的每个结点表示了一个 HTML 标签或标签内的文本项

DOM 树结构精确地描述了 HTML 文档中标签间的相互关联性。将 HTML 或 XML 文档转化为 DOM 树的过程称为解析(parse

HTML 文档被解析后,转化为 DOM 树,因此对 HTML 文档的处理可以通过对 DOM 树的操作实现。DOM 模型不仅描述了文档的结构,还定义了结点对象的行为,利用对象的方法和属性,可以方便地访问、修改、添加和删除DOM 树的结点和内容

可以试着在浏览器把一个 DOM 节点输出来看一下:

let body = document.body;
for (var key in obj) {
    console.log(`属性:${key},值:${body[key]}`)
}
复制代码

这个节点有很多属性,其中 onclick 的默认值是 null,当把 onclick 赋予一个函数,就可以作为一个事件函数存在。我们可以通过给对应的属性绑定事件来修改、添加和删除DOM 树的结点和内容。

image.png

DOM 元素的查找

通过 JavaScript 查找 DOM 元素有以下几种方法:

  • 通过 id 查找 DOM 元素
  • 通过标签名查找 DOM 元素
  • 通过类名查找 DOM 元素
  • 通过 CSS 选择器查找 DOM 元素
// 通过 id 查找 DOM 元素
let id = document.getElementById("id"); // 查找 id="id" 的元素
// 通过标签名查找 DOM 元素
let p = document.getElementsByTagName("p"); // 查找所有 <p> 元素
// 通过类名查找 DOM 元素
let cls = document.getElementsByClassName("cls"); // 返回包含 class="cls" 的所有元素的列表
// 通过 CSS 选择器查找 DOM 元素
let pCls = document.querySelectorAll("p.cls"); // 返回 class="cls" 的所有 <p> 元素列表
复制代码

事件触发三阶段

  • 事件捕获阶段window 往事件触发处传播,遇到注册的捕获事件会触发
  • 处于目标阶段:传播到事件触发处时触发注册的事件
  • 事件冒泡阶段:从事件触发处往 window 传播,遇到注册的冒泡事件会触发

首先发生的是事件捕获为截获事件提供机会,然后的是实际的目标接收事件,最后一个阶段是事件冒泡阶段,可以在这个阶段对事件做出响应。
如图:

image.png

假设在 DOM 结构里面有 text 的这样一个标签,给这个标签绑定了一个点击事件,那么在点击这个标签的时候是怎么执行事件的呢?

  • 首先是事件捕获阶段,会通过 windowdocumentbodydivtext 这样的顺序一直往下捕获事件。
  • 然后是处于目标阶段,到 text 标签处触发绑定的点击事件。
  • 最后是事件冒泡阶段,事件是在冒泡阶段做出响应的。冒泡阶段通过 textdivbodydocumentwindow 这样的顺序往上冒泡,假如在 div 或者 body 上面也绑定了对应的 onclick 事件,那么会按顺序触发响应。

事件触发一般来说会按照上面的顺序进行,但是也会有特例,如果给一个 body 中的子节点同时注册冒泡和捕获事件,事件触发会按照注册的顺序执行

// 以下会按注册的顺序先打印冒泡事件然后是捕获事件
el.addEventListener('click', event => { console.log('冒泡事件') }, false)
el.addEventListener('click', event => { console.log('捕获事件') }, true)
复制代码

事件注册

我们一般使用 addEventListener 来注册事件,它接受三个参数:

  • 处理的事件名称,如点击事件 click
  • 事件处理程序,即要绑定的函数体;
  • 指定是在事件冒泡还是事件捕获阶段处理参数,可以是布尔值,也可以是对象
    • true 则作为捕获事件处理;
    • false 则作为冒泡事件处理(默认)。

第三个参数是对象时,可以使用以下几个属性:

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

一般来说,我们可以通过使用 stopPropagation 来阻止事件的进一步传播,即阻止事件冒泡。

e.stopPropagation(); // 阻止冒泡
e.stopImmediatePropagation(); // 阻止冒泡 + 捕获
复制代码

事件代理

事件代理是指利用事件冒泡,只指定一个事件处理程序来管理某一类型的所有事件

如果一个节点中的子节点是动态生成的,那么子节点需要注册事件的话可以注册在父节点上。

<ul id="proxy">
  <li>主页</li>
  <li>文章</li>
  <li>公告</li>
  <li>简介</li>
</ul>
复制代码

事件代理:

let proxy = document.querySelector('#proxy')
proxy.addEventListener('click', (event) => {
  let target = event.target; // 当前点击的元素
  if (target.nodeName.toLowerCase() == 'li') {
    console.log('click:' + target.innerHTML);
  }
})
复制代码

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

  • 可以减少内存占用,减少事件注册
  • 不需要给子节点注销事件

总结

  • DOM 是一个树形结构,树的每个结点表示了一个 HTML 标签或标签内的文本项。
  • 事件触发有三个阶段,分别是事件捕获阶段、处于目标阶段、事件冒泡阶段
  • 可以通过 addEventListener 来注册事件。
  • 通过利用事件冒泡来实现事件代理,可以提高性能。
© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享