1. 程式人生 > >深入理解DOM事件機制系列第二篇——事件處理程式

深入理解DOM事件機制系列第二篇——事件處理程式

前面的話

  事件處理程式又叫事件偵聽器,實際上就是事件的繫結函式。事件發生時會執行函式中相應程式碼。事件處理程式有HTML事件處理程式、DOM0級事件處理程式、DOM2級事件處理程式和IE事件處理程式四類,下面將詳細介紹該部分內容

HTML事件處理程式

  某個元素支援的每種事件,都可以使用一個與相應事件處理程式同名的HTML特性來指定。這個特性的值應該是能夠執行的javascript程式碼

  在事件處理程式函式內部,this值等於事件的目標元素

<div id="box" style="height:30px;width:200px;background-color:pink;"
onclick = "this.innerHTML+= '1';"></div>

  在HTML中定義的事件處理程式也可以呼叫在頁面其他地方定義的指令碼

<div id="box" style="height:30px;width:200px;background-color:pink;"onclick = "test()"></div>
<script>
    function test(){box.innerHTML+= '1';}    
</script>

  HTML事件處理程式會建立一個封裝著元素屬性值的函式。這個函式中有一個區域性變數event,也就是事件物件。通過event變數,可以直接訪問事件物件,不用自己定義它,也不用從函式的引數列表中獲取

<div id="box" style="height:30px;width:200px;background-color:pink;"onclick = "this.innerHTML+= event.type;"></div>

  在事件處理程式函式內部,可以像訪問區域性變數一樣訪問document及該元素本身的成員。如此一來,事件處理程式要訪問自己的屬性就簡單多了

<button id="box" value="test" style="height:30px;width:200px;background-color:pink;"onclick = "this.innerHTML+= value;"
></button>

【擴充套件】

  下列這種情況輸出的是空字串'',如果與預想結果不一致,請移步至此

<script>
var value=123;
</script>
<button style="height:30px;width:200px;background-color:pink;"onclick = "this.innerHTML+= value;"></button>

缺點

【1】時差問題

  因為使用者可能會有HTML元素一出現在頁面上時就觸發相應的事件,但當時的事件處理程式有可能尚不具備執行條件,就會報錯

<button style="height:30px;width:200px;background-color:pink;"onclick = "this.innerHTML+= val;"></button>
<script src="http://www.qq.com/test.js"></script>
<script>
var val=123;
</script>

【2】耦合問題

   客戶端程式設計的通用風格是保持HTML內容和javaScript行為分離,所以應該避免使用HTML事件處理程式屬性,因為這些屬性直接混合了javascript和HTML,且不易擴充套件

DOM0級事件處理程式

  通過javascript指定事件處理程式的傳統方式,就是將一個函式賦值給一個事件處理程式屬性。這種為事件處理程式賦值的方法是在第四代Web瀏覽器中出現的,而且至今仍然為所有現代瀏覽器所支援。原因一是簡單,二是具有跨瀏覽器的優勢

  每個元素都有自己的事件處理程式屬性,這些屬性通常全部小寫,將這種屬性的值設定為一個函式,就可以指定事件處理程式

  [注意]以DOM0級方式新增的事件處理程式會在事件流的冒泡階段被處理

<div id="box" style="height:30px;width:200px;background-color:pink;"></div>
<script>
box.onclick = function(){this.innerHTML += '1';}    
</script>    

  可以通過將事件處理程式屬性設定為null來刪除事件處理程式

box.onclick = null;

缺點

  DOM0級事件處理程式的缺點是圍繞著每個事件目標對於每種事件型別只能新增一個事件處理程式

DOM2級事件處理程式

  DOM2級事件處理程式定義了兩個方法用於處理指定和刪除事件處理程式的操作:addEventListener()和removeEventListener()

  所有DOM節點中都包含這兩個方法,並且它們都接受3個引數:要處理的事件名、作為事件處理程式的函式和一個布林值。最後的布林值引數如果是true,表示在捕獲階段呼叫事件處理程式;如果是false,表示在冒泡階段呼叫事件處理程式。若最後的布林值不填寫,則和false效果一樣

  [注意]IE8-瀏覽器不支援DOM2級事件處理程式

  使用DOM2級事件處理程式的好處是可以新增多個事件處理程式,並按照他們新增的順序觸發

  以下程式碼以1-2的順序輸出

<div id="box" style="height:30px;width:200px;background-color:pink;"></div>
<script>
box.addEventListener('click',function(){this.innerHTML += '1'},false);
box.addEventListener('click',function(){this.innerHTML += '2'},false);    
</script>    

  以下程式碼以2-1的順序輸出

