捕获、冒泡与事件委托

学这个的时候,这东西把我搞得很头晕。于是决定写一篇Blog出来把这玩意说清楚,让和我一样在这个坑里打滚的人不那么晕菜。尽量照顾到每个细节,标准是让自己再看的时候不管忘记了多少知识点都能看得懂。

那么,开始吧。

基础知识

1.鼠标点击。

我的鼠标左键点击了网页,然后网页获取到了我的点击事件,调取了DOMAPI——真的是这样吗?

不,不是这样的。鼠标首先应该是系统级的API,所以最先知情的应该是系统。系统得知你点击了鼠标,并把这个事件传递给浏览器,然后浏览器才会通过DOMAPI通知网页。这很重要。

2.事件绑定

DOM的事件元素绑定其实没啥好说的,无非就是绑定的方法不同。一种是直接绑定,就是xx.onclick这样的。简单粗暴,DOM level0就支持。等等,什么是DOM level0?这又引出了一个隐藏知识点。DOM level0 指的是DOM level1事件之前即支持的事件,DOM level1时间基准为W3C制定第一个标准。另外一种,就是DOM level2新增的事件监听,见下文。

3.child元素和parent元素的通知问题。
根据1,那么我点击了child元素并且child元素被监听,那么就会出现一个通知顺序的问题。是父元素先通知,还是子元素先通知?那么,就顺理成章的进入了下一个介绍:

捕获和冒泡

要说捕获和冒泡,得先介绍事件监听。事件监听和绑定其实差不了太多,但是新增了一个特点,就是无论监听多少次,都不会覆盖掉前面的事件。讲白了就是事件绑定plus。而捕获和冒泡其实本质上只是Child和Parent通知的两种顺序。捕获指的是parent先通知,child后通知,而冒泡则相反.

捕获只有在早期的浏览器中才使用,第一个尝试冒泡的,居然是现在被批判为封建保守的IE。有因有果,这都是有故事的啊~

一大堆概念解释:

事件捕获:当某个元素触发某个事件(如onclick),顶层对象document就会发出一个事件流,随着DOM树的节点向目标元素节点流去,直到到达事件真正发生的目标元素。在这个过程中,事件相应的监听函数是不会被触发的。

事件目标:当到达目标元素之后,执行目标元素该事件相应的处理函数。如果没有绑定监听函数,那就不执行。

事件冒泡:从目标元素开始,往顶层元素传播。途中如果有节点绑定了相应的事件处理函数,这些函数都会被一次触发。

有些情况下,捕获/冒泡并不被我们所需要,比如调试。那么我们可以使用e.stopPropagation()(Firefox)或者e.cancelBubble=true(IE)来阻止。e是什么?DOM Event。当然,我们也能通过改变addEventListener的第三个参数改变事件的执行顺序。(false为冒泡阶段执行,true为捕获阶段执行,默认为false)我想,大概永远都用不到True了吧....

事件委托(事件代理)

介绍完上面的,事件委托是时候登场了。事件委托简单说起来就是利用事件冒泡,只指定一个事件处理程序,就可以管理某一类型的所有事件。
网上我找了很多篇博客,基本都是用这个例子,我就直接抄过来了:
有三个同事预计会在周一收到快递。为签收快递,有两种办法:一是三个人在公司门口等快递;二是委托给前台MM代为签收。现实当中,我们大都采用委托的方案,前台MM收到快递后,她会判断收件人是谁,然后按照收件人的要求签收,甚至代为付款。这种方案还有一个优势,那就是即使公司里来了新员工(不管多少),前台MM也会在收到寄给新员工的快递后核实并代为签收。

嗯,我大概明白是啥意思了。来个例子加深一下印象吧。

<ul id="ul1">
    <li>111</li>
    <li>222</li>
    <li>333</li>
    <li>444</li>
</ul>

window.onload = function(){
    var oUl = document.getElementById("ul1");
    var aLi = oUl.getElementsByTagName('li');
    for(var i=0;i<aLi.length;i++){
        aLi[i].onclick = function(){
            alert(123);
        }
    }
}

这是一段传统的的DOM操作遍历li实现事件操作的代码。有用,但是很蠢。遍历每一个li?真的有这个必要吗?
聪明的人已经想到了,那么我监听ul不就好了吗!bingo,那么用事件委托监听ul会怎么样呢?

window.onload = function(){
    var oUl = document.getElementById("ul1");
   oUl.onclick = function(){
        alert(123);
    }       

嗯,点击ul确实已经实现了功能。但是出现了一个bug.当我们点击li之外,ul之内,事件一样触发了。因为事件是绑定在ul上的啊!也许通过控制ul的样式能够解决这个问题,但是并不是一个好办法。嗯,其实写个判断就能够解决了。这里有个需要解释的e.什么是e?DOM Event.它提供了一个属性叫target,可以返回事件的目标节点。有个小坑:webkit等浏览器一般是使用e.target,而IE浏览器要用event.srcElement.而且这个target只是获取节点位置,我们还需要用nodeName获取标签名,并转化为小写做比较。

        


ul.addEventListener('click', function(e) {
                    // 检查事件源e.targe是否为Li
                    if (e.target && e.target.nodeName.toUpperCase == "LI") {

                        // 真正的处理过程在这里
                        console.log("123");
                    }
                }

这样就只有点击li会触发事件了,并且只执行一次dom操作。绝大多数的blog也是这么写的。

但是,它有bug!

如果第一个li外面套了一个span呢?

<ul id="ul1">
    <span><li>111</li></span>
    <li>222</li>
    <li>333</li>
    <li>444</li>
</ul>

再次点击第一个li,它居然不触发!为什么?console大法一下,发现我们点击的居然是span.那么这个循坏就错的彻彻底底了。但是其实做个小小的修改就行了。

ul.addEventListener('click', function() {
                    let el = e.target
                    while (el && !el.matches(selector)) {
                        el = el.parentNode
                        if (element === el) {
                            el = null
                        }
                    }
                    if (el) {
                        console.log('123')
                    }
                }       


OK,写写抄抄终于总结完了,自己算是弄懂了,下次有机会就用起来试试~

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

推荐阅读更多精彩内容