一、示例
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div id="myDiv">
<ul id="myUl">
<li id="myLi">1</li>
</ul>
</div>
</body>
<script>
let oDiv = document.getElementById('myDiv');
let oUl = document.getElementById('myUl');
let oLi = document.getElementById('myLi');
window.addEventListener('click',function(ev){
var ev = ev || window.event;
let target = ev.target || ev.srcElement;
console.log('捕获' + ev.target.nodeName + ev.currentTarget.nodeName);
},true);
document.addEventListener('click',function(ev){
var ev = ev || window.event;
let target = ev.target || ev.srcElement;
console.log('捕获' + ev.target.nodeName + ev.currentTarget.nodeName);
},true);
document.body.addEventListener('click',function(ev){
var ev = ev || window.event;
let target = ev.target || ev.srcElement;
console.log('捕获' + ev.target.nodeName + ev.currentTarget.nodeName);
},true);
oDiv.addEventListener('click',function(ev){
var ev = ev || window.event;
let target = ev.target || ev.srcElement;
console.log('捕获' + ev.target + ev.currentTarget);
},true);
oUl.addEventListener('click',function(ev){
var ev = ev || window.event;
let target = ev.target || ev.srcElement;
console.log('捕获' + ev.target + ev.currentTarget);
},true);
oLi.addEventListener('click',function(ev){
var ev = ev || window.event;
let target = ev.target || ev.srcElement;
console.log('捕获' + ev.target + ev.currentTarget);
},true)
window.addEventListener('click',function(ev){
var ev = ev || window.event;
let target = ev.target || ev.srcElement;
console.log('冒泡' + ev.target.nodeName + ev.currentTarget.nodeName);
},false);
document.addEventListener('click',function(ev){
var ev = ev || window.event;
let target = ev.target || ev.srcElement;
console.log('冒泡' + ev.target.nodeName + ev.currentTarget.nodeName);
},false);
document.body.addEventListener('click',function(ev){
var ev = ev || window.event;
let target = ev.target || ev.srcElement;
console.log('冒泡' + ev.target.nodeName + ev.currentTarget.nodeName);
},false);
oDiv.addEventListener('click',function(ev){
var ev = ev || window.event;
let target = ev.target || ev.srcElement;
console.log('冒泡' + ev.target + ev.currentTarget);
},false);
oUl.addEventListener('click',function(ev){
var ev = ev || window.event;
let target = ev.target || ev.srcElement;
console.log('冒泡' + ev.target + ev.currentTarget);
},false);
oLi.addEventListener('click',function(ev){
var ev = ev || window.event;
let target = ev.target || ev.srcElement;
console.log('冒泡' + ev.target + ev.currentTarget);
},false)
</script>
</html>DOM2的事件流:事件捕获、处于目标状态、事件冒泡。
目标源(target)为:li,当鼠标点击后,会发生事件捕获。
捕获阶段:首先window会捕获事件,之后document、body会捕获到事件,再往后div、ul一层一层的捕获到事件。
目标阶段:li事件发生了两次,因为在捕获阶段为其绑定了事件,在冒泡阶段也绑定了事件,所以发生了两次。note:处于目标阶段的事件不按照事件捕获-事件冒泡的顺序执行,而是按其绑定事件的顺序执行。
冒泡阶段:与捕获阶段相反的步骤,将事件一步一步冒泡到window。
解释:target是真正发生事件的dom元素。currentTarget是当前事件发生在那个dom上。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div id="myDiv">
<ul id="myUl">
<li id="myLi">1</li>
</ul>
</div>
</body>
<script>
let oDiv = document.getElementById('myDiv');
let oUl = document.getElementById('myUl');
let oLi = document.getElementById('myLi');
window.addEventListener('click',function(ev){
var ev = ev || window.event;
let target = ev.target || ev.srcElement;
console.log('捕获' + ev.target.nodeName + ev.currentTarget.nodeName);
},true);
document.addEventListener('click',function(ev){
var ev = ev || window.event;
let target = ev.target || ev.srcElement;
console.log('捕获' + ev.target.nodeName + ev.currentTarget.nodeName);
},true);
document.body.addEventListener('click',function(ev){
var ev = ev || window.event;
let target = ev.target || ev.srcElement;
console.log('捕获' + ev.target.nodeName + ev.currentTarget.nodeName);
},true);
oDiv.addEventListener('click',function(ev){
var ev = ev || window.event;
let target = ev.target || ev.srcElement;
console.log('捕获' + ev.target + ev.currentTarget);
},true);
oUl.addEventListener('click',function(ev){
var ev = ev || window.event;
let target = ev.target || ev.srcElement;
console.log('捕获' + ev.target + ev.currentTarget);
},true);
//事件源的冒泡事件在捕获之前
oLi.addEventListener('click',function(ev){
var ev = ev || window.event;
let target = ev.target || ev.srcElement;
console.log('冒泡' + ev.target + ev.currentTarget);
},false)
oLi.addEventListener('click',function(ev){
var ev = ev || window.event;
let target = ev.target || ev.srcElement;
console.log('捕获' + ev.target + ev.currentTarget);
},true)
window.addEventListener('click',function(ev){
var ev = ev || window.event;
let target = ev.target || ev.srcElement;
console.log('冒泡' + ev.target.nodeName + ev.currentTarget.nodeName);
},false);
document.addEventListener('click',function(ev){
var ev = ev || window.event;
let target = ev.target || ev.srcElement;
console.log('冒泡' + ev.target.nodeName + ev.currentTarget.nodeName);
},false);
document.body.addEventListener('click',function(ev){
var ev = ev || window.event;
let target = ev.target || ev.srcElement;
console.log('冒泡' + ev.target.nodeName + ev.currentTarget.nodeName);
},false);
oDiv.addEventListener('click',function(ev){
var ev = ev || window.event;
let target = ev.target || ev.srcElement;
console.log('冒泡' + ev.target + ev.currentTarget);
},false);
oUl.addEventListener('click',function(ev){
var ev = ev || window.event;
let target = ev.target || ev.srcElement;
console.log('冒泡' + ev.target + ev.currentTarget);
},false);
</script>
</html>处于目标阶段的dom事件是按其绑定事件的顺序执行。
问题1:测试onclick事件发生在那个阶段?
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div id="myDiv">
<ul id="myUl">
<li id="myLi">1</li>
</ul>
</div>
</body>
<script>
let oDiv = document.getElementById('myDiv');
let oUl = document.getElementById('myUl');
let oLi = document.getElementById('myLi');
oDiv.onclick = function(){
console.log("测试onclick事件发生在那个阶段?" + this);
};
window.addEventListener('click',function(ev){
var ev = ev || window.event;
let target = ev.target || ev.srcElement;
console.log('捕获' + ev.target.nodeName + ev.currentTarget.nodeName + this);
},true);
document.addEventListener('click',function(ev){
var ev = ev || window.event;
let target = ev.target || ev.srcElement;
console.log('捕获' + ev.target.nodeName + ev.currentTarget.nodeName + this);
},true);
document.body.addEventListener('click',function(ev){
var ev = ev || window.event;
let target = ev.target || ev.srcElement;
console.log('捕获' + ev.target.nodeName + ev.currentTarget.nodeName + this);
},true);
oDiv.addEventListener('click',function(ev){
var ev = ev || window.event;
let target = ev.target || ev.srcElement;
console.log('捕获' + ev.target + ev.currentTarget.nodeName + this);
},true);
oUl.addEventListener('click',function(ev){
var ev = ev || window.event;
let target = ev.target || ev.srcElement;
console.log('捕获' + ev.target + ev.currentTarget.nodeName + this);
},true);
oLi.addEventListener('click',function(ev){
var ev = ev || window.event;
let target = ev.target || ev.srcElement;
console.log('捕获' + ev.target + ev.currentTarget.nodeName + this);
},true)
window.addEventListener('click',function(ev){
var ev = ev || window.event;
let target = ev.target || ev.srcElement;
console.log('冒泡' + ev.target.nodeName + ev.currentTarget.nodeName + this);
},false);
document.addEventListener('click',function(ev){
var ev = ev || window.event;
let target = ev.target || ev.srcElement;
console.log('冒泡' + ev.target.nodeName + ev.currentTarget.nodeName + this);
},false);
document.body.addEventListener('click',function(ev){
var ev = ev || window.event;
let target = ev.target || ev.srcElement;
console.log('冒泡' + ev.target.nodeName + ev.currentTarget.nodeName + this);
},false);
// oDiv.addEventListener('click',function(ev){
// var ev = ev || window.event;
// let target = ev.target || ev.srcElement;
// console.log('冒泡' + ev.target + ev.currentTarget);
// },false);
oUl.addEventListener('click',function(ev){
var ev = ev || window.event;
let target = ev.target || ev.srcElement;
console.log('冒泡' + ev.target + ev.currentTarget.nodeName + this);
},false);
oLi.addEventListener('click',function(ev){
var ev = ev || window.event;
let target = ev.target || ev.srcElement;
console.log('冒泡' + ev.target + ev.currentTarget.nodeName + this);
},false)
</script>
</html>onclick直接绑定的事件发生在冒泡阶段。
事件绑定、解绑、阻止默认
事件绑定:
1、直接获取元素绑定
element.onclick = function(e){
// ...
};优点:简单稳定,浏览器兼容性好。
缺点:
(1)只会在事件冒泡中执行;this指向当前元素。
(2)一个元素只能绑定一个事件处理函数,新绑定的事件处理函数会覆盖旧的事件处理函数;
2、w3c方法:
element.addEventListener('click', function(e){
// ...
}, false);优点:
(1)该方法同时支持事件冒泡和事件捕获。事件阶段取决于事件的第三个参数:true(捕获)、false(冒泡)。this指向当前元素。
(2)可以为同一个元素绑定多个事件,而且不会覆盖先绑定的事件。
缺点:IE不支持,必须使用IE的attachEvent函数代替。
element.attachEvent('onclick', function(){
// ...
});note:
(1)IE中this指向window。
(2)事件名必须以ontype形式命名,不能用type形式。
(3)IE只支持事件的冒泡阶段。
解除事件:
element.removeEventListener('click', function(e){
// ...
}, false);IE:
element.detachEvent('onclick', function(){
// ...
});阻止事件传播
stopPropagation()方法不仅可以阻止事件在冒泡阶段的传播,还能阻止事件在捕获阶段的传播。stopPropagation()很少用到在捕获阶段去阻止事件的传播,大家就以stopPropagation()只能阻止事件在冒泡阶段传播。
IE9之前的IE不支持stopPropagation()方法,而是设置事件对象cancelBubble属性为true来实现阻止事件进一步传播。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div id="myDiv">
<ul id="myUl">
<li id="myLi">1</li>
</ul>
</div>
</body>
<script>
let oDiv = document.getElementById('myDiv');
let oUl = document.getElementById('myUl');
let oLi = document.getElementById('myLi');
oDiv.onclick = function(){
console.log("测试onclick事件发生在那个阶段?" + this);
};
window.addEventListener('click',function(ev){
var ev = ev || window.event;
let target = ev.target || ev.srcElement;
console.log('捕获' + ev.target.nodeName + ev.currentTarget.nodeName + this);
},true);
document.addEventListener('click',function(ev){
var ev = ev || window.event;
let target = ev.target || ev.srcElement;
console.log('捕获' + ev.target.nodeName + ev.currentTarget.nodeName + this);
},true);
document.body.addEventListener('click',function(ev){
var ev = ev || window.event;
let target = ev.target || ev.srcElement;
console.log('捕获' + ev.target.nodeName + ev.currentTarget.nodeName + this);
},true);
oDiv.addEventListener('click',function(ev){
var ev = ev || window.event;
let target = ev.target || ev.srcElement;
console.log('捕获' + ev.target + ev.currentTarget.nodeName + this);
//在捕获阶段阻止事件传播
ev.stopPropagation();
},true);
oUl.addEventListener('click',function(ev){
var ev = ev || window.event;
let target = ev.target || ev.srcElement;
console.log('捕获' + ev.target + ev.currentTarget.nodeName + this);
},true);
oLi.addEventListener('click',function(ev){
var ev = ev || window.event;
let target = ev.target || ev.srcElement;
console.log('捕获' + ev.target + ev.currentTarget.nodeName + this);
},true)
window.addEventListener('click',function(ev){
var ev = ev || window.event;
let target = ev.target || ev.srcElement;
console.log('冒泡' + ev.target.nodeName + ev.currentTarget.nodeName + this);
},false);
document.addEventListener('click',function(ev){
var ev = ev || window.event;
let target = ev.target || ev.srcElement;
console.log('冒泡' + ev.target.nodeName + ev.currentTarget.nodeName + this);
},false);
document.body.addEventListener('click',function(ev){
var ev = ev || window.event;
let target = ev.target || ev.srcElement;
console.log('冒泡' + ev.target.nodeName + ev.currentTarget.nodeName + this);
},false);
oUl.addEventListener('click',function(ev){
var ev = ev || window.event;
let target = ev.target || ev.srcElement;
console.log('冒泡' + ev.target + ev.currentTarget.nodeName + this);
},false);
oLi.addEventListener('click',function(ev){
var ev = ev || window.event;
let target = ev.target || ev.srcElement;
console.log('冒泡' + ev.target + ev.currentTarget.nodeName + this);
},false)
</script>
</html>阻止事件的默认行为:
preventDefault()可以阻止事件的默认行为发生,默认行为是指:点击a标签就转跳到其他页面、拖拽一个图片到浏览器会自动打开、点击表单的提交按钮会提交表单等等,因为有的时候我们并不希望发生这些事情,所以需要阻止默认行为。
IE9之前的IE中,可以通过设置事件对象的returnValue属性为false达到同样的效果。
function cancelHandler(event){
var event=event||window.event;//兼容IE
//取消事件相关的默认行为
if(event.preventDefault) //标准技术
event.preventDefault();
if(event.returnValue) //兼容IE9之前的IE
event.returnValue=false;
return false; //用于处理使用对象属性注册的处理程序
}事件委托
1、为什么要使用事件委托?
在javascript中,添加到页面上的处理程序的个数直接影响到页面运行的性能。导致这一问题的原因是多方面的:
(1)每个函数都是一个对象,都会占用内存。内存中的对象越多,性能就越差。
(2)必须事先指定所有的事件处理程序而导致的dom返问次数,会延迟整个页面的交互。
2、事件委托的原理
事件委托的原理是利用事件冒泡,只指定一个事件处理程序,就可以管理某一类型的所有事件。触发目标试剑石,它其实也要经过事件捕获、处于目标阶段、事件冒泡三个阶段。对于事件委托来说,在事件捕获或者事件冒泡阶段处理并没有优劣之分,从兼容性的角度来说,建议使用事件冒泡模型。
示例可以看上一篇tab标签。