<div id="box" style="height:30px;width:200px;background-color:pink;"></div>
<script>
setTimeout(function(){
box.addEventListener('click',function(){this.innerHTML += '1'},false);    
},16);
box.addEventListener('click',function(){this.innerHTML += '2'},false);    
</script>

引數

  如果希望向監聽函式傳遞引數,可以用匿名函式包裝一下監聽函式

<div id="box" style="height:30px;width:200px;background-color:pink;"></div>
<script>
box.addEventListener("click",function(){
    test('123');
},false);
function test(x){box.innerHTML += x;}
</script>

移除

  通過addEventListener()新增的事件處理程式只能使用removeEventListener()來移除,移除時傳入的引數與新增處理程式時使用的引數相同。這意味著,addEventListener()新增的匿名函式將無法移除  

  以下無效

<div id="box" style="height:30px;width:200px;background-color:pink;"></div>
<script>
box.addEventListener("click",function(){
    this.innerHTML += '1'
},false);
box.removeEventListener('click',function(){
    this.innerHTML += '1'
},false);    
</script>

  以下有效

<div id="box" style="height:30px;width:200px;background-color:pink;"></div>
<script>
var handle = function(){this.innerHTML += '1'};
box.addEventListener("click",handle,false);
box.removeEventListener('click',handle,false);    
</script>

IE事件處理程式

  IE實現了與DOM中類似的兩個方法:attachEvent()和detachEvent()。這兩個方法接受相同的兩個引數:事件處理程式名稱與事件處理程式函式。由於IE8-瀏覽器只支援事件冒泡,所以通過attachEvent()新增的事件處理程式都會被新增到事件冒泡階段

  attachEvent()方法的第一個引數是"onclick",而非DOM的addEventListener()方法中的"click"

<div id="box" style="height:30px;width:200px;background-color:pink;"></div>
<script>
box.attachEvent('onclick',function(){this.innerHTML += '1';});
</script>

   [注意]attachEvent()方法只冒泡到document,且IE10-瀏覽器支援

<div id="box" style="height:30px;width:200px;background-color:pink;"></div>
<button id="reset">還原</button>
<script>
//IE10-瀏覽器返回div body html document
//其他瀏覽器報錯
reset.onclick = function(){history.go();}
box.attachEvent('onclick',function(){box.innerHTML += 'div\n';});
document.body.attachEvent('onclick',function(){box.innerHTML += 'body\n';});
document.documentElement.attachEvent('onclick',function(){box.innerHTML += 'html\n';});
document.attachEvent('onclick',function(){box.innerHTML += 'document\n';});
window.attachEvent('onclick',function(){box.innerHTML += 'window\n';});
</script>    

this

  與其他三個事件處理程式不同,IE事件處理程式的this指向window,而非被繫結事件的元素

<!-- <div> -->
<div id="box" style="height:100px;width:300px;background-color:pink;"
onclick = "console.log(this)"></div>
<div id="box" style="height:100px;width:300px;background-color:pink;"></div>
<script>
box.onclick= function(){
    console.log(this);//<div>
}
</script>
<div id="box" style="height:100px;width:300px;background-color:pink;"></div>
<script>
box.addEventListener('click',function(){
    console.log(this);//<div>
});
</script>
<div id="box" style="height:100px;width:300px;background-color:pink;"></div>
<script>
box.attachEvent('onclick',function(){
    console.log(this);//window
});
</script>

順序

  使用attachEvent()方法新增的事件處理程式的觸發順序是有區別的。IE9、10瀏覽器是按正序執行的,而IE8-瀏覽器則是按倒序執行的  

<div id="box" style="height:30px;width:100px;background-color:pink;"></div>
<script>
box.attachEvent('onclick',function(){
    box.innerHTML += '1';
});
box.attachEvent('onclick',function(){
    box.innerHTML += '2';
});
</script>

移除

  使用attachEvent()新增的事件可以通過detachEvent()來移除,條件是必須提供相同的引數。與DOM方法一樣,這也意味著新增的匿名函式將不能被移除。不過,只要能夠將對相同函式的引用傳給detachEvent(),就可以移除相應的事件處理程式  

  以下無效

<div id="box" style="height:30px;width:200px;background-color:pink;"></div>
<script>
box.attachEvent("onclick",function(){
    box.innerHTML += '1'
},false);
box.detachEvent('onclick',function(){
    box.innerHTML += '1'
},false);    
</script>

  以下有效

<div id="box" style="height:30px;width:200px;background-color:pink;"></div>
<script>
var handle = function(){box.innerHTML += '1'};
box.attachEvent("onclick",handle,false);
box.detachEvent('onclick',handle,false);    
</script>    

