js之事件冒泡,事件捕获,事件委托

事件捕获:当一个事件触发后,从window对象出发,不断经过下级节点,直到目标节点,这个过程就是事件捕获。自内而外,从根到叶,从小到大。

事件冒泡:即是事件开始时由最具体的元素接收,然后逐级向上传播到较为不具体的节点。当事件到达目标节点之后,会沿着捕获阶段的路线原路返回,同样所有经过节点会被触发。即是自内而外,从叶到根,从小到大。

说起来很复杂,其实用一个示列就很容易理解

<!DOCTYPE html>
<html>
	<head>
		<meta charset="UTF-8">
		<title></title>
		<style type="text/css">
			div{
				margin: 50px;
				overflow: hidden;
				
			}
			#div1{
				
				width:300px;
				height: 300px;
				background-color: #008000;
			}
			#div2{
				width:200px;
				height:200px;
				background-color: #FFFF00;
			}
			#div3{
				width:100px;
				height: 100px;
				background-color: #FF0000;	
			}
			
		</style>
		<script type="text/javascript">
			window.onload = function(){
			var div1 = document.getElementById("div1");
			var div2 = document.getElementById("div2");
			var div3 = document.getElementById("div3");
			/*
			 *DOM2级事件定义了两个方法,用于处理指定和删除事件处理程序的操作
			 * addEventListener(),removeEventLister()
			 * 接收三个参数
			 * 1.要处理的事件名,2作为事件处理程序的函数,3一个布儿值,默认为false
			 * 如果布儿值为true,表示在捕获阶段调用事件处理程序
			 * 如果为默认的false,则表示在冒泡阶段调用事件处理程序
			 * 
			 * */
			div1.addEventListener("click",function(){
				console.log("我是div1");
				alert(this.id);
				
			},false);
			     
			div2.addEventListener("click",function(){
				console.log("我是div2");
				alert(this.id);
				
			},false);
			div3.addEventListener("click",function(){
				console.log("我是div3");
				alert(this.id);
				
			},false);
			}
		</script>
	</head>
	<body>
		<div id="div1">
			<div id="div2">
				<div id="div3">
					
				</div>
			</div>
		</div>
		
	</body>
</html>

很简单的写了一个测试,按照冒泡规则,当我点击div3的时候,会继续向上冒泡,会显示div2,div1。
在这里插入图片描述
当我点击div3之后,理论上应该是div3—>div2—>div1
在这里插入图片描述
然后事件的捕获,只需要将false改为true,就变成了事件的捕获,这时候如果点击div3的话,按照事件捕获规则,会从根到叶,也就是说会div1–>div2–>div3,
在这里插入图片描述
注意:

  1. 在ie浏览器中并不支持这两个函数,ie支持attachEvent()和detachEvent()。
  2. 这两个方法接收相同的两个参数,事件处理程序名称和事件处理程序函数
  3. ie8之前只支持事件冒泡,所以通过attachEvent()添加的事件处理程序都会被添加到冒泡阶段 。
  4. 但是attachEvent()与DOM方法不同的是,不是以添加他们的顺序执行,而是以相反的顺序被触发。
  5. detachEvent()移除相应的事件处理函数。条件是必须提供相同的参数,也就是说添加的匿名函数不能被移除
var btn.document.getElementById("mybtn");
btn.attachEvent("onclick",function(){
    alert("click");
});
btn.attachEvent("onclick",function(){
    alert("hello world");
});

//为同一个按钮绑定了两个不同的事件处理程序,但是却不按照添加的先后顺序执行
//而是以相反的顺序被触发

问题又来了
能不能阻止事件冒泡呢?答案当然是可以的!

//利用这个函数就可以阻止事件冒泡了
event.stopPropagation();
//如果将这行代码加入到刚刚那个示例的函数中去,就能阻止事件的冒泡了,这个时候就不会再冒泡了
//div1,div2不会被触发
var document.getElementById("div3");
div3.addEventListener("click",function(){
               //阻止事件冒泡
                event.stopPropagation();
				console.log("我是div3");
				alert(this.id);
				
			},false);
----------------------------------------------------------
function() {
    // IE里阻止冒泡
    window.event.cancelBubble = true;
    // IE里获取事件源的id
    var srcID = window.event.srcElement.id;
}
function(event) {
    // 非IE里阻止冒泡
    event.stopPropagation();
    // 非IE里获取事件源的id
    var ID_src = event.target.id;
}

