一、事件的Event对象和属性
首先来看一段代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>event_bubble</title>
</head>
<style>
</style>
<body>
<div id="grand" class="box">
<div id="far" class="box">
<div id="son" class="box">
<button id="butn" class="butn">click</button>
</div>
</div>
</div>
<script>
document.getElementById("butn").onclick = function(){
console.log(event)
}
</script>
</body>
</html>
打印的内容如下图:
其中的一些属性:
view: 只读,对象,通常为发生事件的window对象;
type: 只读,字符串,发生事件类型:(图中为:click);
eventPhase: 只读,数字,事件流经历的阶段(1、捕获;2、目标;3、冒泡);
target: 只读,对象,派发事件的目标对象;
currentTarget: 只读,对象,当前在调用监听器的对象,就是当前 addEventListener 绑定的对象;
button: 只读,数字,被按下的鼠标键整数
(1、鼠标左键;2、鼠标右键;3、鼠标左右同时;4、鼠标中键)
(firefox 中:0、左键; 1、中间;2、右键)
cancelBubble: 布尔值,可以阻止事件冒泡,相当于 preventBubble();
returnValue: 布尔值,是否阻止浏览器默认事件动作,相当于 e.preventDefault();
screenX,screenY: 鼠标指针事件发生时相对于显示器左上角的位置;
cancelLable: 只读,布尔值,处理事件的默认行为是否可以阻止;
若值为 true ,则 event 的preventDefalut 方法可以使用,
若为 false,则不可用;
bubbles: 可读,布尔值,事件是否开启了冒泡;
preventDefault(): 阻止浏览器的默认行为,IE中可以 return false 方法类似;
stopPropagation(): 停止当前事件流传播,但不会停止当前处理的对象;
preventBubble(): 阻止冒泡,不建议使用,用stopPropagation()代替;
preventCapture: 阻止捕获,不建议使用,用stopPropagation()代替;
二、事件冒泡
js的事件传播中,当一个元素触发了事件后,事件会逐级向上传递给父辈,知道 document 为止。有的浏览器可能会到 window 为止,就像气泡向上冒出,这就是事件冒泡。eg:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>event_bubble</title>
</head>
<style>
.box{
overflow: hidden;
padding: 20px;
border: 1px solid #333333;
}
</style>
<body>
<div id="grand" class="box">
<div id="far" class="box">
<div id="son" class="box">
<button id="butn" class="butn">click</button>
</div>
</div>
</div>
<script>
document.getElementsByTagName("body")[0].onclick = function(){
console.log("body");
}
document.getElementById("grand").onclick = function(){
console.log("grand");
}
document.getElementById("far").onclick = function(){
console.log("far");
}
document.getElementById("son").onclick = function(){
console.log("son");
}
document.getElementById("butn").onclick = function(){
console.log(event)
console.log("butn");
}
</script>
</body>
</html>
如图:点击 按钮后:同时触发了,butn,son,far,grand,body,此类型成为事件冒泡。
所以开发中为了避免此类情况,需要阻止事件冒泡;
1、 使用标准的 w3c 的方式,e.stopPropagation(); 这里的stopPropagation的标准的事件对象的一个方法调用即可。
2、非标准的IE方式;window.event,cancelBubble = true; 这里的cancelBubble 是 IE 事件对象的属性,设为 true 即可。
document.getElementById("butn").onclick = function(){
stopBubble();
}
var stopBubble = function(e) {
// 如果提供了事件对象,则这是一个非IE浏览器
if( e && e.stopPropagation){
// 支持 w3c 的 stopPropagation(); 方法
e.stopPropagation();
}else {
// 否则使用 IE 的方式来获取取消事件 冒泡
window.event.cancelBubble = true;
}
}
3、在 vue 开发中,methods 只有纯粹的数据逻辑,而不是去处理 DOM 事件细节。所以 vue 为 v-on 提供了事件修饰符;在标签上写.stop
<p>
<button @click.stop="handleClick"></button>
<button v-on:click.stop="handleClick"></button>
</p>
三、事件捕获
与事件冒泡相反,它从顶层元素开始,直到事件触发底层元素。
如何触发事件捕获
target.addEventListener(type, function(){}, boolean);
// target为 document 元素
// type 为事件类型
// boolean 布尔值, true 时为事件捕获 false 为事件冒泡
// eg:
document.getElementById("grand").addEventListener("onclick", function(){
console.log("grand");
}, true ); // 为id为 grand 的元素添加事件捕获
三、事件委托
what: 事件委托就是利用事件冒泡原理,指定一个事件程序来管理一类的所有事件。
why: 一般来说。dom 元素需要有事件处理时,给元素添加事件和程序即可,但是当添加事件的 dom 元素数量过多时就会影响页面的整体性能,比如:有 100 个列表中的 button 需要添加 click 点击事件,我们会利用 for 循环的方法遍历所有相应的 button 元素,然后添加 click 点击事件。这样会需要不断的与 dom 节点进行交互,访问的 dom 越多,引起浏览器重绘和重排的次数就会变多,从而延长整个页面的交互处理时间。
所以需要事件委托把所有的操作放在一个元素或者程序中,只进行一次 dom 操作,可以大大的减少了 dom 操作的次数,提高性能。
how: 利用冒泡原理,将所有子元素的事件触发,写在其父级元素上。委托代为执行事件。
eg:
<ul id="list">
<li class="li"><button>1</button></li>
<li class="li"><button>2</button></li>
<li class="li"><button>3</button></li>
<!-- 中间省略 -->
<li class="li"><button>9</button></li>
<li class="li"><button>10</button></li>
</ul>
要点击 button 时,通常写的代码:
var btns = document.getElementsByTagName("button");
for(let i = 0; i < btns.length; i++){
btns[i].onclick = function () {
alert(btns[i].innerHtml);
}
}
获取到 button ,点击 button ,要多次寻找目标的 button 的位置,才能执行最后的操作。利用事件委托把点击事件的触发写在父级元素 ul 上,那么所有的 li 和 button 都会被触发,Event 对象提供了一个属性到 target ,可以返回事件的目标节点, target 可以表示当前的事件操作的 dom元素,但不是真的 dom ,具有兼容性,标准浏览器用 ev.target, ie 用event.srcElement 可获取当前目标节点的位置。
document.getElementById("list").onclick = function() {
var ev = event || window.event;
var target = ev.target || ev.srcElement;
if(target.nodeName.toLowerCase() == "BUTTON"){
alert(target.innerHtml);
}
}
可以委托的事件:click, mousedown , mouseup , keydown , keyup , keypress;
mouseenter , mouseleave , focus , blur 等不适合事件委托