16_事件冒泡和捕获的执行顺序了解吗?什么是事件委托

一、事件冒泡和捕获

运行条件:当一个事件发生在具有父元素的的元素上时,现代浏览器根据事件添加时的设置来执行(冒泡或者捕获)

通过 addEventListener() 的第三个属性来设置事件是通过捕获阶段注册的(true),还是冒泡阶段注册的(false)。默认情况下是false。

1、事件冒泡

从实际操作的元素(事件)向上级父元素一级一级执行下去,直到达到<html>

有些时候父元素和子元素都定义了click事件,但是不希望点击子元素的时候执行父元素的click事件(例如dialog弹窗的遮罩层如果是父元素,而dialog弹窗内容层是子元素,同时可以通过点击遮罩层来关闭弹窗,但是点击内容层不关闭弹窗),可以通过stopPropagation()在子元素上阻止冒泡。

2、事件捕获(一般不会用到)

浏览器检查元素的最外层祖先<html>,是否在捕获阶段中注册了一个onclick事件处理程序,如果是,则运行它。
然后,它移动到<html>中的下一个元素(点击的元素的父元素),并执行相同的操作,然后是下一个元素(点击的元素的父元素),依此类推,直到到达实际点击的元素。

二、事件捕获和冒泡的区别(执行顺序的不同)

// css
#div1 {
    width: 400px;
    height: 400px;
    background: #f00;
}
#div2 {
    width: 300px;
    height: 300px;
    background: #0f0;
}
#div3 {
    width: 200px;
    height: 200px;
    background: #00f;
}
#div4 {
    width: 100px;
    height: 100px;
    background: #f0f;
}

// html
<div id="div1">我是div1
<div id="div2">我是div2
<div id="div3">我是div3
<div id="div4">我是div4</div>
</div>
</div>
</div>
</body>

// javascript
var div1=document.getElementById("div1");
var div2=document.getElementById("div2");
var div3=document.getElementById("div3");
var div4=document.getElementById("div4");

div1.addEventListener("click",function(){
    alert("我是div1");
})

div2.addEventListener("click",function(){
    alert("我是div2");
})

div3.addEventListener("click",function(){
    alert("我是div3");
})

div4.addEventListener("click",function(){
    alert("我是div4");
})

冒泡的执行顺序:当我们点击div1的时候,只弹出“我是div1”,但是当我们点击div2的时候,就先弹出“我是div2”,再弹出“我是div1”,当我们点击div4 的时候,则是4-3-2-1这样的顺序,这样就叫做冒泡,它就像鱼儿吐泡泡一样,从下到上,泡泡从水下上来泡泡会变得越来越大,从这个角度思考,先从子元素开始执行,然后是父元素,再然后是祖先元素,他们的等级也是越来越大的。在addEventListener里,false是默认的,表示的是冒泡。

捕获的执行顺序:在addEventListener里,第三个参数设置为true,表示的是捕获。将上面的代码中添加进第三个参数true,再运行代码,会发现这时,当我们点击div4的时候,出现的顺序是1-2-3-4,它是从祖先元素开始慢慢找,最后找到我们的点击目标,可以将这个行为理解为就像警察叔叔抓坏人一样,逐渐的缩小抓捕范围,最后确定到某一个人身上,所以这个过程叫做捕获。

dom元素中,既有冒泡,又有捕获的执行顺序:w3c规定,任何发生在w3c事件模型中的事件,首是进入捕获阶段,直到达到目标元素,再进入冒泡阶段。示例:

div1.addEventListener("click",function(){
    alert("div1");
}, false);
div2.addEventListener("click",function(){
    alert("div2");
}, true);
div3.addEventListener("click",function(){
    alert("div3");
}, false);
div4.addEventListener("click",function(){
    alert("div4");
}, true);

这时的div1和div3是冒泡事件,div2和div4是捕获事件,当我们点击div4以后,弹出的顺序是2-4-3-1。因为我们先执行捕获过程,在这个例子中div2和div4是捕获的,那么捕获又是从大到小,所以,先弹出div2,再弹出div4,捕获结束以后就该是冒泡了。冒泡的顺序是从小到大,从子到父,所以就先弹出div3,再就是div1,所以最后的顺序是2-4-3-1。如果我们点击div3,结果会是2-3-1,同样的道理,先捕获,捕获是从div1开始到div3,这中间只有div2是捕获,div4并没有执行到,因为我们点击的目标是div3,后面的步骤和前面的过程一样,先3后1。

最后一个问题,代码示例:

div1.addEventListener("click",function(){
    alert("div1");
}, false);
div2.addEventListener("click",function(){
    alert("div2_捕获");
}, true);
div2.addEventListener("click",function(){
    alert("div2_冒泡");
}, false);

那么,当我们点击div2的时候,结果是 div2_捕获 -> div_2冒泡 -> div1的顺序,代码修改:

div1.addEventListener("click",function(){
    alert("div1");
}, false);
div2.addEventListener("click",function(){
    alert("div2_冒泡");
}, false);
div2.addEventListener("click",function(){
    alert("div2_捕获");
}, true);

这时当我们点击div2的时候,结果是 div2_冒泡 -> div_2捕获 -> div1的顺序

综上结论:绑定在被点击元素的事件是按照代码的顺序发生的,其他非绑定的元素则是通过冒泡或者捕获的触发。按照W3C的标准,先发生捕获事件,后发生冒泡事件。

事件的整体顺序是:非目标元素捕获 -> 目标元素代码顺序 -> 非目标元素冒泡。

三、事件委托

1、使用场景

如果你想要在大量子元素(包括动态添加的)中单击任何一个就可以运行一段代码,这个时候可以把事件监听器设置在父节点上。

2、好处

  • 只在内存中开辟了一块空间,节省资源同时减少了dom操作,提高性能
  • 对于新添加的元素也会有之前的事件

3、事件委托同捕获和冒泡的关系

  • 事件捕获和冒泡是现代浏览器的执行事件的两个不同阶段
  • 事件委托是利用冒泡阶段的运行机制来实现的

4、实例:ul中触发每个li来改变他们的背景颜色

// html
<ul id='ul'>
    <li>111111</li>
    <li>222222</li>
    <li>333333</li>
</ul>
<button id='button'>添加元素</button>


// javascript
window.onload = function() {
      let oUl = document.getElementById('ul');
      let aLi = oUl.getElementsByTagName('li');
      let but = document.getElementById('button');
      let now = 3;

      // 事件源:event对象,不管在哪个事件中,只要你操作的哪个元素就是事件源
      // ie:window.event.srcElement
      // 标准:event.target
      oUl.onmouseover = function(e){
        let ev = e || window.event;
        let target = ev.target || ev.srcElement;
        if(target.nodeName.toLowerCase() == 'li'){
          target.style.background = 'red';
        }
      }

      oUl.onmouseout = function(e){
        let ev = e || window.event;
        let target = ev.target || ev.srcElement;
        if(target.nodeName.toLowerCase() == 'li'){
          target.style.background = '';
        }
      }

      but.onclick = function(){
        now ++;
        let newLi = document.createElement('li');
        newLi.innerHTML = 111111 * now;
        oUl.appendChild(newLi);
      }
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,383评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,522评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,852评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,621评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,741评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,929评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,076评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,803评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,265评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,582评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,716评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,395评论 4 333
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,039评论 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,798评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,027评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,488评论 2 361
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,612评论 2 350