Javascript中的事件流和事件处理程序
1、事件流
Javascript和HTML的交互是通过事件实现的。事件代表文档或浏览器窗口中某个有意义的时刻。事件流描述了页面接收事件的顺序。
1.1 事件冒泡
IE事件流被称为事件冒泡,这是因为事件被定义为从具体的元素(文档树中最深的节点)开始触发,然后向上传播至没那么具体的元素(文档)。比如有以下HTML页面:
<!DOCTYPE html>
<html lang="en">
<head>
<title>Document</title>
</head>
<body>
<div>Click Me</div>
</body>
</html>
在点击页面中的<div>
元素后,最先触发<div>
的click事件,然后,click事件沿着DOM树一路向上,顺序如下:
(1)div元素
(2)body元素
(3)html元素
(4)document
1.2 事件捕获
事件捕获的意思是最不具体的节点应该最先收到事件,而最具体的节点应该最后收到事件。事件捕获实际上是为了在事件到达最终目标前拦截事件。如前面的例子,点击div元素会按照如下顺序触发click事件:
(1)document
(2)html元素
(3)body元素
(4)div元素
2、DOM事件流
DOM2 Events规范规定了事件流分为3个阶段:事件捕获、到达目标和事件冒泡。事件捕获是最先发生,为提前拦截事件提供了可能。然后,实际的目标元素接收到事件。最后一个阶段是冒泡。
3、事件处理程序
为响应事件而调用的函数被称为事件处理程序。
3.1 DOM0事件处理程序
DOM0中指定事件处理程序的方式是把一个函数赋给DOM元素的一个事件处理程序属性。如:
let btn = document.getElementById('oBtn')
btn.onclick = function () {
console.log('clicked')
}
所赋函数被视为元素的方法,事件处理程序会在元素的作用域中运行,this等于元素:
let btn = document.getElementById('oBtn')
btn.onclick = function () {
console.log(this.id) // oBtn
}
以这种方式添加事件处理程序是注册在事件流的冒泡阶段。
通过将事件处理程序属性的值设置为null,可以移除以DOM0方式添加的事件处理程序:
btn.onclick = null
3.2 DOM2事件处理程序
DOM2 Events为事件处理程序的赋值和移除定义了两个:addEventListener()
和removeEventListener()
。方法接收三个参数:事件名、事件处理函数和一个布尔值,布尔值为true表示在捕获阶段调用事件处理程序,false(默认)表示在冒泡阶段调用。这个事件处理程序同样在被附加到的元素的作用域中运行。
let btn = document.getElementById('oBtn')
btn.addEventListener('click', () => {
console.log(this.id)
}, false)
使用DOM2方式的主要优势是可以为同一个事件添加多个事件处理程序。
let btn = document.getElementById('oBtn')
btn.addEventListener('click', () => {
console.log(this.id)
}, false)
btn.addEventListener('click', () => {
console.log('hello world!')
}, false)
// 多个事件处理程序会以添加的顺序触发
通过addEventListener()
添加的事件处理程序只能使用removeEventListener()
并传入与添加时同样的参数来移除。意味着使用addEventListener()
添加的匿名函数无法移除。
let btn = document.getElementById('oBtn')
let handler = function () {
console.log(this.id)
}
btn.addEventListener('click', handler, false)
btn.removeEventListener('click', handler, false)
3.3 IE事件处理程序
IE实现了与DOM类似的方法,即attachEvent()
和detachEvent()
。两个方法接收同样的参数:事件处理程序的名字和事件处理函数。使用attachEvent()添加的事件处理程序会添加到冒泡阶段。
let btn = document.getElementById('oBtn')
btn.attachEvent('onclick', function () { // 注意参数时onclick
console.log('hello world')
})
在IE中使用attachEvent()与使用DOM0方式的主要区别是事件处理函数的作用域。使用DOM0方式时,事件处理程序中的this值等于目标元素。使用attachEvent()时事件处理函数会在全局作用域中运行,因此this等于window。
attachEvent()也可以给一个元素添加多个事件处理程序:
let btn = document.getElementById('oBtn')
btn.attachEvent('onclick', function () {
console.log('hello world')
})
btn.attachEvent('onclick', function () {
console.log(this)
})
// 添加的多个事件处理程序会以添加它们的顺序反向触发。
使用attachEvent()添加的事件处理程序将使用detachEvent()来移除,同样添加的匿名函数无法移除:
let btn = document.getElementById('oBtn')
let handler = function () {
console.log('hello world')
}
btn.attachEvent('onclick', handler)
btn.detachEvent('onclick', handler)
4、跨浏览器事件处理程序
跨浏览器兼容的方式处理事件,需要根据需要分别使用DOM0方式,DOM2方式或IE方式来添加事件处理程序。可以用EventUtil对象来实现:
var EventUtil = {
// 三个参数:目标元素、事件名、事件处理函数
addHandler: function(element, type, handler) {
if (element.addEventListener) {
element.addEventListener(type, handler, false)
} else if (element.attachEvent) {
element.attachEvent("on" + type, handler)
} else {
element["on" + type] = handler
}
},
removeHandler: function(element, type, handler) {
if (element.removeEventListener) {
element.removeEventListener(type, handler, false)
} else if (element.detachEvent) {
element.detachEvent("on" + type, handler)
} else {
element["on" + type] = null
}
}
}
这里的addHandler()和removeHandler()方法并没有解决所有跨浏览器一致性问题,比如IE的作用域问题、多个事件处理程序执行顺序问题等,另外,DOM0只支持给一个事件添加一个处理程序。好在DOM0浏览器已经很少有人用了,所以影响应该不大。
还没有评论,来说两句吧...