一、DOM
JavaScript語言核心。變數的定義、變數的型別、運算子、表示式、函式、if語句、for迴圈、演算法等等。這些東西都屬於語言核心,下次繼續學習語言核心就是物件導向了。JavaScript能做非常多的事情:DOM開發、Ajax開發、Canvas開發、NodeJS開發、前端框架(React、Vue、Angular等等)、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是什麼
文件物件模型 (DOM,Document Object Model) 是 HTML 和 XML 文件的程式設計介面。它給文件(結構樹)提供了一個結構化的表述並且定義了一種方式—程式可以對結構樹進行訪問,以改變文件的結構,樣式和內容。 DOM 提供了一種表述形式— 將文件作為一個結構化的節點組以及包含屬性和方法的物件。從本質上說,它將 web 頁面和指令碼或程式語言連線起來了。
到底什麼是DOM?就是你可以像操作物件一樣操作HTML頁面,而不是操作字串。
DOM將 web 頁面和指令碼或程式語言連線起來了。
回看一下我們之前學習的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'); //通過選擇器得到元素們
jQuery是DOM開發的王者!幫我們解決了元素選擇的相容問題。
jQuery底層很強大,比如$('.par1')機制不是getElementsByClassName(),而是在遍歷所有節點,選擇類名有par1的項。
二、原生JavaScript節點關係
在jQuery中學習parent()、children() 、siblings()、next()、prev()等等節點關係,JS中也有對應的屬性。
原生JS提供的節點關係很少:
childNodes、firstChild、lastChild、parentNode、nextSibling、previousSibling
常見的nodeType值:
1-普通元素節點、比如div、p等等 2-屬性節點 3-文字節點 8-註釋節點 9-document節點 10-文件DTD |
想要檢視某一個元素的節點型別,直接讀取它的nodeType屬性即可。
改變nodeType為3的文字節點的內容,要改變他的nodeValue屬性
2.1 childNodes兒子節點
childNodes在IE6、7、8和高階瀏覽器不一致,高階瀏覽器認為所有的換行為空文字節點,而IE678無視這個空文字節點。
div中沒有文字節點,此時應該是4個節點,但是IE9、Chrome、火狐會認為有9個節點、IE8認為有4個節點。
高階瀏覽器會把空文字當做一個節點,標籤前後的空文字也被算作一個。
註釋的前後算不算空文字節點,各個瀏覽器有不同的解釋。所以用節點的時候,一定要去過濾、判斷節點的nodeType是不是1。
<div id="box"> <p></p> <p></p> <p></p> <p></p> </div>
oBox.childNodes.length; //Chrome數值9、IE678是4 |
為解決相容性問題(到底空文字算不算兒子,所以要封裝函式來解決):
可以利用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屬性表示父親節點。任何節點的parentNode的nodeType一定是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 previousSibling和nextSibling兄弟節點
上一個兄弟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,實際上瀏覽器會認為我們點選了所有的盒子,甚至於body、document、window。
為了描述事件的傳播,人為規定了一個事件的傳播方向,稱為“事件流”。兩個階段:事件捕獲階段,事件冒泡階段。
“事件流”描述的是頁面上各個元素接收事件的順序。
4.2 DOM0級事件監聽
DOM分級別,DOM0級、1級、2級、3級,是不同的標準,標準一直在升級。
之前學習的on開頭的語法新增事件,稱為“DOM0級事件”。
事件的觸發一定是按照事件流的順序,由於DOM0級只能監聽冒泡階段,所以順序是:box3→box2→box1→body→document→window 如果改變監聽順序,彈出順序不變。
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級事件,IE6、7事件只能冒泡到document,IE8只能冒泡body,不能繼續冒泡到window。也就是說不能給window物件新增事件。
4.3 DOM2級事件監聽
DOM1級規範中,沒有對事件進行改動,所以沒有DOM1級的事情
DOM2級做了新的規範,不用on**來繫結監聽了,而是用一個方法
W3C推出了addEventListener()函式,add新增、event事件,listener監聽
它接收三個引數:事件,函式,是否監聽捕獲階段
元素.addEventListener(事件,事件處理函式,是否新增到捕獲階段) |
第一個引數:事件名不用謝on。(click、mouseover)
第二個引數:函式可以是匿名函式,也可以是有名函式
第三個引數:布林值,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);
彈出順序是:A→B→C
DOM2級事件監聽中,this指的是觸發事件的這個元素,就是事件傳播到的這個元素。
DOM2級小測試-看看執行順序:正確答案在下篇文章。
小題目:頁面box1最大,巢狀box2、box3,點選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事件監聽
IE6、7、8不支援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'); });
彈出c、b、a
最詭異是,this永遠是window物件而不是事件傳播到的元素。
box1.attachEvent('onclick', function(){ alert(this === window); });
所以最正確的工作實踐,就是用DOM0級事件監聽方法,除非基於特別充分的理由,否則不用DOM2級。什麼時候理由充分用DOM2級呢?比如一些新興事件,如手機事件:touchstart、touchmove、touchend事件等等。只能用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); }
IE6、7、8用detachEvent即可,同樣,也必須起名。
box1.attachEvent('onclick',fun) function fun(){ alert('你好'); } btn.onclick = function(){ box1.detachEvent('onclick',fun); }
五、事件物件event
這裡的知識就不是理論了 ,有實戰價值。
5.1 event相容性問題
在任何事件中,都有事件物件event。瀏覽器會往事件處理函式中,傳入一個實參,就是事件物件,裡面封裝了你這次觸發事件(點選、滑鼠移入、移出、鍵盤按下)的細節。比如滑鼠位置、是否按下某個鍵等等。
一般用變數event來接收,注意,不是必須的,名字可以改。
所有事件的細節,都被封裝到event物件裡面。
比如clinetX、clinetY就是這次滑鼠點選的位置。
先說相容性問題,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,表示這個事件冒泡,但是要記住:
onmouseover的event.bubbles是true,表示冒泡 onmouseout的event.bubbles是true,表示冒泡 onmouseenter的event.bubbles是false,表示不冒泡 |
有兩個事件不冒泡:
onmouseenter和mouseleave的event.bubbles是false,不冒泡 |
onmouseenter和mouseleave這兩個事件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') }
onmouseenter和mouseleave比onmouseover和onmouseout好用
// $('#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(); });
●target、srcElement屬性,表示事件發生的最內層元素
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,讓ol的event.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); } } }