DOM 事件机制
- 事件是元素天生具备的行为方式(和写不写
JS
代码没关系),当我们去操作元素的时候会触发元素的很多事件 - 给当前元素的某一个事件绑定方法,目的是为了让当前元素某个事件被触发的时候,可以做一些事情
DOM0 事件
DOM0 事件绑定
element.onclick=function(e){
//=>this:element
e=e||window.event;
element.onmouseenter=function(){}
- 给当前元素对象的某一个私有属性(onxxx 这样的私有属性)赋值的过程(之前属性默认值是
null
,如果我们给赋值一个函数,相当于绑定了一个方法) - 当赋值成功(赋值一个函数),此时浏览器会把
DOM
元素和赋值的函数建立关联,以及建立DOM
元素行为操作的监听,当某一个行为被用户触发,浏览器会把相关行为赋值的函数执行 - 1、只有 DOM 元素天生拥有这个私有属性(onxxx 事件私有属性),我们赋值的方法才叫做事件绑定,否则属于给当前元素设置一个自定义属性而已
- 2、移除事件绑定的时候,我们只需要赋值为
null
即可 - 3、在 DOM0 事件绑定中,只能给当前元素的某一个事件行为(某一个事件私有属性)绑定一个方法,绑定多个方法,最后一次绑定的会把之前绑定的都替换掉
btn.onclick = function() {
console.log("this is a click event");
};
click
事件并没有像其他函数一样,必须要调用才可以执行,click 事件并不确定什么时候发生,而当浏览器发现用户点击该按钮时,浏览器就检测 btn.onclick
是否有值,如果有,就会执行 btn.onclick.call(btn,event)
,此时函数执行,call()
方法接收两个参数,第一个指向调用当前方法的对象,也就是 this
。
需要注意的是,指定的 this
值并不一定是该函数执行时真正的 this
值,如果这个函数处于非严格模式下,则指定为 null
和 undefined
的 this
值会自动指向全局对象(浏览器中就是 window
对象),同时值为原始值(数字,字符串,布尔值)的 this
会指向该原始值的自动包装对象。
另一个参数则是事件对象 event
,该对象也可以通过 arguments[0]
来访问,它包含了事件相关的所有信息,如本例子中,则包含了点击事件的全部信息。可以通过给函数传参来获取事件信息。
btn.onclick = function(e) {
console.log("this is a click event");
console.log(e); // 事件对象
};
在 DOM0 级中,如果想要实现一个对象绑定多个函数,可以这样实现。
function fn1() {
// do something
}
function fn2() {
// do something
}
btn.onclick = function(e) {
fn1.call(this, xxx);
fn2.call(this, yyy);
};
https://github.com/HarleyWang...
DOM2 事件
当前这个实例 document.body
之所以能用 addEventListener
这个方法,说明这个方法肯定在它的原型上出现。
可以用 instanceof
检测
DOM2
级事件定义了两个方法,用于处理指定和删除事件处理程序的操作,addEventListener()
和 removeEventListener()
,所有的 DOM
节点中都包含这两个方法,它们都接收 3
个参数
- 要处理的事件名
- 作为事件处理程序的函数
- 布尔值,
true
代表在捕获阶段调用事件处理程序,false
表示在冒泡阶段调用事件处理程序,默认为false
。
btn.addEventListener("click", function() {
// do something
});
btn.addEventListener("click", function() {
// do something else
});
element.addEventListener(
"click",
function(event) {
// 只执行一次的代码
},
{ once: true }
);
- 1、
DOM2
事件绑定使用的addEventListener/attachEvent
都是在EventTarget
这个内置类的原型上定义的,调取使用的时候,首先通过原型链找到这个方法,然后执行完成事件绑定的效果 - 浏览器首先会给当前元素的某一个事件行为开辟一个事件池(事件队列)[其实是浏览器有一个统一的事件池,我们每个元素的某个行为绑定的方法都放在这个事件池中,只是通过相关的标识来区分的],当我们通过 addEventListener 做事件监听的时候,会把绑定的方法存放在事件池中
- 当元素的某一个行为触发,浏览器会到对应的事件池中,把当前存放在事件池中的所有方法,依次按照存放的先后顺序执行
DOM0
是私有属性赋值,DOM2
是事件池的事件机制
- 1、所有
DOM0
支持的事件行为,DOM2 都可以使用,不仅如此,DOM2 还支持一些DOM0
没有的事件行为:DOMContentLoaded...
- 2、DOM2 中可以给当前元素的某一个事件行为绑定‘多个不同的方法’(不同的方法=>不同的空间地址,比如一个私有 fn 和全局的 fn 地址不一样,所以不是同一个 fn 方法,)(因为绑定的所有方法都存放在事件池中了;而 DOM0 是通过给私有属性赋值来绑定方法的,一个私有属性只能附一个值)
- 3、
DOM2
事件绑定的移除比较麻烦一些,需要和绑定的时候:事件类型、绑定的方法、传播阶段,三个完全一致才可以移除掉
DOM3 事件
DOM3 级事件在 DOM2 级事件的基础上添加了更多的事件类型,全部类型如下
事件类型 | 说明 | 举例 |
---|---|---|
UI 事件 | 当用户与页面上的元素交互时触发 | {load、scroll |
焦点事件 | 当元素获得或失去焦点时触发 | blur、focus |
鼠标事件 | 当用户通过鼠标在页面执行操作时触发 | dbclick、mouseup |
滚轮事件 | 当使用鼠标滚轮或类似设备时触发 | mousewheel |
文本事件 | 当在文档中输入文本时触发 | textInput |
键盘事件 | 当用户通过键盘在页面上执行操作时触发 | keydown、keypress |
合成事件 | 当为 IME(输入法编辑器)输入字符时触发 | compositionstart |
变动事件 | 当底层 DOM 结构发生变化时触发 | DOMsubtreeModified |
同时 DOM3
级事件也允许开发人员自定义一些事件。
var event = new Event('build');
// Listen for the event.
elem.addEventListener('build', function (e) { ... }, false);
// Dispatch the event.
elem.dispatchEvent(event);
添加自定义数据 – CustomEvent()
var event = new CustomEvent("build", { detail: elem.dataset.time });
冒泡事件&捕获事件
事件委托
由于事件会在冒泡阶段向上传播到父节点,因此可以把子节点的监听函数定义在父节点上,由父节点的监听函数统一处理多个子元素的事件。这种方法叫做事件的代理(delegation
)
。
那么利用事件冒泡或捕获的机制,可以对事件绑定做一些优化。 在 JS 中,如果我们注册的事件越来越多,页面的性能就越来越差,因为:
- 函数是对象,会占用内存,内存中的对象越多,浏览器性能越差
- 注册的事件一般都会指定 DOM 元素,事件越多,导致 DOM 元素访问次数越多,会延迟页面交互就绪时间。
- 删除子元素的时候不用考虑删除绑定事件
<ul id="list">
<li>1</li>
<li>2</li>
<li>3</li>
<li>4</li>
</ul>
<script>
// 给父层元素绑定事件
document.getElementById('list').addEventListener('click', function (e) {
// 兼容性处理
var event = e || window.event;
var target = event.target || event.srcElement;
// 判断是否匹配目标元素
if (target.nodeName.toLocaleLowerCase() === 'li') {
console.log('the content is: ', target.innerHTML);
}
});
</script>
EventTarget
DOM
的事件操作(监听和触发),都定义在 EventTarget
接口。所有节点对象都部署了这个接口,其他一些需要事件通信的浏览器内置对象(比如,XMLHttpRequest
、AudioNode
、AudioContext
)也部署了这个接口
该接口主要提供三个实例方法。
- addEventListener:绑定事件的监听函数;用于在当前节点或对象上,定义一个特定事件的监听函数。一旦这个事件发生,就会执行监听函数。该方法没有返回值。
- removeEventListener:移除事件的监听函数;方法用来移除 addEventListener 方法添加的事件监听函数;removeEventListener 方法的参数,与 addEventListener 方法完全一致
- dispatchEvent:触发事件;当前节点上触发指定事件,从而触发监听函数的执行。该方法返回一个布尔值,只要有一个监听函数调用了 Event.preventDefault(),则返回值为 false,否则为 true;参数是 Event 实例
Event 对象常见的方法和属性
- event. preventDefault();如果调用这个方法,默认事件行为将不再触发。什么是默认事件呢?例如表单一点击提交按钮(submit)刷新页面、a 标签默认页面跳转或是锚点定位等
- event.stopPropagation() & event.stopImmediatePropagation() event.stopPropagation() 方法阻止事件冒泡到父元素,阻止任何父事件处理程序被执行; stopImmediatePropagation 既能阻止事件向父元素冒泡,也能阻止元素同事件类型的其它监听器被触发。而 stopPropagation 只能实现前者的效果
https://juejin.im/post/5be643...https://www.w3.org/TR/DOM-Lev...
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。