總結

  由於IE8-瀏覽器不支援addEventListener()方法,所以需要配合attachEvent()方法來實現全瀏覽器的事件繫結相容寫法。同時,由於attachEvent()方法中的this指向window,所以需要對this進行顯式修改

function addEvent(target,type,handler){
    if(target.addEventListener){
        target.addEventListener(type,handler,false);
    }else{
        target.attachEvent('on'+type,function(event){
            return handler.call(target,event);
        });
    }
}

呼叫順序

  如果瀏覽器同時出現這四種事件處理程式,那麼它們的呼叫順序在各瀏覽器中表現並不一致 

<div id="box" style="height:100px;width:100px;background:pink;" onclick = "this.innerHTML +='html\n'"></div>
<script>
if(box.addEventListener){
    box.addEventListener('click',function(){this.innerHTML += 'DOM2級\n'})
}    
if(box.attachEvent){
    box.attachEvent('onclick',function(){box.innerHTML +='IE\n'})
}
box.onclick = function(){
    this.innerHTML += 'DOM0級\n';
}
</script>

【相同點】

  如果同時出現HTML事件處理程式和DOM0級事件處理程式,DOM0級會覆蓋HTML事件處理程式

【不同點】

  chrome/opera/safari等webkit核心的瀏覽器會按照事件處理程式出現的順序來排列,所以結果為:DOM2級 DOM0級

  firefox瀏覽器和IE瀏覽器會將DOM0級事件優先呼叫

  所以firefox和IE11瀏覽器結果為:DOM0級 DOM2級

  IE9、10瀏覽器結果為:DOM0級 DOM2級 IE

  IE8-瀏覽器結果為:DOM0級 IE

相關推薦

深入理解DOM事件機制系列第二——事件處理程式

前面的話   事件處理程式又叫事件偵聽器,實際上就是事件的繫結函式。事件發生時會執行函式中相應程式碼。事件處理程式有HTML事件處理程式、DOM0級事件處理程式、DOM2級事件處理程式和IE事件處理程式四類,下面將詳細介紹該部分內容 HTML事件處理程式   某個元素支援的每種事件,都可以使用一個與

深入理解this機制系列第二——this繫結優先順序

前面的話   上一篇介紹過this的繫結規則,那如果在函式的呼叫位置上同時存在兩種以上的繫結規則應該怎麼辦呢?本文將介紹this繫結的優先順序 顯式繫結 pk 隱式繫結   顯式繫結勝出 function foo() { console.log( this.a ); } var

深入理解DOM節點類型第一——12種DOM節點類型概述

接下來 詳細 instr lang TE eva published () 兩個 前面的話   DOM是javascript操作網頁的接口,全稱為文檔對象模型(Document Object Model)。它的作用是將網頁轉為一個javascript對象,從而可以使用ja

深入理解指令碼化CSS系列第一——指令碼化行內樣式

前面的話   指令碼化CSS,通俗點說,就是使用javascript來操作CSS。引入CSS有3種方式:外部樣式,內部樣式和行間樣式。本文將主要介紹指令碼化行間樣式 基本用法   行間樣式又叫內聯樣式,使用HTML的style屬性進行設定 <div style="height: 40px

深入理解DOM節點型別第三——註釋節點和文件型別節點

前面的話   把註釋節點和文件型別節點放在一起是因為IE8-瀏覽器的一個bug。IE8-瀏覽器將標籤名為"!"的元素視作註釋節點,所以文件宣告也被視作註釋節點。本文將詳細介紹這兩部分的內容 註釋節點 【特徵】   註釋在DOM中是通過Comment型別來表示,註釋節點的三個node屬性——node

深入理解DOM節點型別第五——元素節點Element

前面的話   元素節點Element非常常用,是DOM文件樹的主要節點;元素節點是HTML標籤元素的DOM化結果。元素節點主要提供了對元素標籤名、子節點及特性的訪問,本文將詳細介紹元素節點的主要內容 特徵   元素節點的三個node屬性——nodeType、nodeName、nodeValue分別是

深入理解javascript作用域系列第一——內部原理

前面的話   javascript擁有一套設計良好的規則來儲存變數,並且之後可以方便地找到這些變數,這套規則被稱為作用域。作用域貌似簡單,實則複雜,由於作用域與this機制非常容易混淆,使得理解作用域的原理更為重要。本文是深入理解javascript作用域系列的第一篇——內部原理   內部原理分成編譯、執

深入理解java虛擬機器系列(一):為什麼要學習JVM?

