前端筆記之JavaScript(十)深入JavaScript節點&DOM&事件

mufengsm發表於2019-03-27

DOM

JavaScript語言核心。變數的定義、變數的型別、運算子、表示式、函式、if語句、for迴圈、演算法等等。這些東西都屬於語言核心,下次繼續學習語言核心就是物件導向了。JavaScript能做非常多的事情:DOM開發、Ajax開發、Canvas開發、NodeJS開發、前端框架(ReactVueAngular等等)、HTML5開發。這些都需要語言核心的知識。

DOM開發說白了就是瀏覽器中的頁面效果開發,在2011年之前,DOM開發佔據了前端開發工程師的90%的工作;但是現在,DOM開發的工作比重已經降到了10%以下。換句話說,2011年之前,前端 = 做特效的;2011年之後,前端要負責得到後臺的資料介面,用前端MVC邏輯分層開發前端組建、介面、功能,還要寫HTML5,還要做canvas動畫!

上層的框架遮蔽了下層的語言的一些麻煩、不方便的東西,並且提供更方便的API。

jQuery就是幹這個事情的,把JS中的不方便封裝起來,暴露的API都是非常簡便的。

jQuery的哲學就是DOM程式設計領域的霸主,操作DOM節點、繫結監聽、運動、css樣式、Ajax等等都有封裝。

工作上都是用jQuery,如果不用jQuery也是用類似的東西。沒有人會不用輪子去開發頁面效果。

 

JavaScript中Library表示“庫”,如果這個庫的功能很強大,甚至顛覆了傳統程式設計的語法、行文習慣,我們就可以叫做“框架”。


1.1 DOM是什麼

文件物件模型 (DOMDocument Object Model) HTML XML 文件的程式設計介面。它給文件(結構樹)提供了一個結構化的表述並且定義了一種方式—程式可以對結構樹進行訪問,以改變文件的結構,樣式和內容。 DOM 提供了一種表述形式— 將文件作為一個結構化的節點組以及包含屬性和方法的物件。從本質上說,它將 web 頁面和指令碼或程式語言連線起來了

到底什麼是DOM就是你可以像操作物件一樣操作HTML頁面,而不是操作字串。

DOMweb 頁面和指令碼或程式語言連線起來了。

回看一下我們之前學習的DOM操作,都在幹嘛?我們在開發特效,但是微觀的看,實際上在進行:

1) 得到HTML節點

2) 改變節點的屬性

3) 改變節點的樣式

4) 修改節點、刪除節點、增加節點

5) 節點之間的關係


1.2原生JavaScript得到節點

 document.getElementById('box');

 document.getElementsByTagName('p');

以上兩是全線瀏覽器都相容的得到元素方法。

 

以下這些得到元素的方法都不相容IE678 

document.getElementsByName('aaa')[0]  //通過name屬性得到元素們
document.getElementsByClassName('pp') //通過類名得到元素們
document.querySelector('#box');
document.querySelectorAll('#box p');  //通過選擇器得到元素們

jQueryDOM開發的王者!幫我們解決了元素選擇的相容問題。

jQuery底層很強大,比如$('.par1')機制不是getElementsByClassName(),而是在遍歷所有節點,選擇類名有par1的項。

 


二、原生JavaScript節點關係

jQuery中學習parent()children() siblings()next()prev()等等節點關係,JS中也有對應的屬性。

原生JS提供的節點關係很少:

childNodesfirstChildlastChildparentNodenextSiblingpreviousSibling

 

常見的nodeType值:

 1-普通元素節點、比如divp等等

 2-屬性節點

 3-文字節點

 8-註釋節點

 9-document節點

 10-文件DTD

 

想要檢視某一個元素的節點型別,直接讀取它的nodeType屬性即可。

改變nodeType3的文字節點的內容,要改變他的nodeValue屬性

 

2.1 childNodes兒子節點

