DOM事件
DOM事件模型
DOM0级模型
又称为原始事件模型,在该模型中,事件不会传播,即没有事件流的概念。这种方式所有浏览器都兼容,但是逻辑与显示并没有分离。事件绑定监听函数比较简单,有两种方式:
HTML代码中直接绑定:
<input type="button" onclick="fun()">
通过JS代码指定属性值:
var btn = document.getElementById('.btn'); btn.onclick = fun;
移除监听函数:
btn.onclick = null;
IE事件模型
IE事件模型共有两个过程:
- 事件处理阶段(target phase)。事件到达目标元素,触发目标元素的监听函数。
- 事件冒泡阶段(bubbling phase)。事件从目标元素冒泡到 document,依次检查经过的节点是否绑定了事件监听函数,如果有则执行。
事件绑定监听函数的方式如下:
// eventType指定事件类型(注意加on),handler是事件处理函数
attachEvent(eventType, handler)
事件移除监听函数的方式如下:
detachEvent(eventType, handler)
DOM2级模型
属于W3C标准模型,现代浏览器(除IE6-8之外的浏览器)都支持该模型。在该事件模型中,一次事件共有三个过程:
- 事件捕获阶段(capturing phase)。事件从 document 一直向下传播到目标元素, 依次检查经过的节点是否绑定了事件监听函数,如果有则执行。
- 事件处理阶段(target phase)。事件到达目标元素, 触发目标元素的监听函数。
- 事件冒泡阶段(bubbling phase)。事件从目标元素冒泡到 document,依次检查经过的节点是否绑定了事件监听函数,如果有则执行。
事件绑定监听函数的方式如下:
// eventType指定事件类型(不要加on);handler是事件处理函数;useCapture用于指定是否在捕获阶段进行处理,true表示在捕获阶段触发,false表示在冒泡阶段触发
addEventListener(eventType, handler, useCapture)
事件移除监听函数的方式如下:
removeEventListener(eventType, handler, useCapture)
捕获阶段,事件依次传递的顺序是:window –> document –> html–> body –> 父元素–>目标元素。
冒泡阶段,事件依次传递的顺序是:
一般的浏览器: (除IE6.0之外的浏览器):目标元素->父元素 -> body -> html -> document -> window
IE6.0:目标元素->父元素 -> body -> html -> document
通用的事件侦听器函数
const eventUtils = {
// 添加事件
addEvent: function(element, type, callback) {
// DOM2 高版本浏览器的写法
if(element.addEventListener) {
element.addEventListener(type, callback, false); // false表示在事件冒泡阶段触发
}
// DOM2 IE8及以下版本的写法
else if(element.attachEvent) {
element.attachEvent('on' + type, callback)
}
// DOM0的写法
else {
element['on' + type] = callback;
}
},
// 移除事件
removeEvent: function(element, type, callback) {
// DOM2 高版本浏览器的写法
if(element.removeEventListener) {
element.removeEventListener(type, callback, false); // false表示在事件冒泡阶段触发
}
// DOM2 IE8及以下版本的写法
else if(element.detachEvent) {
element.detachEvent('on' + type, callback);
}
// DOM0的写法
else {
element['on' + type] = null;
}
},
// 获取 Event 对象
getEvent: function(event) {
// 普通浏览器的写法为 event,IE678 的写法为 window.event
return event || window.event;
},
// 获取事件类型
getType:function(event) {
return event.type;
},
// 获取事件目标
getTarget: function(event) {
// 普通浏览器的写法为 event.target,IE8及以下版本的写法为 event.srcElement
return event.target || event.srcElement;
},
// 阻止事件冒泡
stopPropagation: function(event) {
if(event.stopPropagation) {
event.stopPropagation();
}
// IE10及以下版本的写法
else {
event.cancelBubble = true;
}
},
// 阻止事件默认行为
preventDefault: function(event) {
if(event.preventDefault) {
event.preventDefault();
}
// IE8及以下版本的写法
else {
event.returnValue = false;
}
}
};
事件代理/事件委托
事件在冒泡过程中会上传到父元素,因此可以把子元素的事件侦听函数定义在父元素上,由父元素的侦听函数统一处理多个子元素的事件。
下面例子中的 ul 列表中有多个列表项 a 标签,如果给每个链接都添加一个事件,那么会过于消耗内存和性能,我们希望,只绑定一次事件,即可应用到多个元素上。因此可以在父元素 ul 上注册一个 click 事件,点击某个子元素 a 会向父元素冒泡,父元素侦听到 click 事件后,根据 event.target
的值判断点击的是哪个 a 标签,从而作出相应的处理。
<ul class="list">
<li><a href="#">111</a></li>
<li><a href="#">222</a></li>
<li><a href="#">333</a></li>
</ul>
const list = document.querySelector('.list');
eventUtils.addEvent(list, 'click', function(e) {
let event = e || window.event;
let target = event.target || event.srcElement;
// 满足某个条件,才执行后续代码
if(target.tagName.toLowerCase() === 'a') {
console.log(target.textContent);
}
});