前端学习的东西有很多,现代前端开发,前端工程化的东西要懂,基础的原生 js 也要懂,毕竟,框架都是有生命周期的,更替非常快,然而却有这么一个框架,它是最轻量的前端框架,每个浏览器都内置,它叫 vanilla.js。好吧,其实 vanilla.js 就是原生 js,不过是网上的一个玩笑而已,但是却能说明一个很重要的问题,就是原生 js 很重要,所以这部分文章是关于前端开发中原生 js 的一系列问题的,这篇谈一谈 DOM 事件。

DOM 事件级别

DOM 分四个级别,一级,二级,三级,没有零级但是通常把 DOM1 规范形成之前的称为 DOM0。而由于 1 级 DOM 标准中并没有定义事件相关的内容,所以 DOM 事件级别只包括 DOM0 级,DOM2 级和 DOM3 级三种。

首先来看不需要操控 DOM 的事件

  1. <button type="button" onclick="log()"></button>
  2. <script>
  3. function log() {
  4. console.log('Hello World');
  5. }
  6. </script>

这段代码大家肯定都见过,不需要控制 DOM,事件处理时间的函数直接写在 html 属性中。当然实际开发中应该没有人这样写了,理由也很简单,html 和 js 强耦合,无论是编写还是维护都没有任何好处,于是就有了 DOM 事件处理。

DOM0 级事件

同样以上面的程序为例,使用 DOM0 事件处理就是下面的样子

  1. <button id="btn" type="button"></button>
  2. <script>
  3. var btn = document.getElementById('btn');
  4. btn.onclick = function() {
  5. console.log('Hello World');
  6. }
  7. </script>

同样很简单,前端开发者一定都不陌生,DOM0 事件定义需要两部,先找到 DOM 节点,然后把处理函数赋值给该节点对象的事件属性。如果想解除事件,那么只要把 null 赋值给事件属性即可。DOM0 级事件无法给一个事件添加多个处理函数,

DOM2 级事件

上面的程序使用 DOM2 级事件处理就是这样的

  1. <button id="btn" type="button"></button>
  2. <script>
  3. var btn = document.getElementById('btn');
  4. function log() {
  5. console.log('Hello World');
  6. }
  7. btn.addEventListener('click', log, false);
  8. </script>

DOM2 级事件使用 addEventListener,里面有三个参数,第一个参数是事件名,就是事件属性去掉 on,第二个参数是事件处理函数,第三个参数是是否在事件捕获阶段执行(关于事件冒泡和事件捕获下面会介绍)。使用 DOM2 事件可以随意添加多个处理函数,移除 DOM2 事件要用 removeEventListener,传入的三个参数与添加事件完全相同。特别的旧版本 IE 浏览器(IE8 及一下),需要使用 attachEvent 和 detachEvent 来添加和移除事件, 传入两个参数第一个是事件属性(包含 on),第二个是处理函数,不支持事件捕获所以没有第三个参数。

DOM3 级事件

DOM3 级事件就是在 DOM2 基础上增加了更多的事件类型

  • UI 事件,当用户与页面上的元素交互时触发,如:load、scroll
  • 焦点事件,当元素获得或失去焦点时触发,如:blur、focus
  • 鼠标事件,当用户通过鼠标在页面执行操作时触发如:dbclick、mouseup
  • 滚轮事件,当使用鼠标滚轮或类似设备时触发,如:mousewheel
  • 文本事件,当在文档中输入文本时触发,如:textInput
  • 键盘事件,当用户通过键盘在页面上执行操作时触发,如:keydown、keypress
  • 合成事件,当为 IME(输入法编辑器)输入字符时触发,如:compositionstart
  • 变动事件,当底层 DOM 结构发生变化时触发,如:DOMsubtreeModified

DOM 事件级别的发展使得事件处理更加完整丰富,而下一个问题就是之前提到的事件冒泡和事件捕获。

事件冒泡和事件捕获

有以下 HTML 结构

  1. <html>
  2. <body>
  3. <div>
  4. <span>
  5. 我是目标内容
  6. </span>
  7. </div>
  8. </body>
  9. </html>

现在给最里面的目标内容绑定事件,就会有一个从事件源和目标之间的事件流,此例中,事件流的方向为 window -> document -> html -> body -> div -> span -> 目标 -> span -> div -> body -> html -> document -> window , 整个事件流分为两个部分,以事件目标为界限,从 window 到目标这个过程为事件捕获,从目标回到 window 的过程叫事件冒泡。如图所示:

原生js之DOM事件相关 - 图1

事件默认的处理阶段为冒泡阶段,可以把 addEventListener 第三个参数设置为 true 来让时间在捕获阶段被处理,不过通常不建议这样做。实际开发中,经常会利用到事件冒泡,也经常需要阻止事件冒泡,这就涉及到事件对象 event 的相关内置方法和属性了。

event 对象

事件处理函数会回调一个参数 event,代表当前事件对象,event 中有很多常用的方法和属性

  • preventDefault 阻止默认行为,比如当点击 submit 按钮时候,可以采用此方法阻止表单提交。
  • stopPropagation 停止事件冒泡,需要防止事件冒泡带来的负面影响的时候就要使用该方法。
  • stopImmediatePropagation 阻止后续事件,该方法除了阻止事件冒泡外在当前事件被绑定多个处理程序的时候,后续的处理程序也会被阻止。
  • currentTarget 此属性返回当前事件所绑定的对象。
  • target 此属性返回当前触发事件的对象,注意 target 是触发事件的对象,是真正的事件源,同样以上面的 HTML 为例,给 div 绑定一个事件,点击带文字的 span 后,target 是 span,而 currentTarget 是 div。

事件冒泡和 target 属性能做很多事情,比如考虑下面的结构

  1. <ul id="click">
  2. <li>1</li>
  3. <li>2</li>
  4. <li>3</li>
  5. <li>4</li>
  6. </ul>

如果想要实现点击每个 li 标签就能打印出文本内容,我们可以不用给每个 li 绑定事件,只需要利用事件冒泡即可

  1. var click = document.getElementById('click');
  2. click.addEventListener('click', log, false);
  3. function log(e) {
  4. console.log(e.target.innerText);
  5. }

自定义事件

除了系统内置的事件外,我们还可以自定义事件,由于平时使用的不多可能感觉会很高端,其实自定义事件并不复杂

  1. var myEvent = new Event('myEvent');
  2. document.addEventListener('myEvent', log, false);
  3. function log() {
  4. console.log('hello event');
  5. }
  6. document.dispatchEvent(myEvent);

通过创建 Event 对象来创建事件,通过 dispatchEvent 函数派发事件。自定义事件可以绑定到任意 DOM 元素上,此处选择 document 只是为了演示方便。


以上就是关于 DOM 事件的相关内容总结,接下来后面还会有其他技术的相关文章。

个人博客

隋堤倦客fx109138.github.io原生js之DOM事件相关 - 图2

https://zhuanlan.zhihu.com/p/32985312