JS-DOM事件流

寫在前面的話

其實好久以前就接觸過DOM事件流了,可是在很長時間沒有用到以後,就把它忘記了,因此今天特地來寫一下,順便回顧一下DOM時間流的一些知識javascript

DOM事件流的分類

DOM事件流是有兩種的,一種是捕獲型事件流,另一種是冒泡型事件流,二者其實都很好理解,下面咱們就來詳細介紹一下,爲了你們更好的理解,咱們就先介紹下冒泡型事件流html

  1. 冒泡型事件流 "冒泡":沒錯就是你心中想的,冒泡就是咱們平時能夠常見的,例如水中的氣泡往上冒,這就是冒泡,因此冒泡型事件流,就是當你點擊目標元素的時候,當前所觸發的一些事件會向父元素中傳遞,這就是所謂的事件冒泡,若是你們仍是有不理解的地方,能夠直接看後面的代碼
  2. 捕獲型事件流 捕獲型事件流正好是與冒泡型事件流相反的,當你點擊目標元素的時候,在該目標元素上點擊觸發的事件,會從父元素向下傳遞
  3. 其實,DOM標準規定事件流包括三個階段:事件捕獲階段、處於目標階段和事件冒泡階段。 事件捕獲階段:實際目標在捕獲階段不會接收事件。也就是在捕獲階段,事件從document到再到就中止了。 處於目標階段:事件在上發生並處理。可是事件處理會被當作是冒泡階段的一部分。 冒泡階段:事件又傳播迴文檔。

要點

在咱們討論詳細瞭解以前,咱們首先要弄理清一些知識點前端

  1. 在事件流中咱們必需要記住一個結論,先捕獲後冒泡
  2. 冒泡是默認開啓的,那麼咱們改如何開啓捕獲呢,這時咱們能夠去js聖經中查找一下,addEventListener是支持三個參數的
1. event: 當前事件是什麼事件,click,onmouseover?
2. callback: 當前事件所執行的回調
3. option | useCapture: 第三個參數是比較複雜的,
有兩種狀況,當第三個參數只傳一個Boolean值時,此時就能夠打開捕獲模式,
固然第三個參數還能夠傳入一個對象,這個你們能夠去MDN上看一下,面試的時候仍是會公司問的
    options: {
         capture
         once: 是否只執行一次當前回調
         passive:表示當前監聽器永遠不會調用preventDefault函數
         最後一個option的話,由於有一些兼容性,就不在這裏介紹了
     } 
複製代碼

代碼

  1. 冒泡
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
</head>
<body>
    <div>
        <p>
            <a href="void: javascript(0)">點我讓你看事件捕獲</a>
        </p>
    </div>
</body>
<script>
    $('div').click(function() {
        console.log(2)
    })
    $('p').click(function() {
        console.log(1)
    })
    $('a').click(function() {
        console.log(0)
    })
</script>
</html>
複製代碼

image
當點擊a標籤的時候,此時會按照0--1--2的順序輸出,這就是事件冒泡 2. 事件捕獲

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
</head>
<body>
    <div>
        <p>
            <a href="void: javascript(0)">
                <span>123</span>
            </a>
        </p>
    </div>
</body>
<script>
    let div = document.getElementsByTagName('div')[0]
    let p = document.getElementsByTagName('p')[0]
    let a = document.getElementsByTagName('a')[0]
    let span = document.getElementsByTagName('span')[0]

    div.addEventListener("click", function() {
        console.log(2)
    })
    p.addEventListener("click", function() {
        console.log(1)
    })
    a.addEventListener("click", function() {
        console.log(0)
    }, true)
    span.addEventListener("click", function() {
        console.log(-1)
    })
</script>
</html>
複製代碼

image
這個地方你們要注意看,咱們對a綁定click事件的時候,給第三個參數設置爲true了,表示該事件在捕獲階段觸發, 因此咱們看到的輸出結果是0 -- -1 -- 1 -- 2,這也是驗證了咱們以前的結論,事件流是先捕獲後冒泡的

如何阻止?

阻止事件流

事件捕獲咱們能夠經過addEventListener的第三個參數控制開關,可是事件冒泡咱們該如何阻止呢,難道任由事件冒泡出現嗎?幸運的是咱們有stopPropagation這個函數,這個函數就是用來阻止事件冒泡的(其實也能夠用來阻止事件捕獲),先上代碼java

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
</head>
<body>
    <div>
        <p>
            <a href="void: javascript(0)">
                <span>123</span>
            </a>
        </p>
    </div>
</body>
<script>
    let div = document.getElementsByTagName('div')[0]
    let p = document.getElementsByTagName('p')[0]
    let a = document.getElementsByTagName('a')[0]
    let span = document.getElementsByTagName('span')[0]

    div.addEventListener("click", function() {
        console.log(2)
    })
    p.addEventListener("click", function(e) {
        e.stopPropagation()
        console.log(1)
    })
    a.addEventListener("click", function(e) {
        console.log(0)
        // e.stopPropagation()

    })
    span.addEventListener("click", function() {
        console.log(-1)
    })
</script>
</html>
複製代碼

image
咱們能夠看到,打印的是-1 0 1,到1的時候已經阻止事件向上傳播了,也就是說已經成功的阻止事件冒泡了。 上面咱們能夠看到,這個函數能夠阻止冒泡,那麼它只有這麼一個功能嗎,答案是否認的,來咱們看下面一段代碼

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
</head>
<body>
    <div>
        <p>
            <a href="void: javascript(0)">
                <span>123</span>
            </a>
        </p>
    </div>