childNodesIE678和高階瀏覽器不一致,高階瀏覽器認為所有的換行為空文字節點,而IE678無視這個空文字節點。

div中沒有文字節點,此時應該是4個節點,但是IE9Chrome、火狐會認為有9個節點、IE8認為有4個節點。

高階瀏覽器會把空文字當做一個節點,標籤前後的空文字也被算作一個。

註釋的前後算不算空文字節點,各個瀏覽器有不同的解釋。所以用節點的時候,一定要去過濾、判斷節點的nodeType是不是1

<div id="box">
    <p></p>
    <p></p>
    <p></p>
    <p></p>
</div>

 oBox.childNodes.length;  //Chrome數值9IE6784

 

為解決相容性問題(到底空文字算不算兒子,所以要封裝函式來解決):

可以利用nodeType是不是1來過濾文字節點、註釋節點等,編寫一個函式,得到一個標籤真正的子節點。

jQuery也有這層過濾:

//封裝一個children函式,這個函式能返回obj物件的所有真正兒子節點
function chidlren(obj,num){
    var arr = []; //儲存所有兒子
    //遍歷所有的節點
    for(var i = 0; i < obj.childNodes.length;i++){
        //遍歷的過程,尋找真正的HTML兒子節點、過濾文字、註釋等節點
        //判斷節點型別是不是1
        if(obj.childNodes[i].nodeType == 1){
            arr.push(obj.childNodes[i]); //如果是兒子節點就插入陣列中
        }
    }
    //return arr;
    //返回的是:如果使用者傳入了num,返回某一個兒子,如果沒有num,返回所有兒子
    return num ? arr[num] : arr;
}
chidlren(oBox)[2].style.backgroundColor = 'red';
chidlren(oBox,3).style.backgroundColor = 'red';

2.2 parentNode父親節點

parentNode屬性表示父親節點。任何節點的parentNodenodeType一定是1,也就是說父親節點一定是標籤節點。文字節點、註釋節點沒有兒子。

 

var input = document.getElementsByTagName('input');
for(var i = 0;i < input.length;i++){
   //當點選某個input時,如果自己被選中,此時改變父親的顏色為綠色,否則為白色
   input[i].onclick = function(){
       if(this.checked){
           this.parentNode.style.backgroundColor = 'green';
       }else{
           this.parentNode.style.backgroundColor = 'white';
       }
   }
}

2.3 previousSiblingnextSibling兄弟節點

上一個兄弟previousSibling、下一個兄弟nextSibling。同樣的,文字節點也屬於節點,註釋也是節點,所以一個節點的上一個兄弟可能是文字、註釋節點。原生JS中沒有提供類似nextAll()prevAll()siblings()方法,如果節點沒有上一個兄弟或下一個兄弟、返回null

 

 console.log(pp.previousSibling.nodeType)  

 console.log(pp.nextSibling.nodeType)

 

var pp = document.getElementById("pp");
//返回obj的前面一個兄弟
function prevSibling(obj){
    //開始遍歷obj節點的前面,直到遇見一個nodeType為1的節點
    var prev = obj;
    //迴圈遍歷。注意while的條件是一個賦值語句!賦值語句也有表示式的
    while(prev = prev.previousSibling){
        if(prev.nodeType == 1){
            return prev;
        }
    }
    return null;
}
//得到真正的後面兄弟
function nextSibling(obj){
    //開始遍歷obj節點的前面,直到遇見一個nodeType為1的節點
    var next = obj;
    while(next = next.nextSibling){
        if(next.nodeType == 1){
            return next;
        }
    }
    return null;
}
//返回obj的前面所有兄弟
function prevAll(obj){
    //開始遍歷obj節點的前面,直到遇見一個nodeType為1的節點
    var prev = obj;
    var arr = [];
    while(prev = prev.previousSibling){
        if(prev.nodeType == 1){
            arr.push(prev);
        }
    }
    return arr;
}
prevSibling(pp).style.background = "red";
nextSibling(pp).style.background = "green";
prevAll(pp)[1].style.background = "green";

