目录
  1. 1. DOM 及 DOM 事件
  2. 2. DOM 事件级别
    1. 2.1. 内联事件
    2. 2.2. DOM0 事件
    3. 2.3. DOM2 事件
    4. 2.4. DOM3 事件
  3. 3. DOM 事件流
    1. 3.1. 事件捕获
    2. 3.2. 事件冒泡
    3. 3.3. 阻止捕获、冒泡、默认行为
      1. 3.3.1. 阻止捕获
      2. 3.3.2. 阻止冒泡
      3. 3.3.3. 阻止默认行为
  4. 4. 自定义事件
关于DOM事件的那些事 - 前端随笔

DOM 及 DOM 事件

DOM(Document Object Model——文档对象模型)是用来呈现以及与任意 HTML 或 XML 文档交互的 API。DOM 是载入到浏览器中的文档模型,以节点树的形式来表现文档,每个节点代表文档的构成部分(例如:页面元素、字符串或注释等等)。

DOM 事件 则是被用于通知代码相关事件已经发生。每个事件都是继承自Event对象,可以包括自定义的成员属性及函数用于获取事件发生时相关的更多信息。

DOM树

DOM 事件级别

DOM 标准和 DOM 事件级别

DOM 标准 DOM 事件
DOM0 DOM0
DOM1
DOM2 DOM2
DOM3 DOM3

DOM1 标准没有定义事件相关内容,所以不存在 1 级 DOM 事件模型。


内联事件

在 DOM0 之前,事件处理是通过在 HTML 标签内添加 on 事件绑定函数,也是最早的一种事件处理方式;

1
<button type="button" onclick="show()"></button>
1
2
3
function show() {
console.log('Hello World!')
}

以上就是直接在 button 标签内直接添加 onclick 属性 onclick="show()"来触发show()函数,缺点就是 HTML 和 JS 耦合太强,修改绑定函数名就必须修改两个地方。


DOM0 事件

DOM0 事件通过 js 获取 html 标签元素,将函数赋值给事件属性。

1
2
3
4
var btn = document.getElementById('btn')
btn.onclick = function () {
console.log('Hello World!')
}

缺点:无法同时绑定多个处理函数;
解绑可通过给事件属性赋值:btn.onclick = null;;


DOM2 事件

DOM2 级事件定义了 addEventListenerremoveEventListener 两个方法,分别用来绑定和解绑事件。

绑定事件:addEventListener方法中包含三个参数,语法:

target.addEventListener( type, listener[, useCapture] );
target.addEventListener( type, listener[, options] );

  • type: String,事件类型(click, mousedown…);
  • listener: 监听事件触发时执行函数;
  • useCapture: Boolean,可选,默认false,是否捕获阶段执行,不添加默认冒泡阶段执行。
  • opthins: Object,可选,第三个参数还可设置成参数对象,可用选项如下:
    • capture: Boolean,是否捕获阶段执行;
    • once: Boolean,是否只调用一次;
    • passive: Boolean,设置为 true 时,表示 listener 永远不会调用 preventDefault()。如果 listener 仍然调用了这个函数,客户端将会忽略它并抛出一个控制台警告。

解绑事件:removeEventListener同绑定事件一样,⚠️ 注意,解绑时三个参数都要相同才能解绑,尤其是第三个参数,执行阶段不同是不同的 Listener ,所以参数要一致。

1
2
3
4
5
6
7
8
9
var btn = document.getElementById('btn')
btn.addEventListener('click', show, true)

function show() {
console.log('Hello World')
}

btn.removeEventListener('click', show, true)
// 注意,像这里第三个参数如果不设置默认就是false,参数不一致,是解绑不了的。

DOM3 事件

DOM3 级事件在 DOM2 级事件的基础上添加了更多的事件类型:

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

同时 DOM3 级事件也允许使用者自定义一些事件。


DOM 事件流

DOM 事件流分三个阶段:捕获阶段 => 目标阶段 => 冒泡阶段

Dom 事件流

事件捕获

当某事件触发时,会依照 DOM 树从上层依次往下通知 listener,并执行相应监听函数,比如父元素和目标元素都注册了点击事件,且都设置了捕获阶段执行,这时目标元素被点击,则父元素的执行函数会先执行,再来执行目标元素  函数;

事件捕获流程:
window => document => html => body => … => 目标元素

事件冒泡

事件冒泡则与捕获阶段相反,先执行目标元素事件再逐层往上。

阻止捕获、冒泡、默认行为

event.stopPropagation();: 阻止捕获、冒泡阶段。
event.preventDefault();: 阻止默认行为。
event.stopImmediatePropagation(): 同样可以阻止捕获、冒泡,并阻止相同事件的其它监听函数;

备注:如果有多个相同类型事件的事件监听函数绑定到同一个元素,当该类型的事件触发时,它们会按照被添加的顺序执行。如果其中某个监听函数执行了 event.stopImmediatePropagation() 方法,则当前元素剩下的监听函数将不会被执行。

阻止捕获

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
window.addEventListener(
'click',
function () {
console.log('window click!')
event.stopPropagation()
},
true
)

document.addEventListener(
'click',
function () {
console.log('document click!')
},
true
)

如上,windowdocument都注册了点击事件,并设置了在捕获阶段执行,正常执行应该是先执行 window 的点击事件再执行 document 的点击事件,在 window 执行后添加了event.stopPropagation();就直接停止了捕获,所以 document 的点击事件不会被执行。

注意,一般不会这么操作,如上,如果父元素执行完直接停止捕获,连同后续的冒泡阶段都停止,会导致真正被点击的目标元素点击事件不能执行。

阻止冒泡

实际环境中一般绑定事件都只会设置冒泡阶段执行,当遇到父元素和子元素都绑定相同事件时,为避免子元素触发事件时触发父元素事件,才会去阻止冒泡。如下:

1
2
3
4
5
6
7
8
parentElement.addEventListener('click', function () {
console.log('parentElement click!')
})

childElement.addEventListener('click', function () {
console.log('parentElement click!')
event.stopPropagation()
})

阻止默认行为

<a>标签或表单提交,点击会跳转链接,有时我们并不需要跳转或暂时不需要跳转,只需要执行相应操作即可,这时就需要阻止默认行为,event.preventDefault();

1
2
3
4
<form action="test.html">
用户名:<input type="text" id="username" />
<input type="submit" value="提交" id="sub" />
</form>
1
2
3
4
5
6
7
8
9
var userName = document.querySelector('#username')
var sub = document.querySelector('#sub')

sub.addEventListener('click', function () {
if (userName.value == '') {
alert('用户名不能为空!')
event.preventDefault() // 阻止默认行为
}
})

自定义事件

使用 Event 构造函数创建自定义事件如下:

1
2
3
4
5
6
7
8
// 创建自定义事件
var event = new Event('build');

// 绑定事件
elem.addEventListener('build', function (e) { ... });

// 触发事件
elem.dispatchEvent(event);

注意这里的 event 是事件,build 是事件名,绑定和触发并不是用同一个。


文章作者: Vincent F0ng
文章链接: https://vincef0ng.cn/post/javascript-dom/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 Vincent F0ng
请喝奶茶
  • 微信
  • 支付宝
领红包

评论(支持Markdown)