</body>
<script>
    let div = document.getElementsByTagName('div')[0]
    let p = document.getElementsByTagName('p')[0]
    let a = document.getElementsByTagName('a')[0]
    let span = document.getElementsByTagName('span')[0]

    div.addEventListener("click", function() {
        console.log(2)
    })
    p.addEventListener("click", function(e) {
        e.stopPropagation()
        console.log(1)
    }, true)
    a.addEventListener("click", function(e) {
        console.log(0)
    })
    span.addEventListener("click", function() {
        console.log(-1)
    })
</script>
</html>
複製代碼

咱們能夠看到的是,控制檯的打印是隻有1的,這也就是說,這個函數不只僅是用於阻止事件冒泡,它還能夠用於阻止事件捕獲,更能夠說,事件流的三個階段它都能阻止,這就是他的做用jquery

那麼除了使用stopPropagation()以外,咱們還有其餘的方法阻止嗎,你們猜的不錯,咱們確實還有一種方法,來阻止事件流,那就是經過return false,這個方法是有一些缺陷的,他是只有在jQuery中才能生效,在原生js中只能是**阻止默認行爲(注意這個名詞,後面咱們會提到)**的,先上代碼git

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
</head>
<body>
    <div>
        <p>
            <a href="void: javascript(0)">點我讓你看事件捕獲</a>
        </p>
    </div>
</body>
<script>
    $('div').click(function() {
        console.log(2)
    })
    $('p').click(function() {
        console.log(1)
        return false
    })
    $('a').click(function() {
        console.log(0)
    })
</script>
</html>
複製代碼

image
咱們能夠看到控制檯的打印結果只有0 -- 1因此咱們認證了return false是能夠阻止冒泡的

阻止默認行爲

上面咱們在講return false的時候,咱們提到了一個名詞,阻止默認行爲,那麼什麼是默認行爲呢,咱們舉個栗子,當咱們點擊a標籤的時候,它會自動跳轉到href對應的地址,這個跳轉就叫默認行爲,咱們能夠經過return false來阻止一些默認行爲 除此以外,還有另外一種方法就是經過調用preventDefault方法來實現,這個函數也是用來阻止默認事件的github

注意點

咱們在使用preventDefault和stopPropagation的時候,是會有兼容性的,在IE中是不存在這兩個函數的,那麼在IE中咱們是如何實現阻止冒泡和阻止默認事件的呢,看代碼面試

//阻止冒泡事件
function stopBubble(e){
    if(e && e.stopPropagation){
        // 非IE瀏覽器
        e.stopPropagation();
    }else{ 
        //IE瀏覽器
        window.event.cancelBubble=true;
    }
}
// 阻止默認行爲
//阻止瀏覽器默認行爲
function stopDefault(e){ 
    //標準瀏覽器
    if(e && e.preventDefault){ 
        e.preventDefault(); 
    } 
    //個別IE
    else{ 
        window.event.returnValue=fale;
        return false;
    } 
}
複製代碼

經過以上代碼,咱們就能夠完美的實現阻止冒泡和默認行爲了ajax

存在的意義

在咱們討論了這麼長的事件捕獲和事件冒泡以後,咱們要想一下,爲何會有事件捕獲和事件冒泡呢 這就是咱們接下來要討論的事件代理(事件委託),api

那麼什麼是事件委託呢,事件委託就是利用事件冒泡,只指定一個事件處理程序,就能夠管理某一類型的全部事件(--- 源自JavaScript高級程序設計),

那麼爲何會出現事件委託呢,當咱們有1個li的時候,咱們給li加上click事件,這樣是徹底沒有問題的,可是當咱們有成百上千個li呢,此時咱們會怎麼處理,固然一種最簡單的方法就是for循環遍歷,而後給每一個li都加上click事件,這樣確實能實現,可是咱們要想到的是,html頁面的渲染速度是和dom的操做的多少掛鉤的,而dom操做的多少會和綁定的事件的數量掛鉤的,綁定的數量越多,渲染確定是越慢的,那麼此時你確定會問了,有什麼好的解決方法嗎,此時咱們就能夠用到這個名詞了,事件委託,有限事件委託的原理就是事件冒泡,當咱們給li添加事件的時候,此時事件流會順着li向外層的ul流去,那麼此時咱們即可以只在ul上添加和li上相同的事件,即可以實現和在li上添加的相同的效果,咱們先上代碼看一下

html 
<ul id="ul1">
    <li>你好</li>
    <li>你好</li>
    <li>你好</li>
    <li>你好</li>
    <li>你好</li>
    <li>你好</li>
    <li>你好</li>
    <li>你好</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);
        }
    }
}
複製代碼

上面這種方式是給每一個li添加事件,這個毫無疑問會下降渲染速度,下面咱們看一下事件代理以後的代碼

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

咱們僅僅須要綁定一個事件,變能夠實現相同的效果,豈不是美哉?,因此這就是咱們常說的事件代理

======================================================================= 分割線,DOM事件流已經分析完畢了, 若是有什麼不正確的地方歡迎你們指出來,我是一隻在前端路上前進的程序袁,並且我會一直前進下去的,但願能和你們共同進步 歡迎你們進個人github上去看一下,個人github

相關文章
相關標籤/搜索