三、原生JavaScript DOM節點操作

HTML節點我們原來最多就是改改HTML屬性,比如改改src屬性;或者改改css樣式,比如.style或者.css()

現在的問題是,我們要增加節點、刪除節點、移動節點、替換節點。

 


3.1 createElement()建立和appendChild()新增

建立節點的方法: create建立,Element元素。接收一個引數,就是建立的標籤是什麼。

 document.createElement()

 

追加節點的方法:建立出來的節點不在DOM樹上,所以就應該用appendChild()來新增到DOM樹上:

 父親.appendChild(新兒子);

 

var btn = document.getElementById('btn');
var txt = document.getElementById('txt');
var ul = document.getElementsByTagName('ul')[0];
btn.onclick = function(){
   //建立一個li標籤,用變數oLi 來表示,建立除了的節點不是任何節點的兒子(沒有在DOM樹上)
   var oLi = document.createElement('li');
   oLi.innerHTML = txt.value; //改變這個節點的內容
   //把新建立的節點,追加到DOM樹上
   ul.appendChild(oLi);
}

appendChild()一般來說就是用來追加新建立的節點,如果試圖把頁面上已經有的節點,appendChild()到別的地方,那麼這個節點將移動,也就是說,同一個節點不可能在頁面上兩個地方出現。

比如:

<div id="box1">
    <p id="xiaoming">我是小明</p>
</div>
<div id="box2">

</div>
<script type="text/javascript">
     var box2 = document.getElementById('box2');
     var xiaoming = document.getElementById('xiaoming');
     box2.appendChild(xiaoming);
</script>

以上將把xioaming移動到box2裡面。

 

innerHTML建立節點:

事實上,工作的時候很少用createElement。因為innerHTML足夠好用,innerHTML也可以用來建立節點,甚至效率createElement還高。

var year = document.getElementById('year');
for(var i = 1950; i <= 2018;i++){
   //建立節點
   var op = document.createElement('option');
   //改變建立出來的節點內容
   op.innerHTML = i;
   //上DOM樹
   year.appendChild(op); // 父親.appendChild(新兒子);
}

 

innerHTML建立:

for(var i = 1950; i <= 2018; i++) {
    year.innerHTML += '<option>'+i+'</option>';
}

 

JavaScript是動態變數:

var oBox = document.getElementById('box');
//得到box裡面所有的p,現在box沒有p,所以ops是一個空陣列
var ops = oBox.getElementsByTagName('p');
var np = document.createElement('p'); //建立節點
oBox.appendChild(np); //追加節點
var np = document.createElement('p'); //建立節點
oBox.appendChild(np); //追加節點
var np = document.createElement('p'); //建立節點
oBox.appendChild(np); //追加節點
var np = document.createElement('p'); //建立節點
oBox.appendChild(np); //追加節點
var np = document.createElement('p'); //建立節點
oBox.appendChild(np); //追加節點
//這裡彈出多少?初學者認為彈出0,因為先得到陣列p,然後建立節點,節點又沒有往陣列裡面push,所以應該ops數不變才對。
//但是JS中儲存DOM節點的變數是動態,不是一個瞬時照片,而是一個有生命的動態物件,當oBox裡面的p標籤變化時,ps也變化
console.log(ops.length)

3.2 insertBefore()新增

appendChild是把新節點插入在父親的所有子節點的後面,也就是說新增的節點就是父親的最後一個兒子。

我們可以在任意一個位置新增子節點:會在原有標杆兒子之前插入

 父親.insertBefore(新兒子,原有標杆兒子)

 

