事件
事件是用户在使用浏览器查看页面时的某种操作。针对用户的操作,我们可以设置相应函数来对发生的事件进行进一步的交互处理。例如点击后跳转页面,验证按钮的点击发送验证码等。
事件具有很多种类,点击,双击,鼠标移动到某区域等。
开发者可以设定针对事件发生时页面以什么方式回应(自定义事件响应函数)。
什么是事件流
事件流描述的是从页面接受事件的顺序。
事件发生时会在元素节点间按照特定的顺序传递,这个传递过程就是DOM事件流。
DOM事件流分为三个阶段
- 捕获阶段:浏览器从HTML文档,到body标签,再逐层深入到事件元素。途中不会触发各级元素的对应事件。从另一角度理解:为了寻找事件元素而从上到下依次寻找。
- 当前目标阶段:捕获阶段找到事件元素,该事件元素的对应事件触发。
- 冒泡阶段:事件元素的事件触发后,浏览器按照捕获阶段的原路返回。返回过程中途径每一级元素,便触发对应元素的同一事件。
我们可以代码演示对应效果。
<!DOCTYPE html>
<html lang="zh-Hans">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>测试</title>
<style>
#box{
width:200px;
height: 200px;
background-color: #ccc;
}
#son{
width: 150px;
height: 150px;
background-color: #0f0;
}
#inner{
width: 100px;
height: 100px;
background-color: #0ff;
}
</style>
</head>
<body>
<div id="box">
<div id="son">
<div id="inner"></div>
</div>
</div>
<script>
//获取元素部分
var box=document.getElementById("box");
var son=box.children[0];
var inner=son.children[0];
//绑定事件函数
box.onclick=function(){console.log("box响应点击");}
son.onclick=function(){console.log("son响应点击");}
inner.onclick=function(){console.log("inner响应点击");}
</script>
</body>
</html>
页面显示如下
点击中心的蓝色div,输出结果为
可以看出针对嵌套元素事件,浏览器会先进行内层元素的事件触发,再进行外层元素的事件触发。
当我们在addEventListener中绑定事件函数时指定事件捕获时,该顺序发生变化。
//获取元素部分
var box=document.getElementById("box");
var son=box.children[0];
var inner=son.children[0];
//绑定事件函数
// box.onclick=function(){console.log("box响应点击");}
// son.onclick=function(){console.log("son响应点击");}
// inner.onclick=function(){console.log("inner响应点击");}
box.addEventListener("click",function(){console.log("box响应点击");},true);
son.addEventListener("click",function(){console.log("son响应点击");},true);
inner.addEventListener("click",function(){console.log("inner响应点击");},true);
当我们使用addEventListener方法添加事件时,可以设置捕获传递事件。当混合使用捕获传递和冒泡传递时,事件触发既有从外到内(DOM树中是从上到下)的以捕获传递的事件,也有从内到外(DOM树中是从下到上)的事件。这时,事件的顺序应该是怎样排列呢?
直接说结论:事件触发是首先按照捕获的顺序确定事件元素,再按照冒泡的顺序依次触发事件。对于设置捕获传递的事件(特例,因为一般默认都是冒泡传递),在捕获阶段直接触发。对于设置冒泡传递的事件,在捕获阶段和当前目标阶段完成后再依次向上传递,触发事件。
<!DOCTYPE html>
<html lang="zh-Hans">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>测试</title>
<style>
#box{
width:200px;
height: 200px;
background-color: #ccc;
}
#son{
width: 150px;
height: 150px;
background-color: #0f0;
}
#inner{
width: 100px;
height: 100px;
background-color: #0ff;
}
</style>
</head>
<body>
<div id="box">
<div id="son">
<div id="inner"></div>
</div>
</div>
<script>
//获取元素部分
var box=document.getElementById("box");
var son=box.children[0];
var inner=son.children[0];
//绑定事件函数 直接绑定默认冒泡传递。
box.onclick=function(){console.log("box响应点击");}
// son.onclick=function(){console.log("son响应点击");}
inner.onclick=function(){console.log("inner响应点击");}
//使用addEventListener可以设置捕获传递。
// box.addEventListener("click",function(event){console.log("box响应点击");},true);
son.addEventListener("click",function(){console.log("son响应点击");},true);
// inner.addEventListener("click",function(){console.log("inner响应点击");},true);
</script>
</body>
</html>
可以通过设置不同元素为冒泡或捕获传递方式来实现这个问题的解答。
阻止冒泡或捕获
event.stopPropagation()方法可以阻止事件的进一步传递。
//获取元素部分
var box=document.getElementById("box");
var son=box.children[0];
var inner=son.children[0];
//绑定事件函数
// box.onclick=function(){console.log("box响应点击");}
// son.onclick=function(){console.log("son响应点击");}
// inner.onclick=function(){console.log("inner响应点击");}
box.addEventListener("click",function(event){event.stopPropagation();console.log("box响应点击");},true);
son.addEventListener("click",function(){console.log("son响应点击");},true);
inner.addEventListener("click",function(){console.log("inner响应点击");},true);
这时阻止了事件进一步向下传递(向内部传递)。这里展示的是阻止捕获。
这个方法也可以阻止事件冒泡。与上面不同的是需要将stopPropagation在内层的元素事件中使用。
//获取元素部分
var box=document.getElementById("box");
var son=box.children[0];
var inner=son.children[0];
//绑定事件函数
box.onclick=function(){console.log("box响应点击");}
son.onclick=function(){console.log("son响应点击");}
inner.onclick=function(){event.stopPropagation();console.log("inner响应点击");}
这里在冒泡传递的情况下,由inner元素向上传递事件,在起始的位置就被stopPropagation方法阻止冒泡。即使点击内层元素也只有一个元素响应事件。
笔者尚处于学习阶段,本文如有错误或不足之处,欢迎指正。