阻止冒泡的作用:
有时候我们需要阻止冒泡,比如存在这么一个页面A–>B–>C,c是最内层元素,如果用户点击c,则跳转页面,点击b,无反应,点击a,关闭页面,这个时候就需要阻止冒泡了,我们给A加一个点击事件关闭,给C加一个单击跳转事件,这时候如果点B,会产生冒泡就会关闭页面,如果我们不想关闭页面,那么就需要给B绑定一个单击响应事件,添加上阻止冒泡函数event.stopPropagation();即可

事件委托:
事件委托又称为事件代理,其实很好理解,就是A把事情委托给B去解决,举个例子

/*假如到了中午吃饭的时间,咦嘿,可是有的室友由于忙着玩游戏或者写作业不想离开宿舍,
* 但是又不能不吃饭,于是呢就拜托一个室友去买饭(事件委托),这样一个室友可以带很多份饭回来,
* 根据你的要求来买饭,帮你付钱。这样就会省很多事,并且如果还有其他宿舍的人也不想出去了,也
* 想让带饭,这样也可以继续带饭。(给新增的节点绑定事件)
*
*/

同理,加入ul中存在很多的li,平常li很少,可以一个一个绑定事件但是假如li很多,成千上万个呢,这时候一个个绑定一个事件,那就变得非常麻烦了,即使用循环来绑定也需要很长时间,所以这个时候事件委托就展现了它的作用,只需要给ul绑定事件,li就不需要再绑定了,这样你点击任意一个ul中的li,根据事件冒泡规则,都会触发ul中的绑定事件。这样,问题就会变得简单化了

		window.onload = function() {
			var ul_1 = document.getElementById('ul_1');
			function clickLi() {
				alert('你点击了li');
			}
			//为ul绑定单击响应事件
			ul_1.addEventListener('click', function(event) {
				// event事件对象,有一个target属性,指向事件的目标
				var src = event.target;
				// target里面的属性是非常多的,id名、class名、节点名等等都可以取到
				if(src.nodeName.toLowerCase() == 'li') {
					// 我们判断如果target事件源的节点名字是li,那就执行这个函数
					//toLowerCase()是因为li不区分大小写,所以需要转换为小写
					clickLi()}
			});

		};





//简单提一下currentTarget ,this,target
----------------------------------------------------------------
//在事件处理程序内部,对象this,始终等于currentTarget的值
//target则只包含事件的实际目标
//如果将事件处理程序指定给了目标元素,则this、currentTarget、和target包含相同的值
----------------------------------------------------------------
var btn = document.getElementById("mybtn");
btn.onclick = function(event){
    alert(event.currentTarget === this);   //true
    alert(event.target === this);          //true
    //由于click事件的目标是按钮,因此这三个值是相等的。
};
-----------------------------------------------------------------
document.body.onclick = function(event){
    alert(event.currentTarget === document.body);       //true
    alert(this === documnet.body);                      //true
    alert(event.target === document.getElementById("mybtn"));   //true
    //this和currentTarget都等于document.body,因为事件处理程序是注册到这个元素上的
    //target元素等于按钮,是因为它是click事件的真正目标。
    //由于按钮上并没有注册事件处理程序,结果click事件就冒泡到document.body,在那里事件得到处理。

}
-----------------------------------------------------------------

上述示例我们给ul绑定了单击响应事件,所有的li都可以触发函数,但是我们发现,这样只能是一个函数,如果我们想要给不同的li绑定不同的函数那怎么做呢?

<script type="text/javscript">
      window.onload = function(){
				//设置li的响应函数
				function clickLi_1(){
					alert("你点击的为li_1")
				}
				function clickLi_2(){
					alert("你点击的为li_2")
				}
				function clickLi_3(){
					alert("你点击的为li_3")
				}
				
				var ul_1 = document.getElementById("ul_1");
				ul_1.addEventListener("click",function(event){
					//获取实际目标的id
					var ID_src = event.target.id;
					if(ID_src == "Li_1"){
						clickLi_1();
					}
					else if(ID_src == "Li_2"){
						clickLi_2();
					}
					else if(ID_src == "Li_3"){
						clickLi_3();
					}
				},false);
			};
</script>

<body>
		
		<ul id="ul_1">
			<li id="li_1">1</li>
			<li id="li_2">2</li>
			<li id="li_3">3</li>
		</ul>
		
	</body>

它的原理就是利用监听父元素来给子元素绑定不同的事件,从而减少了监听的次数,提高了速度,这就是所谓的事件委托。
事件委托的原理是利用事件冒泡,如果冒泡被阻止了,那么我们就不能用事件委托的方式去监听事件了!!!

发布了29 篇原创文章 · 获赞 59 · 访问量 8696

猜你喜欢

转载自blog.csdn.net/weixin_42878211/article/details/105162120
今日推荐