var btn = document.getElementById('btn');
var txt = document.getElementById('txt');
var ul = document.getElementsByTagName('ul')[0];
var lis = document.getElementsByTagName('li');
btn.onclick = function(){
   //建立一個li標籤,用變數oLi 來表示,建立除了的節點不是任何節點的兒子(沒有在DOM樹上)
   var oLi = document.createElement('li');
   oLi.innerHTML = txt.value; //改變這個節點的內容
   //把新建立的節點,追加到DOM樹上
   //在lis[0]的前面插入
   ul.insertBefore(oLi,lis[0]);
}

如果想每次都在開頭新增,那麼就是:

 ul.insertBefore(oLi,lis[0]);

lis這個變數是動態的,這次新增的li,下回就是lis[0]


3.3 removeChild()刪除

父親.removeChild(兒子);
<ul>
    <li>吃飯 <a href="###">刪除</a></li>
    <li>睡覺 <a href="###">刪除</a></li>
    <li>打豆豆 <a href="###">刪除</a></li>
</ul>
<script type="text/javascript">
     var ul = document.getElementsByTagName('ul')[0];
     var lis = document.getElementsByTagName('li');
     var as = document.getElementsByTagName('a');

     for(var i = 0;i< as.length;i++){
        as[i].onclick = function(){
            ul.removeChild(this.parentNode);

            //如果要自殺,也要找到爸爸
            //this.parentNode.removeChild(this);
        }
     }
</script>

 

如果要自殺,也要找到爸爸:

 this.parentNode.removeChild(this);

 


3.4 replaceChild()替換

替換節點:

 父親.replaceChild(新兒子,舊兒子)

 

<div>
    <p>趙麗穎</p>
    <p id="xh">小黑</p>
    <p>迪麗熱巴</p>
</div>
<script type="text/javascript">
     var oBox = document.getElementsByTagName('div')[0]
     var xh = document.getElementById('xh');

     //建立節點,孤兒節點
     var op = document.createElement('p');
     op.innerHTML = '朱老師'; //更改op的內容
     oBox.replaceChild(op,xh);
</script>

3.5 clone()克隆

克隆節點,引數true表示深度克隆,節點裡面的所有內容和事件一同複製。

複製之後的節點是個孤兒節點,所以也要使用appendChild()等方法來新增上DOM樹。

 

 克隆物件.cloneNode(true)

 

<div id="box1">
    <ul>
        <li><span>趙麗穎</span></li>
        <li><span>迪麗熱巴</span></li>
        <li><span>柳巖</span></li>
        <li><span>志玲姐姐</span></li>
    </ul>
</div>
<div id="box2">

</div>
<script type="text/javascript">
     var box1 = document.getElementById('box1');
     var box2 = document.getElementById('box2');
     var ul = document.getElementsByTagName('ul')[0];
     var lis  = document.getElementsByTagName('li');

     //克隆li和li的所有後代(要加true),然後追加到ul中
     //ul.appendChild(lis[0].cloneNode());  //克隆第0個li
     box2.appendChild(ul.cloneNode(true)); //克隆ul追加到box2中
</script>

四、事件監聽

一堆理論知識正要來襲。

4.1事件流

我們考慮一個結構,三個div巢狀,點選最內層的div,我們點選了誰?僅僅點選了最內層div嗎?不是就像手指放在到一個同心圓中,實際上手指觸碰到了任何一個圓。

點選最內層的div,實際上瀏覽器會認為我們點選了所有的盒子,甚至於bodydocumentwindow

 

為了描述事件的傳播,人為規定了一個事件的傳播方向,稱為“事件流”。兩個階段:事件捕獲階段,事件冒泡階段。

“事件流”描述的是頁面上各個元素接收事件的順序。

 

 


4.2 DOM0級事件監聽

DOM分級別,DOM0級、1級、2級、3級,是不同的標準,標準一直在升級。

之前學習的on開頭的語法新增事件,稱為“DOM0級事件”。

 

事件的觸發一定是按照事件流的順序,由於DOM0級只能監聽冒泡階段,所以順序是:box3box2box1bodydocumentwindow 如果改變監聽順序,彈出順序不變。

