一、事件流
1.概述
在JavaScript中,事件流描述的是事件在DOM结构中传播和被处理的顺序。事件流分为冒泡阶段和捕获阶段。
冒泡阶段(Bubbling Phase):事件首先从最内层的元素开始向父级元素传播,一直传播到最外层的元素。在这个阶段,事件是从内向外传播的。
捕获阶段(Capturing Phase):与冒泡相反,事件从最外层的元素开始传播,一直传播到最内层的元素。在这个阶段,事件是从外向内传播的。
事件的传播过程:
- 捕获阶段:事件从根元素传播到目标元素,依次触发每个元素上绑定的捕获事件处理程序。
- 目标阶段:事件到达目标元素,触发目标元素上绑定的事件处理程序。
- 冒泡阶段:事件从目标元素开始向上冒泡,依次触发每个元素上绑定的冒泡事件处理程序。
在实际开发中,事件处理程序一般是绑定在目标元素上,通过事件冒泡机制,可以在目标元素的父元素上捕获到事件,实现事件委托和事件代理等功能。
简单来说,捕获阶段是从父到子,冒泡阶段是从子到父,而实践开发中都是使用事件冒泡为主。
2.事件捕获
从DOM的根元素开始去执行对应的事件,事件捕获需要写对应代码才能看到效果,语法如下:
DOM.addEventListener(事件类型,事件处理函数,是否使用捕获机制);
说明:
- addEventListener第第三个参数传入true代表是捕获阶段触发(很少使用);
- 若传入false代表冒泡阶段触发,默认就是false;
- 若是用L0事件监听,则只有冒泡阶段,没有捕获。
3.事件冒泡
当一个元素的事件被触发时,同样的事件将会在该元素的所有祖先元素中依次被触发。这一过程被称之为事件冒泡,简单理解就是当一个元素触发事件时,会依次向上调用所有的父级元素的同名事件。
举例:
<!DOCTYPE html>
<html>
<head><meta charset="utf-8"><title>事件冒泡</title><style>.father{width: 50%;height: 300px;background-color: yellow;}.son{width: 50%;height: 300px;background-color: blue;}</style>
</head>
<body>我是爷爷元素<div class="father">我是父元素<div class="son">我是子元素</div></div>
</body>
<script>const father = document.querySelector(".father");const son = document.querySelector(".son");document.addEventListener('click',()=>{alert("爷爷来了");});father.addEventListener('click',()=>{alert("爸爸来了");});son.addEventListener('click',()=>{alert("儿子来了");});
</script>
</html>
此时,若点击子元素,则父元素和爷元素的箭头函数也会被触发。
4.阻止事件冒泡
因为默认就有冒泡模式的存在,所以很容易导致事件影响到父级元素,若想把事件就限制在当前元素内,就需要阻止事件冒泡。
语法:
事件对象.stopPropagation();
示例:
<!DOCTYPE html>
<html>
<head><meta charset="utf-8"><title>事件冒泡</title><style>.father{width: 50%;height: 300px;background-color: yellow;}.son{width: 50%;height: 300px;background-color: blue;}</style>
</head>
<body>我是爷爷元素<div class="father">我是父元素<div class="son">我是子元素</div></div>
</body>
<script>const father = document.querySelector(".father");const son = document.querySelector(".son");document.addEventListener('click',()=>{alert("爷爷来了");});father.addEventListener('click',(effervescence)=>{alert("爸爸来了");effervescence.stopPropagation();});son.addEventListener('click',(effervescence)=>{alert("儿子来了");effervescence.stopPropagation();});
</script>
</html>
此方法可以阻断事件流动传播,不光在冒泡阶段有效,在捕获阶段同样有效。
5.阻止事件默认行为
我们某些情况下需要阻止默认行为的发生,比如阻止链接的跳转,表单域的跳转。
语法:
事件对象.preventDefault();
示例:
<!DOCTYPE html>
<html>
<head><meta charset="utf-8"><title>阻止默认行为</title>
</head>
<body><form action="#"><input type="submit" value="提交"></form>
</body>
<script>const form = document.querySelector('form');form.addEventListener("click",(e)=>{e.preventDefault();})
</script>
</html>
6.解绑事件
(1)on事件方式
直接使用null覆盖就可以实现事件的解绑。
语法:
btn.onclick = function(){alert("我被点击了");
}
btn.onclick = null;
(2)addEventListener方式
必须使用removeEventListener(事件类型,事件处理函数[,获取捕获或者冒泡阶段])方法可以解绑事件,注意匿名函数无法被解绑。
function fn(){alert("我被点击了");
}
btn.addEventListener("click",fn);
btn.removeEventListener("click",fn);
二、一些注意事项和总结
1.鼠标经过事件
- mouseover和mouseout会有冒泡效果;
- mouseenter和mouseleave没有冒泡效果,这里推荐优先使用;
2.两种注册事件的区别
(1)传统on注册(L0)
- 同一个对象,后面注册的事件会覆盖前面注册(同一个事件)
- 直接使用null覆盖就可以实现事件的解绑;
- 都是冒泡阶段执行的
(2)事件监听注册
- 语法:addEventListener(事件类型,事件处理函数,是否使用捕获);
- 后面注册事件不会覆盖前面注册的事件(同一个事件);
- 可以通过第三个参数去确定在冒泡或者捕获阶段执行;
- 必须使用removeEventLintener(事件类型,事件处理函数,是否使用捕获)解绑函数;
- 匿名函数无法被解绑。