上一篇是談的事件流,博客地址:點我;這次我們說說具體的事件處理。
0x00:事件處理程序
現在有三種方式注冊事件處理程序:
- HTML事件處理程序
- DOM0級事件處理程序
- DOM2級事件處理程序
001:HTML事件處理程序
就是說給html標簽的屬性設置事件處理程序;例如:
<p onclick="alert('hello')">點我</p>
p標簽的屬性onclick,把它的值設置成javascript代碼字符串;這就是早期的js的用法
現在這個方式,用的不多了,是因為HTML與JavaScript代碼耦合太緊密。就是說html代碼里有javascript代碼,現在都講究行為,樣式,結構分離;
還有就是這里的javascript代碼字符串中的this,event對象是可以直接使用的。例如:
<p onclick="console.log(this);console.log(event)">點我</p>
打印是這樣:
這是因為當指定一串javascript代碼作為HTML事件處理程序屬性值時,瀏覽器會把代碼字符串為類似下面的函數中運行:
function (event) { with(document) { with(this.form || {}) { with(this) { //這里是代碼字符串 } } } }
這就解釋了為什么能直接使用event對象和this對象了。
但是還要注意有時我們也這樣用
<body> <p onclick="aa();">點我</p> <script type="text/javascript"> function aa() { console.log(this); console.log(event); } </script> </body>
這樣打印什么呢?

這時候的this就是window對象,這就是this對象有的時候讓人很難確定到底指的那個作用域。這這里的函數就是window調用的;還有就是如果你想引用指向元素的作用域的this,你可以向這個函數把這個this對象傳遞當參數過去,就像這樣:
<body> <p onclick="aa(this)">點我</p> <script type="text/javascript"> function aa(_this) { console.log(_this); console.log(event); } </script> </body>
打印:

002:DOM0級事件處理程序
就是說,首先獲取對這個元素DOM對象的引用,用DOM的getElementById()等這種方式獲取到對這個元素對象的引用,然后就是每個事件都在這個對象上用相應的屬性,例如click事件,那么這個對象就有一個onclick屬性與之對應,那你在這個對象的屬性上綁定事件處理程序,例obj.onclick = function() {//相應代碼};那么當你在點擊的時候就會觸發事件了;
這種方式大家都太熟悉了我就不舉例了。
這種方式的優點就是幾乎所有瀏覽器都兼容這種方式,一般不存在兼容性問題,我們平時寫代碼現在也用的很多。簡單方便嘛。
但是他也是有缺點的。每個事件元素目標對於每個事件類型只能最多注冊一個事件處理程序。
例如:
obj.onclick = function () { //這里是代碼1 }; obj.onclick = function () { //這里是代碼2 }
那么后面的會覆蓋前面的,前面這個注冊程序就不會執行了。
這種事件處理的程序在事件流中只能會冒泡;之前的在事件流說過這個;
要是想刪除這個事件處理程序,就這樣:
obj.onclick = null;
003:DOM2級事件處理程序
DOM2級事件定義了兩個方法,addEventListener()方法和removeEventListener()方法來處理和刪除事件處理程序。它們可以接收三個參數,第一個參數:要處理的事件名,是一個字符串,但是要記得不要加“on”作為前綴,第二個參數:作為事件處理的函數,第三個參數:一個布爾值,這個布爾值為true時,那就在事件捕獲階段調用事件處理程序,如果是false那就在冒泡階段調用事件處理程序。ps。這兩個方法支持除IE8及以下的其他版本的所有瀏覽器
它的主要優點是可以為同一個對象的同一個事件綁定多個事件處理程序。並且注冊的多個事件是按順序執行的;
上例子:
<p id="test">點我</p> <script type="text/javascript"> var test = document.getElementById("test"); test.addEventListener("click",function () { alert("first click"); },false);
test.addEventListener("click",function () {
alert("secend click");
},false);
</script>
如果你想移除事件處理程序,你就需要用到removeEventListener()方法,它也接收三個參數,這三個參數要和你用addEventListener添加事件時的參數一樣,必須要一模一樣;並且如果addEventListener()的第二個參數,如果是匿名函數,那么用removerEventListener()是刪除不了的;並且它也就只能處理用addEventListener注冊的事件,那么用DOM0級注冊的事件是不能用它刪除的。
上離子:
test.addEventListener("click",fn,false);
test.removeEventListener('click',fn,false);
這樣就跟你沒有注冊事件似的;
但是如果你這樣:
test.addEventListener("click",fn,false);
test.addEventListener("click",fn,false);
test.addEventListener("click",fn,false);
卻只會彈出一個alert,而不是三個,這就是如果你使用相同的參數在同一對象調用多次,處理程序只是注冊一次;
IE永遠都是一朵奇葩,IE8及以下不支持,但是有兩個跟DOM2級的方法相似的事件處理方法,attachEvent()和detachEvent()方法。需要兩個參數,第一參數:事件名,注意要在事件名前加“on”,第二個參數:事件處理程序函數;它也是可以綁定多個事件處理程序的;
這兩個方法支持IE5 ——IE10,IE11就不再支持了;
我就說說他和標准DOM2級的不同,相同點就不提了,
1.事件處理的作用域:
不管是DOM0級還是DOM2級他們的事件處理程序都是在它們所屬元素的的作用域中運行,但是attachEvent卻不是,事件處理程序會在全局作用域中運行,即事件處理程序中的this === window的,一定要記住這里面的this是指向window的
2.當調用多個事件處理程序時:
第一點:當為同一DOM對象同一個事件添加多個不同的事件處理程序函數時:在IE5-IE8,這個調用順序也很奇葩,后注冊的函數,先被調用,但是IE9,IE10這個順序就變過來了。
第二點:當為同一個DOM對象的同一個事件添加多個相同的事件處理程序函數時:在IE5——IE8是你添加幾個就執行幾個,但是在IE9,10卻不是這樣,它只會為你注冊一個事件處理程序,即你添加多個執行一個;ps。但是你如果用匿名函數,其實每個匿名函數都是不同的函數。即使你里面的內容一樣。
由於IE的這種奇葩,我們想用DOM2級的方法,但是又要照顧IE8及以下,這就產生了一個跨瀏覽器的事件處理程序
//通過能力檢測來確定瀏覽器支持 var EventUtil = { //param ele 元素對象 //param type 事件類型 //param handler 事件處理程序函數 addHandler: function (ele,type,handler) { if(ele.addEventListener) { ele.addEventListener(type,handler,false); } else if (ele.attachEvent) { ele.attachEvent("on" + type,handler); } else { ele["on" + type] = handler; } }, removeHandler: function (ele,type,handler) { if (ele.removeEventListener) { ele.removeEventListener(type,handler,false); } else if(ele.attachEvent) { ele.detachEvent("on" + type,handler); } else { ele["on" + type] = null; } } }
這個程序沒有考慮IE的attachEvent()函數的作用域問題,也就是說它里面this不能用。但你可以直接引用這個對象,或者用event對象的srcElement獲取到這個對象;
0x01 事件對象
在觸發DOM上的事件時就會自動產生一個事件對象,這個對象包含這個事件的相關信息。
001.獲取event
就是直接在事件處理程序函數傳遞第一個參數就是event對象,這個是瀏覽器自動傳遞的。但是我今天親測了一下發現:
|
|
DOM0級 |
DOM2級(addEventListener方法) |
attachEvent (僅IE5—IE10支持) |
| chrome |
支持傳參; 也支持window.event |
支持傳參; 也支持window.event |
|
| IE |
支持傳參; 也支持window.event (但是IE8及以下不支持傳參方式) |
支持傳參; 也支持window.event (IE9-IE11支持下)
|
支持傳參; 也支持window.event |
| firefox |
僅支持傳參;
|
僅支持傳參;
|
|
| opera |
支持傳參; 也支持window.event |
支持傳參; 也支持window.event |
|
我用的瀏覽器都是最新的;
但是一般為了兼容性一般在用到的是時候這樣寫:
event = event || window.event;
002.取消默認操作行為
比如a標簽只要你點擊就跳轉到href屬性值的url,要是你不想讓它跳轉,你就要阻止默認行為;
DOM0級事件處理程序中的取消默認事件:方式1:在事件處理程序的函數中返回值設置為false,這個所有瀏覽器都支持;
方式2:event對象的方法,這樣用:event.preventDefault()方法,但是這個只是一部分瀏覽器支持:chrome,opera,IE11;那就是說其他的都不支持了;還有IE自家的returnValue屬性,event.returnValue = false;也可取消默認行為,僅IE5-IE10支持;
總結:如果你想用,那你就用返回值這種方式,兼容性最好了。
DOM2級中:那就直接用event.preventDefault();但是IE的attachEvent中要取消默認行為,要用event.returnValue = false;
003.阻止事件傳播(冒泡或捕獲)
DOM0級和DOM2級都可以用event對象的stopPropagation(),但是IE8及以下不支持;
IE8及以下可以用事件對象的cancelBubble屬性,設置為true就取消冒泡;
004.在介紹一個event對象的一個type屬性的用法:它指的是事件目標
在例子中說明:
function handler (evt) { switch(evt.type) { case "click": //相應代碼 console.log("clicked"); break; case "mouseover": // 相應代碼 console.log("mouseover"); break; case "mouseout": //相應代碼 console.log("mouseout"); break; } } test.onclick = handler; test.onmouseover = handler; test.onmouseout = handler;
多了不解釋;
005.event中的屬性
不同的事件它的event對象所包含的屬性也會有所不同。
下列只是列出了每個事件都會有的
| 屬性/方法 | 類型 | 讀/寫 | 說明 |
| bubbles | Boolean | read | 表明事件是否冒泡 |
| cancelable | Boolean | read | 表明是否可以取消事件的默認行為 |
| currentTarget | Element | read | 其事件處理程序當前正在處理事件的那個元素 |
| defaultPrevented | Boolean | read | 為true表示已經調用了preventDafult() |
| detail | Integer | read | 與事件相關的細節信息 |
| eventPhase | Integer | read | 表明調用事件處理程序的階段:1表示捕獲階段 2表示處於目標,3表示冒泡階段 |
| preventDefault() | Function | read | 取消事件的默認行為。如果cancelable是true,則可以使用這個方法 |
| stopImmediatePropagation() | Function | read | 取消事件的進一步冒泡或捕獲,同時阻止任何事件處理程序被調用 |
| stopPropagation() | Function | read | 取消事件的進一步捕獲或冒泡 |
| target | Element | read | 事件目標 |
| trusted | Boolean | read | 為true表示事件是瀏覽器生成的,false表示是由開發人員生成的 |
| type | String | read | 被觸發的事件類型 |
| view | AbstractView | read | 與事件相關的抽象視圖 |
其實還有很多,比如鼠標事件還要有坐標位置信息,如clientX,clientY,screenX,screenY等,但是在鍵盤事件中就沒有坐標信息
IE中所共有的
| 屬性/方法 | 類型 | 讀寫 | 說明 |
| cancelBubble | Boolean | read,write | 默認為false,但是將其設置為true就取消事件的冒泡,(與DOM中的stopPropagation對應 |
| returnValue | Boolean | read,write | 默認為true,設置為false取消事件的默認行為(與DOM的preventDefault) |
| srcElement | Element | read | 事件目標(與DOM中的target對應) |
| type | String | read | 被觸發的事件類型 |
006.跨瀏覽器事件對象
var EventUtil = { getEvent:function (event) { return event ? event : window.event; }, getTarget: function (event) { return event.target || event.srcElement; }, preventDefault: function (event) { if (event.preventDefault) { event.preventDefault(); } else { event.returnValue = false; } }, stopPropagation: function (event) { if(event.stopPropagation) { event.stopPropagation(); } else { event.cancelBubble = true; } } };
如有不准或錯誤,歡迎指正!
參考:javascript高級程序設計;
javasript權威指南;