box1.onclick = function(){ alert('我是box1');}
box2.onclick = function(){ alert('我是box2');}
box3.onclick = function(){ alert('我是box3');}
document.body.onclick = function(){alert('我是body')}
document.onclick = function(){alert('我是document')}
window.onclick = function(){alert('我是window')}

這種監聽寫法,就是DOM0級,就是把onclick當做屬性新增給了div元素。

這種事件新增方法,只能監聽冒泡過程,不能監聽事件捕獲階段。

 

DOM0級事件處理函式中,this指的是觸發事件的DOM元素,就是事件傳播到的這個元素。

DOM0級事件處理函式中,如果同一個物件,同一個事件名,繫結多個監聽,後面寫的覆蓋前面寫的。

 box1.onclick = function(){ alert('我是box1');}

 box1.onclick = function(){ alert('我是box1,後面寫的');}

DOM0級事件,IE67事件只能冒泡到documentIE8只能冒泡body,不能繼續冒泡到window。也就是說不能給window物件新增事件。

 


4.3 DOM2級事件監聽

DOM1級規範中,沒有對事件進行改動,所以沒有DOM1級的事情

DOM2級做了新的規範,不用on**來繫結監聽了,而是用一個方法

W3C推出了addEventListener()函式,add新增、event事件,listener監聽

它接收三個引數:事件,函式,是否監聽捕獲階段

 元素.addEventListener(事件,事件處理函式,是否新增到捕獲階段)

