javascript事件处理机制---事件监听、捕获和冒泡
一、事件监听
在前端开发中,我们经常需要对某些事件进行监听。这样只要在指定的元素上触发了该事件,就会执行一个回调函数来进行相关的操作。
而JavaScript中事件监听的方法总共有三种,分别如下:element.addEventListener(type, listener[, useCapture]); //IE6~8不支持
element.attachEvent('on' + type, listener) //支持IE6~10,IE11不支持
element['on' + type] = function(){} //支持所有浏览器
参数解释:
type:事件类型
listener:事件出发后的回调函数
useCapture:是否使用捕获,如果值为true,useCapture表示用户希望发起捕获。在发起捕获之后,只要DOM子树下发生了该事件类型,都会先被该事件监听器捕获,然后再被派发到DOM子树中的事件监听器中。并且向上冒泡的事件不会触发那些发起捕获的事件监听器。useCapture默认值是false。
addEventListener是W3C工作组在DOM Level 2开始引入的一个注册事件监听器的方法,而在此之前,传统的事件监听方法是通过element['on' + type]的方式来注册的。它们的主要区别是element['on' + type]的方式无法使用事件捕获,并且element['on' + type]不支持对同一个元素的同一个事件注册多个事件监听器。如下面的例子所示,元素被点击之后只会输出1,而不会输出0和1。
element.onclick = function(){ console.log(0); }
element.onclick = function(){ console.log(1); }
然而addEventListener方法在IE6~8的浏览器中不被支持。那么在低版本的IE中怎么来为同一个事件注册多个事件监听器呢?原来IE从IE5.0系列开始就引入了attachEvent()方法来支持这一特性。但遗憾的是该方法也不支持事件捕获。并且从IE11开始,这个方法已经被弃用。二、事件捕获与事件冒泡
捕获 阶段:在事件对象到达事件目标之前,事件对象必须从window经过目标的祖先节点传播到事件目标。 这个阶段被我们称之为捕获阶段。在这个阶段注册的事件监听器在事件到达其目标前必须先处理事件。
目标 阶段:事件对象到达其事件目标。 这个阶段被我们称为目标阶段。一旦事件对象到达事件目标,该阶段的事件监听器就要对它进行处理。如果一个事件对象类型被标志为不能冒泡。那么对应的事件对象在到达此阶段时就会终止传播。
冒泡 阶段:事件对象以一个与捕获阶段相反的方向从事件目标传播经过其祖先节点传播到window。这个阶段被称之为冒泡阶段。在此阶段注册的事件监听器会对相应的冒泡事件进行处理。
在一个事件完成了所有阶段的传播路径后,它的Event.currentTarget会被设置为null并且Event.eventPhase会被设为0。Event的所有其他属性都不会改变(包括指向事件目标的Event.target属性)
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width" />
<title>Index</title>
</head>
<body>
<div id="div1" style="width:300px;height:300px;border:1px solid red">
<div id="div2" style="width:100px;height:100px;border:1px solid red">
<button class="btn primary" id="btnId">test</button>
</div>
</div>
</body>
</html>
<script>
var div1 = document.getElementById('div1')
var div2 = document.getElementById('div2')
var btn = document.getElementById('btnId');
div1.addEventListener('click', function () { alert('div1') },true);
div2.addEventListener('click', function () { alert('div2') },true);
btn.addEventListener('click', function (e) { alert('btn')},true);
</script>
点击按钮,分别弹出:div1,div2,btn
2.事件冒泡
<script>
var div1 = document.getElementById('div1')
var div2 = document.getElementById('div2')
var btn = document.getElementById('btnId');
div1.onclick = function () { alert("div1")}//.addEventListener('click', function () { alert('div1') },false);
div2.onclick = function () { alert("div2"); event.stopPropagation(); }//addEventListener('click', function () { alert('div2') },false);
btn.onclick = function (event) { alert("btn") }//addEventListener('click', function (e) { alert('btn')},false);
</script>
点击按钮,分别弹出:btn,div2,div1
3.event.currentTarget属性:
currentTarget 事件属性返回其监听器触发事件的节点,即当前处理该事件的元素、文档或窗口。在捕获和起泡阶段,该属性是非常有用的,因为在这两个节点,它不同于 target 属性。
/*HTML代码*/
<p id="p1" onmousedown="getEventTrigger(event)">点击我试试</p>
/*JavaScript代码*/
function getEventTrigger(event){
var x = event.currentTarget;
alert("The id of the triggered element: " + x.id);
}
4.Event.eventPhase属性:
eventPhase 属性返回事件传播的当前阶段。它的值是下面的三个常量之一,它们分别表示捕获阶段、正常事件派发和起泡阶段。
window.onload = function(){
var btn = document.getElementById("btn");
btn.onclick = function(event){
alert(event.eventPhase); //2
}
document.body.addEventListener("click",function(event){
alert(event.eventPhase); //1
},true);
document.body.onclick = function(event){
alert(event.eventPhase); //3
}
}
三、阻止事件冒泡
<!DOCTYPE html>
<html>
<head>
<title>Test</title>
<meta charset="utf-8">
<link rel="stylesheet" type="text/css" href="style.css">
<script type="text/javascript" src="jquery.js"></script>
<script type="text/javascript">
function doSometing(obj,evt){
alert(obj.id);
// 做浏览器兼容
var e = (evt)? evt:window.event; //获取IE或非IE浏览器的事件
if (window.event) { //如果是IE浏览器
e.cancelBubble = true; //IE浏览器,设置该属性为true,取消事件冒泡
}else{
e.stopPropagation(); //非IE浏览器,该方法取消事件的进一步捕获或冒泡。
}
}
</script>
</head>
<body>
<div id="parent1" onclick="alert(this.id);" style="width: 250px;background-color: #cacaca">
<p>This is parent1 div</p>
<div id="child1" onclick="alert(this.id);" style="width: 200px;background-color: orange">
<p>This is child1.Will bubble.</p>
</div>
</div>
<br>
<div id="parent2" onclick="alert(this.id);" style="width: 250px;background-color: cyan;">
<p>This is parent2 div</p>
<div id="child2" onclick="doSomething(this,event);" style="width: 200px;background-color: #aeaeae">
<p>This is child2.</p>
</div>
</div>
</div>
</body>
</html>
如图所示,以上代码定义了4个div,其中2个父div,2个子div。单击每个div都会弹出一个消息框,显示其id。单击child1和child2时,事件会冒泡传导到其父元素parent1和parent2。因此,单击child1会弹出两个消息框。而单击child2时,会调用doSomethig函数,使用cancelBubble属性或stopPropagation()方法来停止事件冒泡。
四、阻止事件的默认行为
IE和其他浏览器阻止事件的默认行为的方法不同。在IE中,可以通过设置event对象的returnValue属性为false来阻止事件的默认行为;在其他浏览器中,则可以通过设置event对象的preventDefault()方法来实现。
<!DOCTYPE html>
<html>
<head>
<title>Test</title>
<meta charset="utf-8">
<link rel="stylesheet" type="text/css" href="style.css">
<script type="text/javascript" src="jquery.js"></script>
<script type="text/javascript">
window.onload = function(){
var test = document.getElementById('test');
test.onclick = function(e){ //当单击此超链接时执行这个函数
alert('URL:' + this.href + ',不会跳转');
stopDefault(e);
}
}
function stopDefault(e){
if(e && e.preventDefault){ //判断浏览器是非IE浏览器
e.preventDefault(); //非IE浏览器下使用preventDefault方法
}else{
//IE浏览器下令事件(window.event)的returnValue属性为false;
window.event.returnValue = false;
}
return false;
}
</script>
</head>
<body>
<a href="http://www.baidu.com" id="test">百度</a>
</body>
</html>
可以通过event对象的preventDefault属性来判断浏览器是否支持preventDefault()方法。如果preventDefault属性值为true则表示支持,否则不支持。下一篇: Oracle中Job的使用