<script> function print(){ console.log(1) } X.onclick = print; //是對的 Y.onclick = print(); Z.onclick = print.call(); </script> <button id='X' onclick='print'>A</button> <button id='Y' onclick='print()'>B</button> //是對的 <button id='Z' onclick='print.call()'>C</button> //是對的
HTML 中onclick = '要執行的代碼'
,一旦用戶點擊,瀏覽就eval('要執行的代碼'),這裏不是屬性,不是函數,是一段代碼。
JavaScript 中,一旦用戶點擊,瀏覽器就執行X.onclick.call(X,{})
,意思是onclick
是一個函數屬性,而print
是一個屬性,由於它的類型是對象;但下面的print()
的類型是undefined
。
因此在HTML 中必須傳遞一個要執行的代碼,JavaScript 中必須傳遞一個函數html
<button id='xxx'>A<button> xxx.addEventListener('click',function(){ console.log(1) }) xxx.onclick = function(){ console.log(2) }
那它們兩個的區別是啥呢?xxx.onclick = function(){}
只是一個屬性,因此它是惟一的,若是這時候你要綁定2個onclick
事件就麻煩了。xxx.addEventListener()
它是一個隊列,先進先出,就是說先綁定的先觸發。因此它叫作事件監聽隊列,這裏是排隊,xxx
擁有一個隊列叫作eventlisteners
,你不能直接訪問它,但你能夠經過addEventListener
去排個隊,若是用戶點擊了,就先觸發排在第一位的函數,排在後面的一次觸發。隊列的實質是數組。既然有入隊,那是否是也有出隊:node
function f1(){ console.log(1); } function f2(){ console.log(2); } function f1(){ console.log(3); } xxx.addEventListener('click',f1); xxx.addEventListener('click',f2); xxx.removeEventListener('click',f1); xxx.addEventListener('click',f3); xxx.removeEventListener('click',f3);
最後的結果就是打印:2。
f1 在排隊的過程當中移除了,f3 剛進入隊列就移除了。數組
前面寫輪播的時候用了one
事件,它內部是怎樣的呢?瀏覽器
function f1(){ console.log(1); xxx.removeEventListener('click',f1); } xxx.addEventListener('click',f1);
one
在函數處理時的最後一句加上離開隊列語句,on
就是沒有出隊這句的,也就是說一直會執行。函數
捕獲事件:事件從最外層父元素到最裏層子元素的順序觸發,叫作捕獲事件,果document > html > body > div > p > button
代理
冒泡事件:正好和捕獲事件相反,事件從最裏層子元素到最外層父元素的順序觸發,叫作冒泡事件,如:button > p > div > body > html > document
code
DOM 2 中規定了同時支持捕獲事件和冒泡事件,用addEventListener()
方法綁定一個事件處理函數,其語法:target.addEventListener(type, listener, useCapture);
type
:表示監聽事件類型的字符串,如clcik
,前面沒有on
listener
:表示監聽的對象或者一個函數useCapture
:表示是否使用捕獲,接受一個布爾值,true
是觸發捕獲事件,不寫默認false
觸發用冒泡事件。htm
事件在默認執行的過程當中是先捕獲後冒泡,第三個參數只是讓它在什麼狀態下觸發。若是一個節點捕獲和冒泡事件同時存在,誰在前誰先執行,和事件無關。對象
<div id="s1">s1 <div id="s2">s2</div> </div> <script> s1.addEventListener("click",function(e){ console.log("s1 冒泡事件"); //4️⃣ },false); s2.addEventListener("click",function(e){ console.log("s2 冒泡事件"); //2️⃣ },false); s1.addEventListener("click",function(e){ console.log("s1 捕獲事件"); //1️⃣ },true); s2.addEventListener("click",function(e){ console.log("s2 捕獲事件"); //3️⃣ },true); </script>
當我點擊在s2
上面,觸發順序是s1捕獲事件
> s2冒泡事件
> s2捕獲事件
> s1冒泡事件
隊列
事件代理:事件會在冒泡階段向上傳播到父節點,所以能夠把子節點的監聽函數定義在父節點上,由父節點的監聽函數統一處理多個子元素的事件。這種方法叫作事件的代理(delegation)。
<ul id="colors"> <li>red</li> <li>yellow</li> <li>blue</li> <li>green</li> <li>black</li> <li>white</li> </ul> <script> var ul = document.querySelector('ul'); ul.addEventListener('click', function (event) { if (event.target.tagName.toLowerCase() === 'li') { // some code } }); </script>
來看一個具體例子:獲取li
裏面的文本,用事件流方法
var ul = document.querySelector('ul'); ul.addEventListener('click',showColor,false); function showColor(e){ var x = e.target; if(x.nodeName.toLowerCase() === 'li'){ console.log('The color is ' + x.innerHTML); } }
若是不使用事件代理,要獲取li
裏面的文本應該怎麼弄呢?老方法是獲取到全部子元素li
,遍歷它就能夠了
var colors = color-list.children for(var i=0;i<colors.length;i++){ colors[i].addEventListener('click',showColor,false); }; function showColor(e){ var x = e.target; console.log("The color is " + x.innerHTML); };
阻止事件再也不傳播,能夠使用事件對象的stopPropagation()
方法,但不會完全消除事件,若是說綁定多個事件的話。
用stopImmediatePropagation()
方法,綁定的其餘事件也將不會執行。