事件,就是文档或者浏览器窗口中发生的一些特定的交互瞬间。小柒从事件流开始总结有关事件的知识点。
事件流:描述从页面中接受事件的顺序。
DOM2级事件规定的事件流包括三个阶段:事件捕获阶段、处于目标阶段、事件冒泡阶段
。
首先发生的是事件捕获,为截获事件提供了机会;然后是实际的目标接受到事件,最后一个阶段是冒泡阶段,可以在这个阶段对事件做出响应。
其思想:让不是目标的节点更早接收到事件,而具体的目标节点应该最后接收到事件。用意就是在事件到达目标之前就捕获它。
以HTML结构为例,说明事件捕获:
<!DOCTYPE HTML>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
<body>
<div></div>
</body>
</html>
如果点击div,那么document
对象先捕获click事件,然后事件沿着DOM树依次向下,直到具体目标div
。具体流程:
(1) document
(2) <html>
(3) <body>
(4) <div>
注意: IE9+、现代浏览器都支持事件捕获,但是从window对象开始捕获的。 具体流程:
(1) window
(2) document
(3) <html>
(4) <body>
(5) <div>
举个例子:
DOM2级addEventListener()方法中的第三个参数设置为true时,就是事件捕获阶段。
<!DOCTYPE HTML>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
<body>
<div id="box" style="height:100px;width:300px;background-color:pink;"></div>
<button id="reset">还原</button>
<script>
//IE8-浏览器不支持addEventListener方法
//其他浏览器依次返回window document html body div
reset.onclick = function(){
history.go();}
box.addEventListener('click',function(){
box.innerHTML += 'div\n'},true)
document.body.addEventListener('click',function(){
box.innerHTML += 'body\n';},true);
document.documentElement.addEventListener('click',function(){
box.innerHTML += 'html\n';},true);
document.addEventListener('click',function(){
box.innerHTML += 'document\n';},true);
window.addEventListener('click',function(){
box.innerHTML += 'window\n';},true);
</script>
</body>
</html>
思想:事件先由具体的元素接受,然后逐级向上传播到各个不具体的节点。
在IE中,事件流就是事件冒泡。
HTML结果与上面事件捕获一样。点击div
元素,那么这个click事件沿DOM树向上传播。具体流程:
(1) <div>
(2) <body>
(3) <html>
(4) document
注意: IE9+、现代浏览器都支持事件冒泡,但是会一直冒泡到window对象。 具体流程:
(1) <div>
(2) <body>
(3) <html>
(4) document
(5) window
举个例子:
这里没有使用DOM2级方法,而是直接使用的DOM0级事件处理程序。直接给事件赋一个函数。
<!DOCTYPE HTML>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
<body>
<div id="box" style="height:100px;width:300px;background-color:pink;"></div>
<button id="reset">还原</button>
<script>
//IE8-浏览器返回div body html document
//其他浏览器返回div body html document window
reset.onclick = function(){
history.go();}
box.onclick = function(){
box.innerHTML += 'div\n';}
document.body.onclick = function(){
box.innerHTML += 'body\n';}
document.documentElement.onclick = function(){
box.innerHTML += 'html\n';}
document.onclick = function(){
box.innerHTML += 'document\n';}
window.onclick = function(){
box.innerHTML += 'window\n';}
</script>
</body>
</html>
事件处理程序又叫事件侦听器,实际就是事件的绑定函数。事件处理程序有HTML事件处理程序
、DOM0级处理程序
、DOM2级事件处理程序
、IE事件处理程序
。
直接将事件属性写在元素标签内部,这里的this指向事件目标元素div
。
<div id="box" style="height:30px;width:200px;background-color:pink;"onclick = "this.innerHTML+= '1';"></div>
也可以将事件处理程序写在其他的脚本中:
<div id="box" style="height:30px;width:200px;background-color:pink;"onclick = "test()"></div>
<script>
function test(){
box.innerHTML+= '1';}
</script>
HTML事件处理程序里面会有一个局部变量event
,在函数内部可以访问它:
<div id="box" style="height:30px;width:200px;background-color:pink;"onclick = "this.innerHTML = event.type;"></div>
就是采用传统的方式,并且以这种方式添加的事件处理程序会在冒泡阶段,如下:
<div id="box" style="height:30px;width:200px;background-color:pink;"></div>
<script>
box.onclick = function(){
this.innerHTML += '1';}
</script>
缺点 :DOM0级事件处理程序的缺点是围绕着每个事件目标对于每种事件类型只能添加一个事件处理程序
就是我们很熟悉的方法addEventListener()
和removeEventListener()
。所有DOM节点中都会包含这两个方法,并且它们都接受3个参数:事件名
、事件处理程序的函数
、一个布尔值
。布尔值如果是true,表示在捕获阶段;如果是false,表示在冒泡阶段;不写和false效果一样。
优点:可以添加多个事件处理程序。
注意:IE8-不支持DOM2级事件处理程序.
<div id="box" style="height:30px;width:200px;background-color:pink;"></div>
<script>
box.addEventListener('click',function(){
this.innerHTML += '1'},false);
box.addEventListener('click',function(){
this.innerHTML += '2'},false);
</script>
通过addEventListener()添加的事件处理程序只能使用removeEventListener()来移除,移除时传入的参数与添加时的参数要相同。这意味着,addEventListener()添加的匿名函数无法删除。
无效例子:
<div id="box" style="height:30px;width:200px;background-color:pink;"></div>
<script>
box.addEventListener("click",function(){
this.innerHTML += '1'
},false);
box.removeEventListener('click',function(){
this.innerHTML += '1'
},false);
</script>
有效例子
<div id="box" style="height:30px;width:200px;background-color:pink;"></div>
<script>
var handle = function(){
this.innerHTML += '1'};
box.addEventListener("click",handle,false);
box.removeEventListener('click',handle,false);
</script>
IE中实现了attachEvent()
和detachEvent()
这两个方法。这两个方法接受两个参数:事件名
、事件处理程序函数
。因为IE-浏览器只支持事件冒泡,所以通过attachEvent()添加的事件处理程序都会被添加到事件冒泡阶段。
attachEvent()方法的第一个参数是"onclick",而非addEventListener()中的"click"。
<div id="box" style="height:30px;width:200px;background-color:pink;"></div>
<script>
box.attachEvent('onclick',function(){
this.innerHTML += '1';});
</script>
举个例子:
下面例子只要在IE10-下有效,返回div body html document
。
<div id="box" style="height:30px;width:200px;background-color:pink;"></div>
<button id="reset">还原</button>
<script>
//IE10-浏览器返回div body html document
//其他浏览器报错
reset.onclick = function(){
history.go();}
box.attachEvent('onclick',function(){
box.innerHTML += 'div\n';});
document.body.attachEvent('onclick',function(){
box.innerHTML += 'body\n';});
document.documentElement.attachEvent('onclick',function(){
box.innerHTML += 'html\n';});
document.attachEvent('onclick',function(){
box.innerHTML += 'document\n';});
window.attachEvent('onclick',function(){
box.innerHTML += 'window\n';});
</script>
注意:与其他三种事件处理程序不同,在IE事件处理程序中this执行window;而非被绑定的元素。
<div id="box" style="height:100px;width:300px;background-color:pink;"></div>
<script>
box.attachEvent('onclick',function(){
console.log(this);//window
});
</script>
使用attachEvent()添加的事件可以通过detachEvent()来移除,条件也必须是相同的参数。这也意味着添加的匿名函数将不能被移除。
下面例子无效:
<div id="box" style="height:30px;width:200px;background-color:pink;"></div>
<script>
box.attachEvent("onclick",function(){
box.innerHTML += '1'
});
box.detachEvent('onclick',function(){
box.innerHTML += '1'
});
</script>
下面例子有效:
<div id="box" style="height:30px;width:200px;background-color:pink;"></div>
<script>
var handle = function(){
box.innerHTML += '1'};
box.attachEvent("onclick",handle);
box.detachEvent('onclick',handle);
</script>
在DOM上触发某个事件时,会产生一个事件对象event
。这个对象包含着与事件有关的信息。
兼容写法:
<div id="box" style="height:30px;width:200px;background:pink;"></div>
<script>
var oBox = document.getElementById('box');
oBox.onclick = function(e){
e = e || event; // 兼容写法(IE8-不支持e;火狐不支持event)
box.innerHTML = e;
}
</script>
事件对象event有很多的属性。比如判断事件类型的event.type
、获取事件目标的currentTarget
、target
以及srcElement
等属性与方法。
1、事件类型
通过event.target判断事件类型:
<div id="box" style="height:30px;width:200px;background:pink;"></div>
<script>
//鼠标移入时,显示mouseover;移出时,显示mouseout;点击时,显示click
var oBox = document.getElementById('box');
oBox.onclick = oBox.onmouseout =oBox.onmouseover =function(e){
e = e || event;
box.innerHTML = e.type;
}
</script>
2、事件目标
关于事件目标共有三个属性:currentTarget
、target
和srcElement
。
currentTarget :返回事件当前所在的节点。即事件绑定在哪个节点就返回哪个节点。与this指向相同(不包括attachEvent()事件处理程序,其this指向window)(IE8-不支持)
<style>
.in{
height: 30px;background-color: lightblue;margin:0 10px;}
</style>
<ul id="box">
<li class="in">1</li>
<li class="in">2</li>
</ul>
<script>
box.onclick = function(e){
e = e || event;
var tags = box.getElementsByTagName('li');
tags[0].innerHTML = e.currentTarget;
tags[1].innerHTML = (e.currentTarget === this);
}
</script>
target:返回当前操作的节点。(IE8-不支持)
<style>
#box{
background-color: lightblue;}
.in{
height: 30px;}
</style>
<ul id="box">
<li class="in">1</li>
<li class="in">2</li>
</ul>
<script>
box.onclick = function(e){
e = e || event;
e.target.style.backgroundColor = 'pink';
}
</script>
srcElement:srcElement属性与target属性功能一致(firefox不支持)
一般为了兼容处理,这样写:
var handler = function(e){
e = e || event;
var target = e.target || e.srcElement;
}
3、事件代理
由于事件在冒泡阶段会向上传播到父节点,因此把子节点的监听函数定义在父节点上,由父节点的监听函数统一处理子元素的事件。这种方法叫做事件代理,也叫事件委托。
事件代理应用事件目标中的target
与srcElement
属性完成。
举个例子:现在有一个
中有5个
,移入时变浅,移除时变红。用常规方法与事件代理方法来实现:
<style>
#box{
background-color: pink;}
.in{
height: 30px;}
</style>
<ul id="box">
<li class="in">1</li>
<li class="in">2</li>
<li class="in">3</li>
<li class="in">4</li>
<li class="in">5</li>
</ul>
<script>
//常规方法
var tags = box.getElementsByTagName('li');
for(var i = 0; i < tags.length; i++){
tags[i].onmouseover = function(e){
this.style.backgroundColor = 'lightblue';
}
tags[i].onmouseout = function(e){
this.style.backgroundColor = 'pink';
}
}
</script>
<script>
//事件代理方法
box.onmouseover = function(e){
e = e || event;
var target = e.target || e.srcElement;
target.style.backgroundColor = 'lightblue';
}
box.onmouseout = function(e){
e = e || event;
var target = e.target || e.srcElement;
target.style.backgroundColor = 'pink';
}
</script>
最适合使用事件委托技术的事件包括click、mousedown、mouseup、keydown、keyup和keypress
4、事件冒泡
关于事件冒泡,event对象有bubbles
、cancelBubble
、stopPropagation()
和stopImmediatePropagation()
这4个属性与方法。
bubbles
:返回一个布尔值,表示当前事件是否会冒泡。该属性只读。(focus、blur、scroll事件不会冒泡)
<button id="test" style="height: 30px;width: 200px;"></button>
<script>
//点击按钮时,按钮内容为true,说明click事件默认可冒泡
test.onclick = function(e){
test.innerHTML =e.bubbles;//true
}
</script>
stopPropagation()
方法表示取消事件的进一步捕获或冒泡,无返回值(IE8-不支持)
<button id="test" style="height: 30px;width: 200px;"></button>
<script>
//点击按钮时,按钮内容为'button',因为阻止了
test.onclick = function(e){
e = e || event;
e.stopPropagation();
test.innerHTML +='button\n';
}
document.body.onclick = function(e){
test.innerHTML += 'body\n'
}
</script>
stopImmediatePropagation()
方法不仅可以取消事件的进一步捕获和冒泡,而且可以阻止同一个事件的其他监听函数被调用。(IE8-不支持)
<button id="test" style="height: 30px;width: 200px;"></button>
<script>
//使用stopImmediatePropagation()方法,
test.addEventListener('click',function(e){
e = e || event;
e.stopImmediatePropagation();
test.innerHTML +='button\n';
})
test.addEventListener('click',function(e){
e = e || event;
test.style.background = 'lightblue';
})
document.body.onclick = function(e){
test.innerHTML += 'body\n'
}
</script>
cancelBubble
属性只能阻止冒泡,无法阻止捕获阶段。
<button id="test" style="height: 30px;width: 200px;"></button>
<script>
test.onclick = function(e){
e = e || event;
e.cancelBubble = true;
test.innerHTML +='button\n';
}
document.body.onclick = function(e){
test.innerHTML += 'body\n'
}
</script>
兼容写法:
var handler = function(e){
e = e || event;
if(e.stopPropagation){
e.stopPropagation();
}else{
e.cancelBubble = true;
}
}
5、事件流
eventPhase
属性返回一个整数值,表示目前所处的事件流阶段。
6、取消默认行为
常见的默认行为有点击链接后,浏览器跳转到指定页面;或者按一下空格键,页面向下滚动一段距离
关于取消默认行为的属性包括cancelable
、defaultPrevented
、preventDefault()
和returnValue
cancelable: 返回一个布尔值,表示事件是否可以取消默认行为(IE8-浏览器不支持)
preventDefault():取消浏览器对象当前事件的默认行为。(IE8-浏览器不支持)
returnValue: 可读写,设置为false就是取消事件的默认行为。(firefox和IE9+浏览器不支持)
取消默认行为的兼容写法:
var handler = function(e){
e = e || event;
if(e.preventDefault){
e.preventDefault(); // 现代浏览器
}else{
e.returnValue = false; // IE8-
}
}
defaultPrevented:表示默认行为是否被阻止。返回true表示被阻止,返回false表示未阻止。
参考文章: