- 深入理解js Dom事件机制(一)——事件流
事件就是当用户或者浏览器自身执行的某种动作,诸如 click、mouseover等都是事件的名称,那响应个事件的函数就称为事件处理程序(事件处理函数、事件句柄)。 事件处理程序的名字都是以on+事件名称命名,比如 click事件的事件处理程序就是onclick, 为某个事件指定事件处理程序的方式大致分为三种。
1、HTML事件处理程序
这个很简单,大家基本初学js的时候都应该用过,就不再赘述,直接看实例代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Html事件处理程序</title>
</head>
<body>
<!-- 第一种:直接在html中定义事件处理程序和事件动作 -->
<input type="button" value="clcik me" onclick="console.log(event.type, this.value);"> <!-- click click me -->
<!-- 第二种:html中定义事件处理程序,执行的动作则调用其他地方的脚本 -->
<input type="button" value="happy" onclick="happy()">
</body>
<script type="text/javascript">
function happy() {
console.log(this === window);
// true
}
</script>
</html>
以上代码展示了两种html指定事件处理程序的方法,需要注意的是 第一种做法的this指向的是元素本身, 所以我们可以很容易的访问元素本身的属性,而第二种做法的this指向的window对象。
缺点:
- 存在时差问题,当用户在元素出现在页面就触发事件,但有可能这个时候事件处理程序不具备执行的条件。
- html与js代码耦合度高,这正是很多开发者放弃html事件处理程序的原因。
2、DOM0级事件处理程序
这种方式首先需要取得一个dom元素对象的应用,然后将一个函数赋值给一个事件处理程序,这种方式在第四代浏览器中就已经出现,至今仍然为现在浏览器所支持,原因一是简单,二是具有跨浏览器的优势。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>DOMO级事件处理程序</title>
</head>
<body>
<input type="button" value="happy" id="happy">
</body>
<script type="text/javascript">
var btn = document.getElementById(‘happy‘);
btn.onclick = function () {
console.log(this.value); // happy
}
</script>
</html>
通过上面的代码可以看出,这种方法指定的事件处理程序中this是指向元素本身。相对应的这种方法也可以删除指定的事件处理程序: btn.onclick = null.
3、DOM2级事件处理程序
DOM2级事件定义了两个方法,用于处理指定和删除事件处理程序的操作:addEventListener()、removeEventListener(),它们都接受三个参数:要处理的事件名、事件处理函数、布尔值(true:在捕获阶段调用事件处理函数,false:在冒泡阶段调用事件处理函数)。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>DOM2级事件处理程序</title>
</head>
<body>
<input type="button" value="happy" id="happy">
</body>
<script type="text/javascript">
var btn = document.getElementById(‘happy‘);
btn.addEventListener(‘click‘,function happy() {
console.log(‘事件捕获阶段被单击‘)
},true)
btn.addEventListener(‘click‘,function happy() {
console.log(‘事件冒泡阶段被单击‘)
},false)
btn.addEventListener(‘mouseover‘,function happy() {
console.log(‘鼠标移入啦‘)
})
</script>
</html>
从上述代码中可以看出:
- addEventListener可以对一个元素添加多个事件处理程序,并可以声明是将事件处理程序添加到哪一个阶段(为了保证兼容性、建议都将事件处理程序添加到冒泡阶段)。
- 需要特别注意的是:removeEventListener移除事件处理函数的时候,传入的事件事件处理函数必须和addEventListener传入的相同,方可移除,这就意味着如果addEventListener中使用了一个匿名函数来作为事件处理函数,那么removeEventListener将无法移除。
4、IE事件处理程序(IE8 && IE8 - && Opera)
IE实现了类似DOM中的两个方法:attacheEvent()、detachEvent(),它们接受两个参数:事件处理程序名称、事件处理函数。由于IE8以及更早的浏览器只支持冒泡事件流,所以通过attacheEvent()添加的事件处理程序都将在会被添加到冒泡阶段。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>IE事件处理程序</title>
</head>
<body>
<input type="button" value="happy" id="happy">
</body>
<script type="text/javascript">
var btn = document.getElementById(‘happy‘);
btn.attachEvent(‘click‘,function happy() {
console.log(‘事件冒泡阶段被单击‘)
})
btn.attachEvent(‘mouseover‘,function happy() {
console.log(‘鼠标移入啦‘)
})
</script>
</html>
与DOM2级方法的异同
-
相同:
1、都可以添加和移除事件处理程序,匿名函数均不可移除。 2、都可以添加多个事件处理程序。
-
不同:
1、IE的事件处理函数会在全局作用于执行,所以this指向window,而DOM方法中this指向元素对象引用 2、当添加多个事件处理程序时:执行的顺序和DOM2级事件处理程序相反。
5、跨浏览器事件处理程序
为了保证事件处理的代码能在大部分的浏览器下得到一致性的运行,我们可以恰当的使用浏览器能力检测能力封装一个通用的事件处理程序添加函数。
let eventUtil = {
addEventHandle(element, eventType, handle) {
if (Object.prototype.toString.apply(handle) !== ‘[object Function]‘) {
throw new TypeError(‘handle invaild‘)
}
if (!element.addEventListener) {
element.addEventListener(eventType,handle)
} else if (element.attachEvent) {
element.attachEvent(`on${eventType}`,handle)
} else{
element[`on${eventType}`] = handle
}
},
removeEventHandle(element, eventType, handle) {
if (Object.prototype.toString.apply(handle) !== ‘[object Function]‘) {
throw new TypeError(‘handle invaild‘)
}
if (!element.removeEventListener) {
element.removeEventListener(eventType,handle)
} else if (element.detachEvent) {
element.detachEvent(`on${eventType}`,handle)
} else{
element[`on${eventType}`] = null
}
}
}
上面的代码很简单,首先判断浏览器是否支持addEventHandle, 如果支持就用它来添加事件程序,否则再判断attachEvent,如果还不支持只能用Dom0级事件添加, 但是针对IE8--使用attachEvent添加事件处理程序时,this的指向并没有做处理,包括事件触发的阶段等。