第一個引數:事件名不用謝on。(clickmouseover

第二個引數:函式可以是匿名函式,也可以是有名函式

第三個引數:布林值,true表示監聽捕獲,false表示監聽冒泡階段

box1.addEventListener('click',function(){
   alert('我是box1的捕獲階段')
},true);

 

第三個引數是true,表示監聽box1的捕獲階段的單擊事件。

要記住true表示捕獲,false冒泡階段:口訣:true上,false下。

box1.addEventListener('click',function(){
   alert('我是box1的捕獲階段')
},true);
box2.addEventListener('click',function(){
   alert('我是box2的捕獲階段')
},true);
box3.addEventListener('click',function(){
   alert('我是box3的捕獲階段')
},true);
box1.addEventListener('click',function(){
   alert('我是box1的冒泡階段')
},false);
box2.addEventListener('click',function(){
   alert('我是box2的冒泡階段')
},false);
box3.addEventListener('click',function(){
   alert('我是box3的冒泡階段')
},false);

box1的捕獲→box2的捕獲→box3的捕獲→box3的冒泡→box2的冒泡→box1的冒泡

坑:最內層的box3,誰先寫就誰先執行,也就是說對於最內層的box3,就不區分冒泡和捕獲了,對於不少罪內層的元素來說,改變事件監聽的順序,不會影響執行結果,先捕獲,後冒泡。

 

DOM2級事件中,如果給同一個元素,同一個事件名,同一個階段新增多個事件監聽,彼此不會覆蓋,先寫的先執行。

box1.addEventListener('click',function(){
   alert('我是box1的冒泡階段A')
},false);
box1.addEventListener('click',function(){
   alert('我是box1的冒泡階段B')
},false);
box1.addEventListener('click',function(){
   alert('我是box1的冒泡階段C')
},false);

彈出順序是:ABC

DOM2級事件監聽中,this指的是觸發事件的這個元素,就是事件傳播到的這個元素。

 


DOM2級小測試-看看執行順序:正確答案在下篇文章。

小題目:頁面box1最大,巢狀box2box3,點選box3彈出順序

box2.onclick = function(){
   alert('A');
}
box2.onclick = function(){
   alert('B');
}
box2.addEventListener('click',function(){
   alert('C')
},false);
box2.addEventListener('click',function(){
   alert('D')
},false);
box2.addEventListener('click',function(){
   alert('E')
},true);
box2.addEventListener('click',function(){
   alert('F')
},true);
box3.addEventListener('click',function(){
   alert('G')
},false);
box3.onclick = function(){
   alert('H')
}
box3.addEventListener('click',function(){
   alert('I')
},true);

4.4低版本IE事件監聽

IE678不支援addEventListener()方法,支援attachEvent()

box1.attachEvent('onclick', function(){
   alert(1)
});

 

第一個引數:必須寫on,和addEventListener不一樣

第二個引數:事件處理函式

沒有第三個引數,不能自由選擇新增到什麼階段,永遠是冒泡階段,也就是說IE678不能監聽捕獲階段。

box1.attachEvent('onclick', function(){
   alert('我是1a');
});
box1.attachEvent('onclick', function(){
   alert('我是1c');
});
box1.attachEvent('onclick', function(){
   alert('我是1b');
});
box2.attachEvent('onclick', function(){
   alert('我是2');
});
box3.attachEvent('onclick', function(){
   alert('我是3');
});

 

比較搞笑,如果給同一個元素新增多個事件名相同的監聽,不互相覆蓋,但是倒著執行,先執行後寫:

box1.attachEvent('onclick', function(){
   alert('我是a');
});
box1.attachEvent('onclick', function(){
   alert('我是b');
});
box1.attachEvent('onclick', function(){
   alert('我是c');
});

彈出cba

 

最詭異是,this永遠是window物件而不是事件傳播到的元素。

box1.attachEvent('onclick', function(){
   alert(this === window);
});

所以最正確的工作實踐,就是用DOM0級事件監聽方法,除非基於特別充分的理由,否則不用DOM2級。什麼時候理由充分用DOM2級呢?比如一些新興事件,如手機事件:touchstarttouchmovetouchend事件等等。只能用DOM2級。

上面學習的就是一些理論知識,總結一下:

事件流永遠是先下(捕獲)後(冒泡),但是不同監聽方式,決定了監聽那一部分。

 

面試意義大於實際意義!

jQuery中用的就是DOM2級,也有輪子,jQuery事件同名不會被覆蓋。

 


4.5事件監聽移除

DOM0級事件監聽的移除很簡單,直接把事件屬性賦值給null即可。

var box1 = document.getElementById('box1');
box1.onclick = function(){
   alert('你好,再次點選就沒有效果了');
   box1.onclick = null; //移除事件監聽
}

DOM2級事件監聽移除,必須指名道姓移除,也就是說,如果是匿名函式新增的事件監聽,是不可能去掉的。

 

新增的時候要有名字:

box1.addEventListener('click',function fun(){
   alert('你好');
   box1.removeEventListener('click',fun,true);
},true);

 

點選按鈕後,移除box1的事件監聽:

btn.onclick = function(){
   box1.removeEventListener('click',fun,true);
}

 

IE678detachEvent即可,同樣,也必須起名。

box1.attachEvent('onclick',fun)
function fun(){
   alert('你好');
}
btn.onclick = function(){
   box1.detachEvent('onclick',fun);
}

 


 

五、事件物件event

這裡的知識就不是理論了 ,有實戰價值。

5.1 event相容性問題

在任何事件中,都有事件物件event。瀏覽器會往事件處理函式中,傳入一個實參,就是事件物件,裡面封裝了你這次觸發事件(點選、滑鼠移入、移出、鍵盤按下)的細節。比如滑鼠位置、是否按下某個鍵等等。

一般用變數event來接收,注意,不是必須的,名字可以改。

所有事件的細節,都被封裝到event物件裡面。

比如clinetXclinetY就是這次滑鼠點選的位置。

 

先說相容性問題,IE678中,event這事件物件不是實參,而是全域性變數,系統會在每一個事件發生的時候,都去改變這個全域性變數的值:

box.onclick = function(){  //圓括號不能寫event,如果寫了,就遮蔽了全域性的event
   alert(event.clientX)
}

 

相容寫法:

box.onclick = function(event){
   //因為在Chrome中event是實參,IE678events全域性變數,所以用或的短路語法,它支援哪個用哪個
   var event = event || window.event;
   alert(event.clientX)
}

5.2事件物件屬性

 event.type      返回事件型別,沒有on,比如“click

 event.target    返回你點選的最小哪個元素,即使這個元素身上沒有監聽,也返回它

 event.currentTarget  返回自己,this一定和它是同一個元素,都是自己

 event.bubbles     返回布林值,表示這個事件是否冒泡

 

bubbles屬性,表示事件是否冒泡

box1.onmouseover = function(event){
   var event = event || window.event;
   console.log(event.bubbles); //表示是否冒泡
}

 

基本上所有事件這個屬性都是true,表示這個事件冒泡,但是要記住:

 onmouseoverevent.bubbles是true,表示冒泡

 onmouseoutevent.bubbles是true,表示冒泡

 onmouseenterevent.bubbles是false,表示不冒泡

 

有兩個事件不冒泡:

 onmouseentermouseleaveevent.bubblesfalse,不冒泡

 

onmouseentermouseleave這兩個事件IE678910全面相容!反而是Chrome30之前不相容,但是現在可以當做全面相容。它們兩個事件是著名天生不冒泡的,當一個內部元素滑鼠進入了,會執行這個元素的事件處理函式,但是事件捕獲繼續往外層傳播。它的外層盒子不會觸發onmouseenter事件。

 

box1.onmouseenter = function(event){
    alert('滑鼠進入了box1')
}
box2.onmouseenter = function(event){
    alert('滑鼠進入了box2')
}
box1.onmouseout = function(event){
    alert('滑鼠離開了box1')
}
box2.onmouseout = function(event){
    alert('滑鼠離開了box2')
}

 

onmouseentermouseleaveonmouseoveronmouseout好用

// $('#box').mouseenter(function(){
//    $(this).children('.mask').slideDown();
// });
// $('#box').mouseleave(function(){
//    $(this).children('.mask').slideUp();
// });
$('#box').mouseover(function(){
   $(this).children('.mask').slideDown();
});
$('#box').mouseout(function(){
   $(this).children('.mask').slideUp();
});

targetsrcElement屬性,表示事件發生的最內層元素

chrome同時支援下面2個屬性,IE只支援srcElement

 var target = event.target || event.srcElement;

 

用這個屬性可以優化程式碼效率,可以製作“事件委託”把子元素的事件,委託給父親,父親通過event.target來判斷是誰觸發了事件。不使用事件委託,我們將會有很多事件監聽;使用事件委託,事件監聽只有1個。提升了頁面的效率。

box1.onclick = function(event){
   var event = event || window.event;
   var target = event.target || event.srcElement;
   alert(target.id)
}

 

事件委託:

比如,小圓點新增監聽,不要給所有li,直接給ol,讓olevent.target這個元素有cur

<body>
    <ol>
        <li class="cur">0</li>
        <li>1</li>
        <li>2</li>
        <li>3</li>
        <li>4</li>
        <li>5</li>
    </ol>
</body>
<script type="text/javascript" src="js/jquery-1.12.4.min.js"></script>
<script type="text/javascript">
    $('ol').mouseover(function(event){
        var event = event || window.event;
        //得到觸發事件的最小元素
        var target = event.target || event.srcElement;
        //讓觸發事件的元素新增cur
        $(target).addClass('cur').siblings().removeClass('cur');
    });
</script>

當頁面上有大量的重複元素要新增監聽的時候,比如100/200個,一定要注意事件委託。

 

window.onload = function(){
  var oUl = document.getElementById("ul1");
  oUl.onclick = function(ev){
    var ev = ev || window.event;
    var target = ev.target || ev.srcElement;
    if(target.nodeName.toLowerCase() == 'li'){
            alert(123);
         alert(target.innerHTML);
    }
  }
}

 

 

 


 

相關文章