废话不多说,直接开始我的笔记吧~
DOM事件级别
DOM级别一共有四个级别,分为: DOM0级、DOM1级、DOM2级以及DOM3级。
DOM事件分为三个级别: DOM0级事件处理、DOM2级事件处理、DOM3级事件处理。
那为什么没有DOM1级事件处理呢? 因为DOM1级里面没有相关定义。 别问我怎么知道的,因为我也只知道这么多~
DOM0级事件
element.onclick = function(){}
话不多说,先上代码
<button id="btn">点击</button>
<script>
const btn = document.getElementById('btn')
btn.onclick = () => {
alert('你好')
}
// btn.onclick = null用来解绑
</script>
在HTML事件处理程序中,onclick事件绑定在button标签中,这样的话如果修改函数名就需要修改至少两处,代码量大了,也就不方便修改。
如上代码中为DOM0级事件处理,通过操作DOM来完成事件的绑定。
那么什么是DOM0级处理事件呢? 它就是将一个函数赋值给事件处理属性,如上面栗子中js通过获取id按钮,将函数赋值给onclick这个处理事件。
DOM0级事件的缺点在于它不能同时绑定多个相同事件,如果绑定多个,则最新绑定的这个事件会覆盖前面一个,在这里我就不多展示了,感兴趣的可以自己试试。
DOM2级事件
element.addEventListener('click',function(){},false)
<button id="btn">点击</button>
<script>
const btn = document.getElementById('btn')
btn.addEventListener('click', () => {
alert('你好')
}, false)
btn.addEventListener('click', () => {
alert('我不好')
}, false)
// btn.removeEventListener('click', () => {}, false); 解绑事件
</script>
如代码所使,DOM2级事解决了在DOM0级不能同时绑定多个相同处理事件的缺点,如代码所示,点击按钮后,会按照代码顺序,先执行你好,再执行我不好。
DOM2级事件中处理函数包含了三个参数: 绑定事件处理属性名称(不需要加on)、处理函数、是否是事件捕获。
IE8以下的版本不支持addEventListener,需要用attachEvent并且不需要第三个参数,因为IE8以下版本只支持
冒泡事件。
DOM3级事件
element.addEventListener('keyup',function(){},false)
DOM3级事件就是在DOM2基础上增加了一些事件类型,如滚动,键盘等事件。同时,DOM3级事件也允许使用者自定义一些事件。
DOM事件流
事件处理程序
从上文中我们可以看到事件处理程序就是例如onclick,onload等,DOM2中也提供了两个处理和删除事件处理程序的操作:addEventListener()和removeEventListener()能够全局访问任何代码块。
详细内容在前文已经讲过,就不过多解释了。
DOM事件模型
事件冒泡: 由最具体的元素(例如按钮)逐级到较为不具体的元素(如document),简单来说就是由里到外。
事件委托: 就是利用了事件冒泡机制,自己触发的事件,让父元素代替执行;这样大量减少内存占用,增加效率。例如卡片式点击。
<div id="parent">
<button id="son">事件冒泡</button>
</div>
<script>
const parent = document.getElementById('parent')
const son = document.getElementById('son')
parent.addEventListener('click', () => {
console.log('我是父亲')
}, false)
son.addEventListener('click', () => {
console.log('我是孩子')
}, false)
</script>
<!--
结果:
我是孩子
我是父亲
-->
事件捕获: 与事件冒泡的顺序相反,从window开始捕获。就是有外到里。
<div id="parent">
<button id="son">事件捕获</button>
</div>
<script>
const parent = document.getElementById('parent')
const son = document.getElementById('son')
parent.addEventListener('click', () => {
console.log('我是父亲')
}, true)
son.addEventListener('click', () => {
console.log('我是孩子')
}, true)
</script>
<!--
结果:
我是父亲
我是孩子
-->
看张图片可能更加清晰吧~
该图来源于网络
DOM事件流
同时支持两种事件模型: 事件捕获和事件冒泡,他们执行的顺序为:
- 事件捕获阶段
- 处于目标阶段
- 事件冒泡阶段
这个前提是捕获阶段不会涉及事件目标。但是有些浏览器在捕获阶段会涉及事件目标
来看个栗子吧~
<div id="parent">
我是父亲
<div id="son">
我是孩子
</div>
</div>
<script>
const parent = document.getElementById('parent')
const son = document.getElementById('son')
// 父亲捕获
parent.addEventListener('click', () => {
console.log('父亲捕获')
}, true)
// 父亲冒泡
parent.addEventListener('click', () => {
console.log('父亲冒泡')
}, false)
// 孩子捕获
son.addEventListener('click', () => {
console.log('孩子捕获')
}, true)
// 孩子冒泡
son.addEventListener('click', () => {
console.log('孩子冒泡')
}, false)
</script>
大家觉得结果是什么呢?
答案应该先父亲捕获,紧接着到目标程序,孩子捕获和孩子冒泡,然后在进行父亲冒泡
那如果我将目标程序中代码顺序改变下,结果还是一样的么
<div id="parent">
我是父亲
<div id="son">
我是孩子
</div>
</div>
<script>
const parent = document.getElementById('parent')
const son = document.getElementById('son')
//对父亲也进行顺序修改
// 父亲冒泡
parent.addEventListener('click', () => {
console.log('父亲冒泡')
}, false)
// 父亲捕获
parent.addEventListener('click', () => {
console.log('父亲捕获')
}, true)
//在这里修改顺序
// 孩子冒泡
son.addEventListener('click', () => {
console.log('孩子冒泡')
}, false)
// 孩子捕获
son.addEventListener('click', () => {
console.log('孩子捕获')
}, true)
</script>
大家看看我的截图吧
显而易见,答案与之前的不一样了,不是说好先进行事件捕获再进行事件冒泡的么?怎么改一下代码顺序就不一样了呢, 而且奇怪的是,父亲代码顺序修改不会对结果有影响(前提是点击我是孩子时,点击我是父亲也会改变),而孩子代码修改却让结果不一样了。
这里面的神奇之处是因为孩子代码是目标程序,目标程序是不会对事件流中的事先定好的排位顺序唯命是从了, 他们更加公平, 遵守的是先来后到的规则, 无论是事件捕获还是事件冒泡, 只要谁先出现在代码里,谁就先执行。
注:此文为本人学习过程中的笔记记录,如果有错误或者不准确的地方请大佬多多指教~