总结笔记,关于JavaScript事件。
1、js事件流
上图为js事件流的全过程,从图中可以得知:
一个完整的事件流是从window开始,最后回到window的一个过程。
事件流分为三个阶段,1-5是捕获阶段,5-6是目标阶段,6-10是冒泡阶段。
2、addEventListener的第三个参数
element.addEventListener(event, function, useCapture);
我们平常用的addEventListener方法中,一般只会用到前两个参数,第一个是需要绑定的事件,第二个是触发事件后要执行的函数。第三个参数默认值是false,表示在事件冒泡阶段调用事件处理函数,如果将其设置为true,则表示在事件捕获阶段调用事件处理函数。
<!DOCTYPE html>
<html>
<head>
<title>事件委托</title>
<style>
body,html{
margin:0px;
padding:0px;
}
#parent{
width: 300px;
height: 300px;
background-color: #808080;
display:flex;
align-items:center;
justify-content:center;
margin:100px;
}
#child {
width: 200px;
height: 200px;
background-color: #999;
}
</style>
</head>
<body>
<div id="parent">
<div id="child">
子元素
</div>
</div>
<script>
var parent = document.getElementById('parent');
var child = document.getElementById('child');
document.body.addEventListener('click', function (e) {
var ev = e || window.event;//获取事件对象,后者兼容IE的写法
//console.log(e);
var target = ev.target || ev.srcElement;//获取事件源对象,后者兼容IE
//console.log(target);
console.log('事件冒泡——click body');
});
parent.addEventListener('click', function () {
console.log('事件冒泡——click parent');
}, false);
child.addEventListener('click', function () {
console.log('事件冒泡——click child');
});
</script>
</body>
</html>
点击body元素结果是:
点击parent元素结果是:
点击child元素结果是:
事件触发顺序是由内到外,这就是事件冒泡。child -> parent -> body。
将js代码改为如下:
document.body.addEventListener('click', function (e) {
var ev = e || window.event;//获取事件对象,后者兼容IE的写法
var target = ev.target || ev.srcElement;//获取事件源对象,后者兼容IE
console.log('事件捕获——click body');
},true);
parent.addEventListener('click', function () {
console.log('事件捕获——click parent');
}, true);
parent.addEventListener('click', function () {
console.log('事件传播——click parent');
}, false);
child.addEventListener('click', function () {
console.log('事件冒泡——click child');
},false);
点击body元素结果是:
点击parent元素结果是:
点击child元素结果是:
父元素通过事件捕获的方式注册了click事件,所以在事件捕获阶段就会触发,然后到了目标阶段,即事件源,之后进行事件冒泡,parent同时也用冒泡方式注册了click事件,所以这里会触发冒泡事件,最后到根节点(body)。这就是整个事件流程。
3、阻止事件冒泡
兼容IE的写法:
function stopPropagationSelf(e) {
if (e && e.stopPropagation) {
e.stopPropagation();
} else {//IE方式取消事件冒泡
window.event.cancelBubble = true;
}
}
4、阻止默认事件
兼容IE的写法:
function preventDefaultSelf(e) {
if (e && e.preventDefault) {
e.preventDefault();
} else {//IE方式阻止默认事件
window.event.returnValue = false;
}
}
5、事件委托
事件委托也称事件代理,事件委托就是利用事件冒泡原理,只指定一个事件处理程序,就可以管理某一类型的所有事件。
一般来讲,会把一个或一组元素的事件委托到他的父层或更外层元素上,真正绑定事件的是外层元素,当事件响应(事件捕获)到真正需要绑定的元素上时,会通过事件冒泡机制,从而触发它的外层元素的绑定事件,然后在外层元素上去执行事件处理函数。
例子:
现在有如上一个todolist,需要有以下功能:
1、点击‘add’按钮时,列表中新增item,内容为文本框输入内容
2、todo Item 点击’完成’按钮时自动删除
我们知道的给DOM绑定事件的方法有以下几种:
嵌入html
这种很明显缺点是代码耦合,不便于维护和开发
<li>事件1 <button onclick="complete">完成</button></li>
DOM绑定
这种比较常见,比较简单易懂,而且兼容性较好,但是也有缺陷,只能实现一个绑定,也就是说我们再为element绑定第二个click事件时候,会覆盖掉之前的click事件
oBtn.onclick = complete
使用事件监听函数
oBtn.addEventListener('click',complete)
兼容性写法:
if (element.addEventListener) {
element.addEventListener(type, handler, false);
} else if (element.attachEvent) {
element.attachEvent("on" + type, handler);
} else {
element["on" + type] = handler;
}
需求很简单,但是有两点需要注意:
如果给‘完成’按钮嵌入DOM事件,因为不断有新item的生成,每次生成都要重新绑定,代码不便管理。
如果给‘完成’按钮绑定事件,则需要循环遍历每一个‘完成’按钮,不断访问DOM,按钮越多,访问次数越多,性能越低。
为了解决上述两个问题,‘事件代理’便是完美的解决方案。
实现思路
将事件绑定到父元素ul上,当用户点击按钮时,通过事件流,冒泡到父元素ul,从而执行回调。
<!DOCTYPE html>
<html>
<head>
<title>事件委托</title>
<style>
body,html{
margin:0px;
padding:0px;
}
</style>
</head>
<body>
<div class="container">
<h1>TODO List</h1>
<input type="text" id="itemInput"/><input type="button" value="add" onclick="addItem();"/>
<ul id="parentUl">
<li>事情1<button>完成</button></li>
<li>事情2<button>完成</button></li>
</ul>
</div>
<script>
var Ul = document.getElementById('parentUl');
Ul.addEventListener('click', function (e) {
var ev = e || window.event;
var target = ev.target || ev.srcElement;
if (target.tagName.toLowerCase() === 'button') {
var liNode = target.parentNode || target.parentElement;
Ul.removeChild(liNode);
}
});
function addItem() {
var itemInput = document.getElementById('itemInput');
var item = document.createElement('li');
item.innerHTML = itemInput.value + '<button>完成</button>'
Ul.appendChild(item);
}
</script>
</body>
</html>
6、事件委托的优点
1、只绑定一次事件,无需频繁访问DOM,提高性能。
2、当有新的DOM生成时,无需重复绑定事件,代码清晰度高。
缺点:
很多事件是不能冒泡的,比如说focus、blur、load本身就没有冒泡的特性,自然就不能用事件委托了。