前言 本來想著關於寫JVM這個專欄,直接寫知識點乾貨的,但是想著還是有必要開篇講一下為什麼要學習JVM,這樣的話讓一些學習者心裡有點底的感覺比較好... 原因一:面試 不得不說,隨著網際網路門檻越來越高,JVM知識也是中高階程式設計師階段必問的一個話題!現在不像以前了,以前會點html都好找工作,現在由於學習

深入理解DOM事件機制系列第三——事件物件

前面的話   在觸發DOM上的某個事件時,會產生一個事件物件event,這個物件中包含著所有與事件有關的資訊。所有瀏覽器都支援event物件,但支援方式不同。本文將詳細介紹事件物件 獲取事件物件   【1】一般地,event物件是事件程式的第一個引數   [注意

深入理解DOM事件機制系列第六——事件模擬

前面的話   事件是網頁中某個特別的瞬間,經常由使用者操作或通過其他瀏覽器功能來觸發。但實際上,也可以使用javascript在任意時刻來觸發特定的事件,而此時的事件就如同瀏覽器建立的事件一樣。本文將詳細介紹事件模擬 引入   以下面的實際需求為例,來詳細說明事

深入理解javascript函數進階系列第二——函數柯裏化

計算 all urn ray body turn () 通過 null 前面的話   函數柯裏化currying的概念最早由俄國數學家Moses Schönfinkel發明,而後由著名的數理邏輯學家Haskell Curry將其豐富和發展,currying由此得

深入理解閉包系列第二——從執行環境角度看閉包

前面的話   本文從執行環境的角度來分析閉包,先用一張圖開宗明義,然後根據圖示內容對程式碼進行逐行說明,試圖對閉包進行更直觀的解釋 圖示 說明   下面按照程式碼執行流的順序對該圖示進行詳細說明 function foo(){ var a = 2; funct

深入理解this機制系列第一——this的4種繫結規則

前面的話   如果要問javascript中哪兩個知識點容易混淆,作用域查詢和this機制絕對名列前茅。前面的作用域系列已經詳細介紹過作用域的知識。本系列開始將介紹javascript的另一大山脈——this機制。本文是該系列的第一篇——this的4種繫結規則 預設繫結   全域性環境中,this預

深入理解javascript物件系列第二——屬性操作

前面的話   對於物件來說,屬性操作是繞不開的話題。類似於“增刪改查”的基本操作,屬性操作分為屬性查詢、屬性設定、屬性刪除,還包括屬性繼承。本文是物件系列的第二篇——屬性操作 屬性查詢   屬性查詢一般有兩種方法,包括點運算子和方括號運算子 var o = { p: 'Hello Wor

深入理解javascript選擇器API系列第二——getElementsByClassName

前面的話   既然有getElementById()和getElementsByTagName()方法,為什麼沒有getElementsByClassName()呢?id屬性、標籤名、class屬性並沒有什麼優劣之分啊。終於,HTML5新增了getElementsByClassName()方法,由於在CSS

深入理解定時器系列第二——被譽為神器的requestAnimationFrame

前面的話   與setTimeout和setInterval不同,requestAnimationFrame不需要設定時間間隔。這有什麼好處呢?為什麼requestAnimationFrame被稱為神器呢?本文將詳細介紹HTML5新增的定時器requestAnimationFrame 引入   計時

深入理解AJAX系列第二--GET請求和POST請求的區別

我們在使用Ajax時,當我們向伺服器傳送資料時,我們可以採用Get方式請求伺服器,也可以使用Post方式請求伺服器.那麼,Get請求和Post請求的區別到底在哪呢? GET請求 get是最常見的請求,最常用於向伺服器查詢某些資訊。必要時,可以將查詢字串引數追

深入理解javascript作用域系列第五

彈出 例子 深入理解java logs title 最終 pre 有變 context 前面的話   對於執行環境(execution context)和作用域(scope)並不容易區分,甚至很多人認為它們就是一回事,只是高程和犀牛書關於作用域的兩種不同翻譯而已。但實際上,

深入理解javascript作用域系列第五——一張圖理解執行環境和作用域

前面的話   對於執行環境(execution context)和作用域(scope)並不容易區分,甚至很多人認為它們就是一回事,只是高程和犀牛書關於作用域的兩種不同翻譯而已。但實際上,它們並不相同,卻相互糾纏在一起。本文先用一張圖開宗明義,然後進行術語的簡單解釋,最後根據圖示內容進行詳細說明 圖示

Kafka 系列(五)—— 深入理解 Kafka 副本機制

一、Kafka叢集 Kafka 使用 Zookeeper 來維護叢集成員 (brokers) 的資訊。每個 broker 都有一個唯一標識 broker.id,用於標識自己在叢集中的身份,可以在配置檔案 server.properties 中進行配置,或者由程式自動生成。下面是 Kafka brokers 叢