溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

JavaScript事件捕獲冒泡分析

發(fā)布時間:2021-11-10 10:45:03 來源:億速云 閱讀:137 作者:iii 欄目:開發(fā)技術(shù)

本篇內(nèi)容主要講解“JavaScript事件捕獲冒泡分析”,感興趣的朋友不妨來看看。本文介紹的方法操作簡單快捷,實用性強。下面就讓小編來帶大家學習“JavaScript事件捕獲冒泡分析”吧!

    一、事件流

    JavaScript中,事件流指的是DOM事件流。

    1、概念

    事件的傳播過程即DOM事件流。
    事件對象在 DOM 中的傳播過程,被稱為“事件流”。
    舉個例子:開電腦這個事,首先你是不是得先找到你的電腦,然后找到你的開機鍵,最后用手按下開機鍵。完成開電腦這個事件。這整個流程叫做事件流。

    2、DOM事件流

    DOM事件,也是有一個流程的。從事件觸發(fā)開始到事件響應是有三個階段。

    • 事件捕獲階段

    • 處于目標階段

    • 事件冒泡階段

    上面例子中,開電腦這個事件的過程就像JavaScript中的事件流,找開機鍵這個過程就是 事件捕獲 的過程,你找到開機鍵后,然后用手按開機鍵,這個選擇用手去按的過程就是 處于目標階段 按下開機按鈕,電腦開始開機這也就是 事件的冒泡。 順序為先捕獲再冒泡。

    了解了事件源,讓我們看看它的三個過程吧!

    事件捕獲:

    注:由于事件捕獲不被舊版本瀏覽器(IE8 及以下)支持,因此實際中通常在冒泡階段觸發(fā)事件處理程序。

    事件捕獲處于事件流的第一步,
    DOM事件觸發(fā)時(被觸發(fā)DOM事件的這個元素被叫作事件源),瀏覽器會從根節(jié)點開始 由外到內(nèi) 進行事件傳播。即事件從文檔的根節(jié)點流向目標對象節(jié)點。途中經(jīng)過各個層次的DOM節(jié)點,最終到目標節(jié)點,完成事件捕獲。

    目標階段:

    當事件到達目標節(jié)點的,事件就進入了目標階段。事件在目標節(jié)點上被觸發(fā)。
    就是事件傳播到觸發(fā)事件的最底層元素上。

    事件冒泡:

    事件冒泡與事件捕獲順序相反。事件捕獲的順序是從外到內(nèi),事件冒泡是從內(nèi)到外。
    當事件傳播到了目標階段后,處于目標階段的元素就會將接收到的時間向上傳播,就是順著事件捕獲的路徑,反著傳播一次,逐級的向上傳播到該元素的祖先元素。直到window對象。

    看一個例子,點擊 box3 會將 box2 與 box1 的點擊事件觸發(fā)。

    <!DOCTYPE html>
    <html>
    
    <head>
        <meta charset="UTF-8">
        <title>JavaScript 事件冒泡</title>
    </head>
    <style type="text/css">
        #box1 { background: blueviolet;}
        #box2 {background: aquamarine;}
        #box3 {background: tomato;}
        div { padding: 40px; margin: auto;}
    </style>
    
    <body>
        <div id="box1">
            <div id="box2">
                <div id="box3"></div>
            </div>
        </div>
        <script>
            window.onload = function () {
                const box1 = document.getElementById('box1')
                const box2 = document.getElementById('box2')
                const box3 = document.getElementById('box3')
                box1.onclick = sayBox1;
                box2.onclick = sayBox2;
                box3.onclick = sayBox3;
                function sayBox3() {
                    console.log('你點了最里面的box');
                }
                function sayBox2() {
                    console.log('你點了最中間的box');
                }
                function sayBox1() {
                    console.log('你點了最外面的box');
                }
            }
        </script>
    </body>
    
    </html>

    這個時候 click 捕獲的傳播順序為:
    window -> document -> <html> -> <body> -> <div #box1> -> <div #box2> -> <div #box3>
    這個時候 click 冒泡的傳播順序為:
    <div #box3> -> <div #box2> -> <div #box1> -> <body> -> <html> -> document -> window

    JavaScript事件捕獲冒泡分析

    現(xiàn)代瀏覽器都是從 window 對象開始捕獲事件的,冒泡最后一站也是 window 對象。而 IE8 及以下瀏覽器,只會冒泡到 document 對象。
    事件冒泡:是由元素的 HTML 結(jié)構(gòu)決定,而不是由元素在頁面中的位置決定,所以即便使用定位或浮動使元素脫離父元素的范圍,單擊元素時,其依然存在冒泡現(xiàn)象。

    現(xiàn)在我們知道了事件流的三個階段后,那我們可以利用這個特性做什么呢?

    二、事件委托

    設(shè)想這樣一個場景,當你有一堆的<li>標簽在一個<ul>標簽下,需要給所有的<li>標簽綁定onclick事件,這個問題我們可以用循環(huán)解決,但還有沒有更簡便的方式呢?
    我們可以給這些<li>共同的父元素<ul>添加onclick事件,那么里面的任何一個<li>標簽觸發(fā)onclick事件時,都會通過冒泡機制,將onclick事件傳播到<ul>上,進行處理。這個行為叫做事件委托, <li>利用事件冒泡將事件委托到<ul>上。
    也可以利用事件捕獲進行事件委托。用法是一樣的,只是順序反了。

      <ul id="myUl">
        <li>item 1</li>
        <li>item 2</li>
        <li>item 3</li>
        ...
      </ul>

    可能還是有點不好理解,簡單來說,就是利用事件冒泡,將某個元素上的事件委托給他的父級。

    舉個生活中的例子,雙十一快遞到了,需要快遞小哥送快遞一般是挨家挨戶送貨上門,這樣效率慢,小哥想了個辦法,把一個小區(qū)的快遞都放在小區(qū)里面的快遞驛站,進行送快遞的事件委托,小區(qū)的收件人能通過取件碼去快遞驛站領(lǐng)取到自己的快遞。
    在這里,快遞小哥送快遞就是一個事件,收件人就是響應事件的元素,驛站就相當于代理元素,收件人憑著收獲碼去驛站里面領(lǐng)快遞就是事件執(zhí)行中,代理元素判斷當前響應的事件匹配該觸發(fā)的具體事件。

    可是這樣做有什么好處呢?

    1、事件委托的優(yōu)點

    事件委托有兩個好處

    • 減少內(nèi)存消耗

    • 動態(tài)綁定事件

    減少內(nèi)存消耗,優(yōu)化頁面性能

    JavaScript中,每個事件處理程序都是對象,是對象就會占用頁面內(nèi)存,內(nèi)存中的對象越多,頁面的性能當然越差,而且DOM的操作是會導致瀏覽器對頁面進行重排和重繪(這個不清楚的話,小伙伴可以了解頁面的渲染過程),過多的DOM操作會影響頁面的性能。性能優(yōu)化主要思想之一就是為了最小化的重排和重繪也就是減少DOM操作。

    在上面給<li>標簽綁定onclick事件的例子中,使用事件委托就可以不用給每一個<li>綁定一個函數(shù),只需要給<ul>綁定一次就可以了,當li的數(shù)量很多時,無疑能減少大量的內(nèi)存消耗,節(jié)約效率。

    動態(tài)綁定事件:

    如果子元素不確定或者動態(tài)生成,可以通過監(jiān)聽父元素來取代監(jiān)聽子元素。
    還是上面在<li>標簽綁定onclick事件的例子中, 很多時候我們的這些<li>標簽的數(shù)量并不是固定的,會根據(jù)用戶的操作對一些<li>標簽進行增刪操作。在每次增加或刪除標簽都要重新對新增或刪除元素綁定或解綁對應事件。

    可以使用事件委托就可以不用給每一個<li>都要操作一遍,只需要給<ul>綁定一次就可以了,因為事件是綁定在<ul>上的, <li>元素是影響不到<ul>的 ,執(zhí)行到<li>元素是在真正響應執(zhí)行事件函數(shù)的過程中去匹配的,所以使用事件委托在動態(tài)綁定事件的情況下是可以減少很多重復工作的。

    我們知道了事件委托的優(yōu)點,那么該如何使用呢?

    2、事件委托的使用

    事件委托的使用需要用的addEventListener()方法,事件監(jiān)聽。
    方法將指定的監(jiān)聽器注冊到調(diào)用該函數(shù)的對象上,當該對象觸發(fā)指定的事件時,指定的回調(diào)函數(shù)就會被執(zhí)行。

    用法:

    element.addEventListener(eventType, function, useCapture);
    參數(shù)必/選填描述
    eventType必填指定事件的類型。
    function必填指定事件觸發(fā)后的回調(diào)函數(shù)。
    useCapture選填指定事件是在捕獲階段執(zhí)行還是在冒泡階段執(zhí)行。

    第三個參數(shù) useCapture 是個布爾類型,默認值為false

    • true - 表示事件在捕獲階段執(zhí)行執(zhí)行

    • false- 表示事件在冒泡階段執(zhí)行執(zhí)行

    看下面例子:

    <!DOCTYPE html>
    <html>
    
    <head>
      <meta charset="UTF-8">
      <title>JavaScript 事件委托</title>
    </head>
    
    <body>
    
      <ul>
        <li>item 1</li>
        <li>item 2</li>
        <li>item 3</li>
        <li>item 4</li>
      </ul>
    
      <script>
        const myUl = document.getElementsByTagName("ul")[0];
    
        myUl.addEventListener("click", myUlFn);
    
        function myUlFn(e) {
          if (e.target.tagName.toLowerCase() === 'li') { // 判斷是否為所需要點擊的元素
            console.log(`您點擊了${e.target.innerText}`);
          }
        }
    
      </script>
    </body>
    
    </html>

    注意:這是一般的事件委托方法,但是這種寫法有問題,就是當_<li>_中還有子元素時,點擊這個子元素就不會進行觸發(fā)事件。這個問題是一個坑。

    事件冒泡有時候確實很有用,但是有時候也討人煩,當你不需要它的時候能不能取消掉呢?

    三、禁止事件冒泡與捕獲

    注意:并不是所有事件都會冒泡,比如focus,blur,change,submit,reset,select等。

    禁止冒泡和捕獲可以用到方法stopPropagation()。
    stopPropagation()起到阻止捕獲和冒泡階段中當前事件的進一步傳播。
    這是阻止事件的冒泡方法,進行冒泡,但是默認事件任然會執(zhí)行,當你調(diào)用了這個方法后。
    如果點擊一個a標簽,這個a標簽會進行跳轉(zhuǎn)。

    使用起來也很簡單,沒有返回值也沒有參數(shù)。

    event.stopPropagation();

    請看下面例子,這個例子實在上文事件冒泡例子基礎(chǔ)上稍加修改得到的

    <div id="box1">
            <div id="box2">
                <div id="box3"></div>
            </div>
        </div>
        <script>
            const box1 = document.getElementById('box1')
            const box2 = document.getElementById('box2')
            const box3 = document.getElementById('box3')
            box1.onclick = sayBox1;
            box2.onclick = sayBox2;
            box3.onclick = sayBox3;
            function sayBox3() {
                console.log('你點了最里面的box');
            }
            function sayBox2(e) {
                console.log('你點了最中間的box');
                e.stopPropagation(); //禁止事件捕獲和冒泡
            }
            function sayBox1() {
                console.log('你點了最外面的box');
            }
        </script>

    當事件冒泡到box2時調(diào)用了在函數(shù)sayBox2,調(diào)用了e.stopPropagation(); 就停止冒泡了。

    到此,相信大家對“JavaScript事件捕獲冒泡分析”有了更深的了解,不妨來實際操作一番吧!這里是億速云網(wǎng)站,更多相關(guān)內(nèi)容可以進入相關(guān)頻道進行查詢,關(guān)注我們,繼續(xù)學習!

    向AI問一下細節(jié)

    免責聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

    AI