《JavaScript高級程序設計》筆記:事件(十三)

事件流

事件冒泡

IE的事件流叫作事件冒泡,即事件開始時由最具體的元素接收,而後逐級向上傳播到較爲不具體的節點(文檔)。以下代碼:css

<body>
    <div id="myDiv">click me</div>
</body>

若是你點擊了上面的div元素,那麼這個click事件會按照以下順序傳播:html

  • (1)<div>
  • (2)<body>
  • (3)<html>
  • (4)document

全部現代瀏覽器都支持事件冒泡,但在具體實現上仍是有一些差異。IE5.5以及更早的版本事件冒泡會跳過<html>元素(從<body>直接跳到document)。IE9及其它瀏覽器則將事件一直冒泡到window對象。web

事件捕獲

事件捕獲的思想是不太具體的節點應該更早接收到事件,而最具體的節點應該最後接收到事件。如上面的例子,單機div元素,那麼就會按照如下順序觸發click事件:windows

  • (1)document
  • (2)<html>
  • (3)<body>
  • (4)<div>

因爲老版本瀏覽器的不支持,所以不多有人使用事件捕獲。數組

DOM事件流

「DOM2級事件」規定的事件流包括三個階段:事件捕獲階段、處於目標階段和事件冒泡階段。瀏覽器

事件處理程序

HTML事件處理程序

<input type="button" value="click me" onclick="alert(this.value)"/>

DOM0級事件處理程序

var btn = document.getElementById('myBtn');
btn.onclick = function(){
    console.log(this.value);
}

DOM2級事件處理程序

「DOM2級事件」定義了兩個方法,用於處理指定和刪除事件處理程序的操做:addEventListener()removeEventListener()。接受三個參數:要處理的事件名、做爲事件處理程序的函數和一個布爾值。布爾值爲true,表示在捕獲階段調用事件處理程序;若是爲false,表示在冒泡階段調用事件處理程序。緩存

var btn = document.getElementById('myBtn');
btn.addEventListener('click',function(){
    alert(this.value)
},false)

IE事件處理程序

IE中實現了與DOM中相似的兩個方法:attachEvent()detachEvent()。這兩個方法接受相同的兩個參數:事件處理程序名稱和事件處理程序函數。因爲IE8及更早版本只支持事件冒泡,因此經過attachEvent()添加的事件處理程序都會被添加到冒泡階段。app

btn.attachEvent('onclick',function(){
    alert(this.value)  //undefined
})

結果爲undefined的緣由是,在使用attachEvent()方法的狀況下,事件處理程序會在全局做用域中運行。所以this等於window。在編寫跨瀏覽器區別的時候,牢記這一區別很是重要。框架

attachEvent()方法也能夠爲一個元素添加多個事件處理程序,以下:iphone

btn.attachEvent('onclick',function(){
    alert('clicked')
})
btn.attachEvent('onclick',function(){
    alert('hello world!')
})

在IE9以及更改版本瀏覽器獲得的結果順序跟addEventListener()的順序同樣,結果是先clicked,後hello world!。在IE8以及之前版本獲得順序是相反的。

跨瀏覽器的事件處理程序

var EventUtil = {
    addHandler:function(element,type,handler){
        if(element.addEventListener){
            element.addEventListener(type,handler,false);
        }else if(element.attachEvent){
            element.attachEvent("on" + type,handler);
        }else{
            element["on" + type] = handler;
        }
    },
    removeHandler:function(element,type,handler){
        if(element.removeEventListener){
            element.removeEventListener(type,handler,false);
        }else if(element.detachEvent){
            element.detachEvent("on" + type,handler);
        }else{
            element["on" + type] = null;
        }
    }
}

調用方式:

var handler = function(){
    alert('clicked');
}
EventUtil.addHandler(btn,"click",handler);

//這裏省略其它代碼
EventUtil.removeHandler(btn,"click",handler);

事件對象

 DOM中的事件對象

兼容DOM的瀏覽器會將一個event對象傳入到事件處理程序中,不管指定事件處理程序時用什麼方法(DOM0級或DOM2級),都會傳入event對象。下面的例子:

var btn = document.getElementById('myBtn');

btn.onclick = function(event){
    console.log(event.type); //click
}
btn.addEventListener('click',function(event){
    console.log(event.type); //click
},false)

在經過HTML特性指定事件處理程序時,變量event中保存着event對象,下面例子:

<input type="button" value="click me" onclick="alert(event.type)"/>

event對象包含與建立它的特定事件有關的屬性和方法。觸發的事件類型不同,可用的屬性和方法也不同。不過,全部事件都會有下表列出的成員:

  •  bubbles:(Boolean)只讀,代表事件是否冒泡。
  • cancelable:(Boolean)只讀,代表是否能夠取消事件的默認行爲。
  • currentTarget:(Element)只讀,其事件處理程序當前正在處理事件的那個元素。
  • defaultPrevented:(Boolean)只讀,爲true代表已經調用了preventDefault()(DOM3級事件中新增)。
  • detail:(Integer)只讀,與事件相關的細節信息。
  • eventPhase:(Integer)只讀,調用事件處理程序的階段:1表示捕獲階段,2表示「處於目標」,3表示冒泡階段。
  • preventDafult():(Function)只讀,取消事件的默認行爲。若是cancelable是true,則可使用這個方法。
  • stopImmediatePropagation():(Function)只讀,取消事件的進一步捕獲或冒泡,同時阻止任何事件處理程序被調用(DOM3級事件中新增)。
  • stopPropagation():(Function)只讀,取消事件的進一步捕獲或冒泡。若是過bubbles爲true,則可使用這個方法。
  • target:(Element)只讀,事件的目標。
  • trusted:(Boolean)只讀,爲true表示事件是瀏覽器生成的,爲false表示事件是有開發人員經過JavaScript建立的(DOM3級事件中新增)。
  • type:(String)只讀,被觸發的事件的類型。
  • view:(AbstractView)只讀,與事件關聯的抽象視圖。等同於發生事件的window對象。

在事件處理程序內部,對象this始終等於currentTarget的值,而target只包含事件的實際目標。若是直接將事件處理程序指向給了目標元素,則this,currentTarget和target包含相同的值,以下代碼:

var btn = document.getElementById('myBtn');

btn.addEventListener('click',function(event){
    console.log(event.currentTarget === this); //true
    console.log(event.target === this); //true
},false)

若是事件處理程序存在於按鈕的父節點中(例如document.body),那麼這些值是不相等的,以下代碼:

var btn = document.getElementById('myBtn');

document.body.addEventListener('click',function(event){
    console.log(event.currentTarget === document.body); //true
    console.log(document.body === this); //true
    console.log(event.target === btn); //true
},false)

 在須要一個函數處理多個事件時,可使用type屬性,以下:

var btn = document.getElementById('myBtn');
var handler = function(event){
    switch(event.type){
        case "click":
            console.log("clicked");
            break;
        case "mouseover":
            event.target.style.backgroundColor = "red";
            break;
        case "mouseout":
            event.target.style.backgroundColor ="";
            break;
    }
};

btn.onclick = handler;
btn.onmouseover = handler;
btn.onmouseout = handler;

阻止事件的默認行爲,使用preventDefault()方法,好比阻止a標籤的跳轉,以下代碼:

var link = document.getElementById('myLink');
link.onclick = function(event){
    event.preventDefault();
}

只有cancelable屬性設置爲true時,纔可使用preventDefault()來取消其默認行爲。

stopPropagation()方法用於當即中止事件在DOM層次中的傳播,寄取消進一步的事件捕獲或冒泡。例如直接添加到一個按鈕的事件處理程序能夠調用stopPropagation(),從而避免觸發註冊在document.body上面的事件處理程序,以下代碼:

var btn = document.getElementById('myBtn');
btn.onclick = function(event){
    console.log('clicked');
    event.stopPropagation();
}
document.body.onclick = function(){
    console.log('body clicked');
}

結果只打印出了clicked。

事件對象的eventPhase屬性,能夠用來肯定事件當前正處理事件流的哪一個階段。1表示捕獲階段,2表示「處於目標」,3表示冒泡階段。

var btn = document.getElementById('myBtn');
btn.onclick = function(event){
    console.log(event.eventPhase);//2
};

document.body.addEventListener('click',function(event){
    console.log(event.eventPhase);//1
},true);

document.body.onclick = function(){
    console.log(event.eventPhase);//3
};

顯示結果順序分別爲1,2,3。

 IE中的事件對象

與訪問DOM中的event對象不一樣,要訪問IE中的event對象有幾種不一樣的方式,取決於事件處理程序的方法。在使用DOM0級方法添加事件處理程序時,event對象做爲window對象的一個屬性存在,以下例子:

var btn = document.getElementById('myBtn');
btn.onclick = function(){
    var event = window.event;
    alert(event.type); //click
};

若是事件程序是使用attachEvent()添加的,那麼就會有一個event對象做爲參數被傳遞到事件處理程序函數中,以下代碼:

var btn = document.getElementById('myBtn');
btn.attachEvent('onclick',function(event){
    alert(event.type); //click
})

 IE中的event對象包含下面的屬性和方法:

  • cancelBubble:(Boolean)讀/寫,默認值爲false,將其設置爲true就能夠取消事件冒泡(與DOM中的stopPropagation()方法的做用相同)。
  • returnValue:(Boolean)讀/寫,默認值爲true,將其設置爲false能夠取消事件的默認行爲(與DOM中的preventDafult()方法的做用相同)。
  • srcElement:(Element)只讀,事件的目標(與DOM中的target屬性相同)。
  • type:(String)只讀,被觸發的事件的類型。

 由於事件處理程序中的做用域是根據指定它的方式來肯定的,因此不能認爲this會始終等於事件目標。故而,最好仍是使用event.srcElement比較保險,以下代碼:

var btn = document.getElementById('myBtn');
btn.onclick = function(){
    alert(window.event.srcElement === this); //true
};

btn.attachEvent('onclick',function(event){
    alert(this); //window
    alert(window.event.srcElement === this); //false
})

取消默認行爲:

var link = document.getElementById('myLink');
link.onclick = function(){
    window.event.returnValue = false;
};

中止事件冒泡:

var btn = document.getElementById('myBtn');
btn.onclick = function(){
    alert('clicked');
    window.event.cancelBubble = true;
};

document.body.attachEvent('onclick',function(event){
    alert('body clicked')
})

 跨瀏覽器的事件對象

var EventUtil = {
    
    //事件處理程序
    addHandler:function(element,type,handler){
        if(element.addEventListener){
            element.addEventListener(type,handler,false);
        }else if(element.attachEvent){
            element.attachEvent("on" + type,handler);
        }else{
            element["on" + type] = handler;
        }
    },
    
    //獲得event對象
    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;
        }
    },
    
    //移除事件處理程序
    removeHandler:function(element,type,handler){
        if(element.removeEventListener){
            element.removeEventListener(type,handler,false);
        }else if(element.detachEvent){
            element.detachEvent("on" + type,handler);
        }else{
            element["on" + type] = null;
        }
    },
    
    //阻止事件捕獲或冒泡
    stopPropagation:function(event){
        if(event.stopPropagation){
            event.stopPropagation();
        }else{
            event.cancelBubble = true;
        }
    }
}

 事件類型

「DOM3級事件」規定以下幾類事件。

  • UI(User Interface,用戶界面)事件,當用戶與頁面上的元素交互時觸發;
  • 焦點事件,當元素得到或者失去焦點時觸發;
  • 鼠標事件,當用戶經過鼠標在頁面上執行操做時觸發;
  • 滾輪事件,當鼠標使用滾輪(或相似設備)時觸發;
  • 文本事件,當在文檔中輸入文本時觸發;
  • 鍵盤事件,當用戶經過鍵盤在頁面上執行操做時觸發;
  • 合成事件,當爲IME(Input Method Editor,輸入法編輯器)輸入字符時觸發;
  • 變更(mutation)事件,當底層DOM結構發生變化時觸發;
  • 變更名稱事件,當元素或屬性名變更時觸發。此類事件已經廢除。

UI事件

  • DOMActivate:表示元素已經被用戶操做(經過鼠標或鍵盤)激活。這個事件在DOM3級事件中被廢除,但firefox2+和Chrome支持它。
  • load:當頁面徹底加載後在window上面觸發,當全部框架都加載完畢時在框架集上面觸發,當圖像加載完畢時在<img>元素上面觸發,或者當嵌入的內容加載完畢時在<object>元素上面觸發。
  • unload:當頁面徹底卸載後在window上面觸發,當全部框架都卸載後在框架集上面觸發,或者當嵌入的內容卸載完畢後早<object>元素上面觸發。
  • abort:在用戶中止下載過程時,若是嵌入的內容沒有加載完,則在<object>元素上面觸發。
  • error:當發送JavaScript錯誤時在window上面觸發,當沒法加載圖片是在<img>元素上面觸發,當沒法加載嵌入內容時在<object>元素上面觸發,或者當有一或多個框架沒法加載時在框架集上面觸發。
  • select:當用戶選擇文本框(<input>或<textarea>)中的一或多個字符時觸發。
  • resize:當窗口或框架的大小發生變化時在window或框架上面觸發。
  • scroll:當用戶滾動帶滾動條的元素中的內容時,在該元素上觸發。<body>元素中包含所加載頁面的滾動條。

多數這些事件都與window對象或者表單控件相關。

除了DOMActivate以外,其它事件在DOM2級事件中都歸爲HTML事件(DOMActivate在DOM2級事件中仍屬於UI事件)。要肯定瀏覽器是否支持DOM2級事件規定的HTML事件,能夠用以下代碼:

var isSupported = document.implementation.hasFeature("HTMLEvents","2.0");

肯定瀏覽器是否支持DOM3級事件定義的事件,代碼以下:

var isSupported = document.implementation.hasFeature("UIEvent","3.0");

1.load事件

EventUtil.addHandler(window,'load',function(event){
    console.log('loaded!');
})

爲<body>元素添加一個onload特性,代碼以下:

<body onload="alert('loaded!')">
</body>

通常在window上面發生的任何事件均可以在<body/>元素中經過相應的特性來指定,由於在HTML中沒法訪問到window元素。建議儘量使用JavaScript方式。

圖片加載:

var image = document.getElementById('myImage');
EventUtil.addHandler(image,'load',function(event){
    event = EventUtil.getEvent(event);
    console.log(EventUtil.getTarget(event).src);
})

待建立新的<img>元素時,能夠爲其指定一個事件處理程序。此時,最重要的是在指定src屬性以前先指定事件,以下代碼:

EventUtil.addHandler(window,'load',function(){
    var image = document.createElement('img');
    EventUtil.addHandler(image,'load',function(event){
        event = EventUtil.getEvent(event);
        console.log(EventUtil.getTarget(event).src);
    })
    document.body.appendChild(image);
    image.src = 'images/b.jpg';
})

須要格外注意的一點是:新圖像元素不必定要從添加到文檔後纔開始下載,只要設置了src屬性就開始下載

一樣的功能可使用DOM0級的Image對象實現,以下代碼:

EventUtil.addHandler(window,'load',function(){
    var image = new Image();
    EventUtil.addHandler(image,'load',function(event){
        event = EventUtil.getEvent(event);
        console.log(EventUtil.getTarget(event).src);
    })
    image.src = 'images/b.jpg';
})

還有一些元素以非標準的的方式支持load事件。在IE9以及更高版本,<script>元素也會觸發load事件。

EventUtil.addHandler(window,'load',function(){
    var script = document.createElement('script');
    EventUtil.addHandler(script,'load',function(event){
        console.log('loaded!');
    })
    script.src = 'js/common.js';
    document.body.appendChild(script);
})

注:IE8以及更早版本不支持<script>元素上的load事件。

<link>元素的load事件:

EventUtil.addHandler(window,'load',function(){
    var link = document.createElement('link');
    link.type ="text/css";
    link.rel ="stylesheet";
    EventUtil.addHandler(link,'load',function(event){
        console.log('loaded!');
    })
    link.href = 'css/rest.css';
    document.getElementsByTagName('head')[0].appendChild(link)
})

與<script>節點相似,在未指定href屬性並將<link>元素添加到文檔以前也不會開始下載樣式表。

2.unload事件

這個事件在文檔徹底被卸載後觸發。只要用戶從一個頁面切換到另外一個頁面,就會發生unload事件。而利用這個事件最多的狀況就是清除引用,以免內存泄露。

EventUtil.addHandler(window,'unload',function(){
    alert('unloaded!');
})

3.resize事件

EventUtil.addHandler(window,'resize',function(){
    alert('resized!');
})

4.scroll事件

EventUtil.addHandler(window,'scroll',function(){
    if(document.compatMode == 'CSS1Compat'){
        console.log(document.documentElement.scrollTop);
    }else{
        console.log(document.body.scrollTop);
    }
})

焦點事件

焦點事件會在頁面得到或者失去焦點時觸發。利用這些事件並與document.hasFocus()方法以及document.activeElement屬性配合,能夠知曉用戶在頁面中的行蹤,如下6個焦點事件。

  • blur:在元素失去焦點時觸發。這個事件不會冒泡;全部瀏覽器都支持它。
  • DOMFocusIn:在元素得到焦點時觸發。這個事件與HTML事件focus等價,但它冒泡。只有opera支持這個事件。DOM3級事件廢除了DOMFocusIn,選擇了focusin。
  • DOMFocusOut:在元素失去焦點時觸發。這個事件是HTML事件blur的通用版本。只有opera支持這個事件。DOM3級事件廢除了DOMFocusOut,選擇了focusout。
  • focus:在元素得到焦點時觸發。這個事件不會冒泡;全部瀏覽器都支持它。
  • focusin:在元素得到焦點時觸發。這個事件與HTML事件focus等價,但它冒泡。支持這個事件的瀏覽器有IE5.5+、Safari5.1+、Opera11.5+和Chrome。
  • focusout:在元素失去焦點時觸發。這個事件是HTML事件blur的通用版本。支持這個事件的瀏覽器有IE5.5+、Safari5.1+、Opera11.5+和Chrome。

IE的focusin和focusout最後被DOM3級事件採納爲標準方式。

當焦點從頁面中的一個元素移動到另外一個元素,會依次觸發下列事件:

(1)focusout在失去焦點的元素上觸發。

(2)focusin在得到焦點的元素上觸發。

(3)blur在失去焦點的元素上觸發。

(4)DOMFocusOut在失去焦點的元素上觸發。

(5)focus在得到焦點的元素上觸發。

(6)DOMFocusIn在得到焦點的元素上觸發。

肯定瀏覽器是否支持這些事件,可使用以下代碼:

var isSupported = document.implementation.hasFeature('FocusEvent','3.0');

鼠標與滾輪事件

DOM3級事件中定義了9個鼠標事件,以下:

  • click:在用戶單機主鼠標按鈕(通常是左邊的按鈕)或者按下回車鍵時觸發。
  • dblclick:在用戶雙擊主鼠標按鈕(通常是左邊的按鈕)時觸發。DOM3級歸入了標準。
  • mousedown:在用戶按下任意鼠標按鈕時觸發。不能經過鍵盤觸發這個事件。
  • mouseenter:在鼠標光標從元素外部首次移動到元素範圍以內時觸發。這個事件不冒泡,並且在光標移動到後代元素上不會觸發。DOM3級事件將它歸入了規範。
  • mouseleave:在位於元素上方的鼠標光標移動到元素範圍以外時觸發。這個事件不冒泡,並且在光標移動到後代元素上不會觸發。DOM3級事件將它歸入了規範。
  • mousemove:當鼠標指針在元素內部移動時重複地觸發。不能經過鍵盤觸發這個事件。
  • mouseout:在鼠標指針位於一個元素上方,而後用戶將其移入另外一個元素時觸發。又移入的另外一個元素可能位於前一個元素的外部,也多是這個元素的子元素。不能經過鍵盤觸發這個事件。
  • mouseover:當鼠標指針位於一個元素外部,而後用戶將其首次移入另外一個元素邊界以內時觸發。不能經過鍵盤觸發這個事件。
  • mouseup:在用戶釋放鼠標按鈕時觸發。不能經過鍵盤觸發這個事件。

頁面上的全部元素都支持鼠標事件。除了mouseentermouseleave,全部鼠標事件都會冒泡,也能夠被取消,而取消鼠標事件將會影響瀏覽器的默認行爲。取消鼠標事件的默認行爲還會影響其餘事件,由於鼠標事件和其餘事件是密不可分的關係。

只有在同一個元素上相繼觸發mousedown和mouseup事件,纔會觸發click事件;若是mousedown或mouseup中的一個被取消,就不會觸發click事件。相似地,只有觸發兩次click事件,纔會觸發一次dblclick事件,若是有代碼阻止了連續兩次觸發click事件(多是直接取消click事件,也可能經過取消mousedown或mouseup間接實現),那麼就不會觸發dblclick事件。這4個事件觸發的順序以下:

(1)mousedown

(2)mouseup

(3)click

(4)mousedown

(5)mouseup

(6)click

(7)dblclick

IE8及以前版本的實現有一個小bug,所以在雙擊事件中,會跳過第二個mousedown和click事件,其順序以下:

(1)mousedown

(2)mouseup

(3)click

(4)mouseup

(5)dblclick

IE9修復了這個bug。

使用以下代碼能夠檢測瀏覽器是否支持如上DOM2級事件(除dblckick、mouseenter和mouseleave以外):

var isSupported = document.implementation.hasFeature('MouseEvents','2.0');

檢測瀏覽器是否支持上面的全部事件,代碼以下:

var isSupported = document.implementation.hasFeature('MouseEvent','3.0');

1.客戶區座標位置

clientXclientY他們的值表示事件發生時鼠標指針在視口中的水平和垂直座標。

var btn = document.getElementById('myBtn');
EventUtil.addHandler(btn,'click',function(event){
    event = EventUtil.getEvent(event);
    console.log("client coordinates:" + event.clientX + "," + event.clientY);
})

2.頁面座標位置

pageXpageY,這兩個屬性表示鼠標光標在頁面中的位置,所以座標是從頁面自己而非視口的左邊和頂邊計算的。

如下代碼能夠取得鼠標事件在頁面中的座標:

var btn = document.getElementById('myBtn');
EventUtil.addHandler(btn,'click',function(event){
    event = EventUtil.getEvent(event);
    console.log("Page coordinates:" + event.pageX + "," + event.pageY);
})

在頁面沒有滾動的狀況下,pageX和pageY的值與clientX和clientY的值相等。

IE8及更早的版本不支持事件對象上的頁面座標,不過可使用客戶區座標和滾動信息能夠計算出來。這時候須要用到document.body(混雜模式)或document.documentElement(標準模式)中的scrollLeft和scrollTop屬性。計算代碼以下:

var btn = document.getElementById('myBtn');
EventUtil.addHandler(btn,'click',function(event){
    event = EventUtil.getEvent(event);
    var pageX = event.pageX,
        pageY = event.pageY;
    if(pageX === undefined){
        pageX = event.clientX + (document.body.scrollLeft || document.documentElement.scrollLeft);
    }
    if(pageY === undefined){
        pageY = event.clientY + (document.body.scrollTop || document.documentElement.scrollTop);
    }
    console.log("Page coordinates:" + pageX + "," + pageY);
})

3.屏幕座標位置

screenXscreenY屬性表示鼠標事件發生時鼠標指針相對於整個屏幕的座標信息。

屏幕座標:

var btn = document.getElementById('myBtn');
EventUtil.addHandler(btn,'click',function(event){
    event = EventUtil.getEvent(event);
    console.log("Page coordinates:" + event.screenX + "," + event.screenY);
})

4.修改鍵

雖然鼠標事件主要是由鼠標來觸發的,但在按下鼠標時鍵盤上的某些鍵的狀態也能夠影響到所要採起的操做。這些修改鍵就是ShiftCtrlAltMeta(在windows鍵盤中的windows鍵,在蘋果機中是Cmd鍵),它們常常被用來修改鼠標事件的行爲。DOM爲此規定了4個屬性,表示這些修改鍵的狀態:shiftKeyctrlKeyaltKeymetaKey。這些屬性中包含的都是布爾值,若是相應的鍵被按下了,則值爲true,不然值爲false。

當某個鼠標事件發生時,經過檢測這幾個屬性能夠肯定是否用戶同時按下了其中的鍵。以下例子:

var btn = document.getElementById('myBtn');
EventUtil.addHandler(btn,'click',function(event){
    event = EventUtil.getEvent(event);
    var keys = new Array();
    if(event.shiftKey){
        keys.push('shift');
    }
    if(event.ctrlKey){
        keys.push('ctrl');
    }
    if(event.altKey){
        keys.push('alt');
    }
    if(event.metaKey){
        keys.push('meta');
    }
    console.log("keys:" + keys.join(','));
})

注:IE8以及以前的版本不支持metaKey屬性。

5.相關元素

在發生mouseovermouseout事件時,還會涉及更多的元素。這兩個事件都會涉及把鼠標指針從一個元素的邊界以內移動到另外一個元素的邊界以內。對mouseover而言,事件的主目標是得到光標的元素,而相關元素就是那個失去光標的元素。相似地,對於mouseout事件而言,事件的主目標就是失去光標的元素,而相關元素是得到光標的元素。來看下面一個例子:

<body>
    <div id="myDiv" style="background-color:red;width:100px;height:100px;"></div>
</body>

這個例子會在頁面上顯示一個<div>元素。若是鼠標指針一開始就在這個<div>元素上,而後移出了這個元素,那麼就會在<div>元素上觸發mouseout事件,相關元素就是<body>元素。與此同時,<body>元素上面會觸發mouseenter事件,相關元素就變成了<div>。

DOM經過event對象的relatedTarget屬性提供了相關元素的信息。這個屬性只對於mouseover和mouseout事件才包含值;對於其它事件,這個屬性的值爲null。IE8以及以前的版本不支持relatedTarget屬性,但提供了保存着一樣信息的不一樣屬性。在mouseover事件觸發時,IE中的fromElement屬性中保存了相關元素;在mouseout事件觸發時,IE的toElement屬性保存着相關元素。(IE9支持這些全部屬性。)把這個添加到EventUtil對象中,以下:

var EventUtil = {
    //省略了其它代碼
    
    //獲得相關元素信息
    getRelatedTarget:function(event){
        if(event.relatedTarget){
            return event.relatedTarget;
        }
        else if(event.toElement){
            return event.toElement;
        }
        else if(event.fromElement){
            return event.fromElement;
        }else{
            return null;
        }
    },
    
    //省略了其它代碼
      
};

調用:

var myDiv = document.getElementById('myDiv');
EventUtil.addHandler(myDiv,'mouseout',function(event){
    event = EventUtil.getEvent(event);
    var target = EventUtil.getTarget(event);
    var relateTarget = EventUtil.getRelatedTarget(event);
    console.log("moused out of" + target.tagName + "to" + relateTarget.tagName);  //moused out ofDIVtoBODY
})

6.鼠標按鈕

只有在主鼠標按鈕被單擊(或鍵盤迴車鍵被按下)時纔會觸發click事件,所以檢測按鈕的信息不是必要的。但對於mousedownmouseup事件來講,則在其event對象存在一個button屬性,表示按下或者釋放的按鈕。DOM的button屬性可能有以下3個值:

  • 0:表示主鼠標按鈕;
  • 1:表示中間的鼠標按鈕(鼠標滾輪按鈕);
  • 2:表示次鼠標按鈕;

在常規的設置中,主鼠標按鈕就是鼠標左鍵,而次鼠標按鈕就是鼠標右鍵。

IE8及以前的版本也提供了button屬性,但這個屬性的值與DOM中的button屬性有很大的差別。

  • 0:沒有按下按鈕;
  • 1:表示按下了主鼠標按鈕;
  • 2:表示按下了次鼠標按鈕;
  • 3:表示同時按下了主次鼠標按鈕;
  • 4:表示按下了中間的鼠標按鈕;
  • 5:表示同時按下了主鼠標按鈕和中間的鼠標按鈕;
  • 6:表示同時按下了次鼠標按鈕和中間的鼠標按鈕;
  • 7:表示同時按下了三個鼠標按鈕;

因爲單獨使用能力檢測沒法肯定差別(兩種模型有同名的button屬性),所以必須另闢蹊徑。咱們知道,支持DOM版鼠標事件的瀏覽器課能夠經過hasFearture()方法檢測,全部能夠再爲EventUtil對象添加getButton()方法:

//獲得button屬性
    getButton:function(event){
        if(document.implementation.hasFeature("MouseEvents","2.0")){
            return event.button;
        }else{
            switch(event.button){
                case 0:
                case 1:
                case 3:
                case 5:
                case 7:
                    return 0;
                case 2:
                case 6:
                    return 2;
                case 4:
                    return 1;
            }
        }
    },

調用:

var myDiv = document.getElementById('myDiv');
EventUtil.addHandler(myDiv,'mousedown',function(event){
    event = EventUtil.getEvent(event);
    alert(EventUtil.getButton(event));
})

7.更多的事件信息

「DOM2級事件」規範在event對象中還提供了detail屬性,用於給出有關事件的更多信息。對於鼠標事件來講,detail中包含了一個數值,表示在給定位置上發生了多少次單擊。在同一個像素上相繼的發生一次mousedown和一次mouseup事件算做一次單擊。detail屬性從1開始計數,每次單擊發生後都會遞增。若是鼠標在mousedown和mouseup之間移動了位置,則detail會被重置爲0。

IE也經過下列屬性爲鼠標事件提供了更多信息。

  • altLeft:布爾值,表示是否按下了alt鍵。
  • ctrlLeft:布爾值,表示是否按下了ctrl鍵。
  • offsetX:光標相對於目標元素邊界的x座標。
  • offsetY:光標相對於目標元素邊界的y座標。
  • shiftLeft:布爾值,表示是否按下了shift鍵。

這些屬性用處不大,只有IE支持他們,另外一方面他們提供的信息要麼沒有什麼用,要麼能夠經過其餘方式計算得來。

8.鼠標滾輪事件

IE6.0首先實現了mousewheel事件。這個事件能夠在任何元素上面觸發,最終會冒泡到document(IE8)或window(IE九、Opera、Chrome及Safari)對象。與mousewheel事件對應的event對象除了包含鼠標事件的全部標準信息外,還包含一個特殊的wheelDelta屬性。當用戶向前滾動鼠標滑輪時,wheelDelta是120的倍數;當用戶向後滾動鼠標滑輪時,wheelDelta是-120的倍數。

EventUtil.addHandler(document,'mousewheel',function(event){
    event = EventUtil.getEvent(event);
    console.log(event.wheelDelta);
})

多數狀況下,只須要知道滾輪的滾動方向就夠了,而這經過檢測wheelDelta的正負號就能夠肯定。

注意的是,在Opera9.5以前的版本中,wheelDelta值的正負號是顛倒的。若是須要支持早期的Opera版本,代碼以下:

EventUtil.addHandler(document,'mousewheel',function(event){
    event = EventUtil.getEvent(event);
    var delta = (client.engine.opera && client.engine.opera < 9.5 ? -event.wheelDelta : event.wheelDelta);
    console.log(delta);
})

Firefox支持一個名爲DOMMouseScroll的相似事件,也是在鼠標滾輪滾動時觸發。鼠標滾輪滾動信息保存在detail屬性中,當向前滾動鼠標滾輪時,這個屬性的值爲-3的整數倍,當向後滾動鼠標滾輪時,這個屬性的值是3的整數倍。

EventUtil.addHandler(document,'DOMMouseScroll',function(event){
    event = EventUtil.getEvent(event);
    console.log(event.detail);
})

跨瀏覽器總結,添加到EventUtil對象中:

//取得鼠標滾輪增量值(delta)
    getWheelDelta:function(event){
        if(event.wheelDelta){
            return event.wheelDelta;
        }else{
            return -event.detail * 40;
        }
    },

調用方式:

(function(){
    function handleMouseWheel(event){
        event = EventUtil.getEvent(event);
        var delta = EventUtil.getWheelDelta(event);
        console.log(delta);
    };
    
    EventUtil.addHandler(document,"mousewheel",handleMouseWheel);
    EventUtil.addHandler(window,"DOMMouseScroll",handleMouseWheel);
})();

9.觸摸設備

 IOS和Android的實現很是特別,由於這些設備沒有鼠標。在面向iphone和ipad中的Safari開發時,要記住如下幾點:

  • 不支持dblclick事件。雙擊瀏覽器窗口會放大頁面,並且沒有辦法改變該行爲。
  • 輕擊可單擊元素會觸發mousemove事件。若是此操做會致使內容變化,將再也不有其餘事件發生;若是屏幕沒有所以變化,那麼會依次發生mousedown、mouseup和click事件。輕擊不可單擊的元素不會觸發任何事件。可單擊的元素是指那些單擊可產生默認操做的元素(如連接),或者那些已經被指定了onclick事件處理程序的元素。
  • mousemove事件也會觸發mouseover和mouseout事件。
  • 兩個手指放在屏幕上且頁面隨手指移動而滾動時會觸發mousewheel和scroll事件。

10.無障礙性問題

若是你的web應用程序或者網站要確保殘疾人特別是那些使用屏幕閱讀器的人都能訪問,那麼在使用鼠標事件時就要格外當心。前面提到過,能夠經過鍵盤上的回車鍵來觸發click事件,但其餘鼠標事件卻沒法經過鍵盤來觸發。爲此,咱們不建議使用click以外的其餘鼠標事件來展現功能或者引起代碼執行。由於這樣會給盲人或視障用戶形成極大不便。

鍵盤與文本事件

「DOM3級事件」爲鍵盤事件制定了規範。有3個鍵盤事件以下:

  • keydown:當用戶按下鍵盤上的任意鍵時觸發,並且按住不放的話,會重複觸發該事件。
  • keypress:當用戶按下鍵盤上的字符鍵時觸發,並且按住不放的話,會重複觸發該事件。按下ESC鍵也會觸發該事件。Safari3.1以前的版本也會在用戶按下非字符鍵時觸發該事件。
  • keyup:當用戶釋放鍵盤上的鍵時觸發。

只有一個文本事件:textInput。這個事件是對keypress的補充,用意是在將文本顯示給用戶以前更容易攔截文本。在文本插入文本框以前會觸發textInput事件。

在用戶按下鍵盤上的字符鍵時,鍵盤執行順序:keydown、keypress、keyup。其中keydown、keypress都是在文本框發生變化以前被觸發的;而keyup是在文本框已經發生變化後觸發。若是用戶按下一個字符鍵不放,那麼會重複觸發keydown與keypress,直到用戶鬆開該鍵爲止。

若是用戶按下的是一個非字符鍵時,執行順序:keydown、keyup。

1.鍵碼

在發生keydown與keyup事件時,event對象的keyCode屬性包含一個代碼,與鍵盤上一個特定的鍵對應。DOM和IE中的event對象都支持keyCode屬性。以下例子:

var textbox = document.getElementById('myText');
EventUtil.addHandler(textbox,'keyup',function(event){
    event = EventUtil.getEvent(event);
    console.log(event.keyCode);
})

經常使用非字符鍵的鍵碼:

左箭頭:37;上箭頭:38;右箭頭:39;下箭頭40;上翻頁:33;下翻頁:34;退出(ESC):27。

不管keydown或者keyup事件都會存在一些特殊狀況。在Firefox和Opera中,按分號鍵時keyCode爲59,也就是ASCII中分號的編碼;但在IE,Safari,Chrome中返回186,即鍵盤中按鍵的鍵碼。

2.字符編碼

IE九、Firefox、Chrome和Safari的event對象都支持一個charCode屬性,這個屬性只有在發生keypress事件時才包含值,並且這個值是按下的那個鍵所代碼字符的ASCII編碼。IE8及以前的版本和Opera則是在keyCode中保存字符的ASCII編碼。下面以跨瀏覽器取得字符編碼,放在EventUtil對象中:

getCharCode:function(event){
        if(typeof event.charCode == 'number'){
            return event.charCode;
        }else{
            return event.keyCode;
        }
    },

使用方式:

var textbox = document.getElementById('myText');
EventUtil.addHandler(textbox,'keypress',function(event){
    event = EventUtil.getEvent(event);
    console.log(EventUtil.getCharCode(event));
})

在取得字符編碼後,就可使用String.fromCharCode()將其轉化爲實際的字符。以下:

var textbox = document.getElementById('myText');
EventUtil.addHandler(textbox,'keypress',function(event){
    event = EventUtil.getEvent(event);
    var charCode = EventUtil.getCharCode(event);
    var text = String.fromCharCode(charCode);
})

3.DOM3級變化

DOM3級事件中的鍵盤事件,再也不包含charCode屬性,而是包含兩個新屬性:keychar,因爲這個兩個屬性各瀏覽器支持程度不同,不推薦使用。

DOM3級事件在添加一個location屬性,也不推薦使用。

4.textInput事件

「DOM3級事件」規範中引入了一個新事件,textInput。根據規範,當用戶在可編輯區域中輸入字符時,就會觸發這個事件。keypress和textInput的區別:

  • 任何能夠得到焦點的元素均可以觸發keypress事件,但只有可編輯區域才能觸發textInput事件。
  • textInput事件只有在用戶按下可以輸入實際字符的鍵時纔會觸發,而keypress事件則在按下那些可以影響文本顯示的鍵也會觸發(例如退格鍵)。

因爲textInput事件主要考慮的是字符,所以它的event對象中還包含一個data屬性。這個屬性的值就是用戶輸入的字符(而非字符編碼)。

var textbox = document.getElementById('myText');
EventUtil.addHandler(textbox,'textInput',function(event){
    event = EventUtil.getEvent(event);
    console.log(event.data)
})

HTML5事件

1.contextmenu事件

EventUtil.addHandler(window,'load',function(event){
    var div = document.getElementById('myDiv');
     EventUtil.addHandler(div,'contextmenu',function(event){
         event = EventUtil.getEvent(event);
         EventUtil.preventDefault(event);
         
         var menu = document.getElementById('myMenu');
         menu.style.left = event.clientX + 'px';
         menu.style.top = event.clientY + 'px';
         menu.style.visibility = 'visible';
     })
     
     EventUtil.addHandler(document,'click',function(event){
         document.getElementById('myMenu').style.visibility = 'hidden';
     })
})

2.beforeunload事件

之因此有發生在window對象上的beforeunload事件,是爲了讓開發人員在頁面卸載前阻止這一操做。能夠經過這個事件來取消卸載並繼續使用原有頁面。

EventUtil.addHandler(window,'beforeunload',function(event){
    event = EventUtil.getEvent(event);
    var message = "你肯定要離開這個頁面嗎?";
    event.returnValue = message;
    return message;
})

3.DOMContentLoaded事件

window的load事件會在頁面中的一切都加載完畢時觸發,但這個過程可能會由於要加載的外部資源過多而頗費周折。而DOMContentLoaded事件則在造成完整的DOM樹以後就會觸發,不理會圖像、js文件、css文件或其它資源是否已經下載完畢。與load事件不一樣,DOMContentLoaded支持在頁面下載的早期添加事件處理程序,這也就意味着用戶可以儘早地與頁面進行交互。

要處理DOMContentLoaded事件,能夠爲document或window添加相應的事件處理程序(儘管這個事件會冒泡到window,但它的目標其實是document)。

EventUtil.addHandler(document,"DOMContentLoaded",function(event){
    alert('content loaded');
})

IE9以及其它瀏覽器支持該事件,對於不支持DOMContentLoaded的瀏覽器,建議在頁面加載期間設置一個時間爲0毫秒的超時調用,以下:

setTimeout(function(){
    //在此添加事件處理程序
},0)

4.pageshow和pagehide事件

往返緩存(back-forward cache,或bfcache),用戶使用瀏覽器的「後退」、「前進」按鈕加快頁面的轉換速度。將整個頁面保存在內存裏。

(function(){
    var showCount = 0;
    EventUtil.addHandler(window,'load',function(){
        alert('loaded fired')
    });
    
    EventUtil.addHandler(window,"pageshow",function(event){
        showCount++;
        alert("Show has been fired" + showCount + "times.");
    });
})()

設備事件

1.orientationchange事件

2.MozOrientation事件

3.deviceorientation事件

4.devicemotion事件

觸摸與手勢事件

觸摸事件以下:

  • touchstart:當手指觸摸屏幕時觸發。
  • touchmove:當手指在屏幕滑動時連續的觸發。在這個事件發生期間,能夠調用preventDefault()能夠阻止滾動。
  • touchend:當手指從屏幕上移開時觸發。
  • touchcancel:當系統中止跟蹤觸摸時觸發。

上面這幾個事件都會冒泡,也均可以取消。它們是以兼容DOM的方式實現的。所以,每一個觸摸事件的event對象都提供了在鼠標事件中常見的屬性:bubbles 、cancelabel 、view 、clientX 、clientY 、screenX、 screenY 、detail 、altKey、 ctrlKey、 shiftKey 和metaKey。

除了常見的DOM屬性外,觸摸事件還包括下列三個用於跟蹤觸摸的屬性。

  • touches:表示當前跟蹤的觸摸操做的Touch對象的數組。
  • targetTouchs:特定於事件目標的Touch對象的數組。
  • changedTouches:表示自上次觸摸以來發生了什麼改變的Touch對象的數組。

每一個touch對象包含下列屬性:

  • clientX:觸摸目標在視口中的x座標。
  • clientY:觸摸目標在視口中的y座標。
  • identifier:標識觸摸的惟一ID。
  • pageX:觸摸目標在頁面中的x座標。
  • pageY:觸摸目標在頁面中的y座標。
  • screenX:觸摸目標在屏幕中的x座標。
  • screenY:觸摸目標在屏幕中的y座標。
  • target:觸摸的DOM節點目標。

以下面例子:

 

function handlerTouchEvent(event){
    
    //只跟蹤一次觸摸
    if(event.touches.length == 1){
        var output = document.getElementById('output');
        switch(event.type){
            case "touchstart":
                output.innerHTML = "Touch started(" + event.touches[0].clientX + "," + event.touches[0].clientY + ")";
                break;
            case "touchend":
                output.innerHTML += "<br/>Touch ended(" + event.changedTouches[0].clientX + "," + event.changedTouches[0].clientY + ")";
                break;
            case "touchmove":
                event.preventDefault(); //阻止滾動
                output.innerHTML += "<br/>Touch moved(" + event.changedTouches[0].clientX + "," + event.changedTouches[0].clientY + ")";
                break;
        }
    }
}

EventUtil.addHandler(document,"touchstart",handlerTouchEvent);
EventUtil.addHandler(document,"touchend",handlerTouchEvent);
EventUtil.addHandler(document,"touchmove",handlerTouchEvent);

 注意,在touchend事件發生時,touches集合中就沒有任何的touch對象了,由於不存在活動的觸摸操做;此時就必須轉而使用changedTouches集合。

在觸摸屏幕上的元素時,這些事件(包括鼠標事件)發生的順序以下:

  • 1.touchstart
  • 2.mouseover
  • 3.mousemove(一次)
  • 4.mousedown
  • 5.mouseup
  • 6.click
  • 7.touched

目前只有IOS版的Safari支持多點觸摸。

內存和性能

在JS中,添加到頁面上的事件處理程序數量將直接關係到頁面的總體運行性能。致使這一問題的緣由是多方面的。首先,每一個函數都是對象,都會佔用內存;內存中的對象越多,性能就越差。其次,必須事先指定全部事件處理程序而致使的DOM訪問次數,會延遲整個頁面的交互就緒時間。事實上,從如何利用好事件處理程序的角度出發,仍是有一些辦法可以提高性能的。

事件委託

事件委託利用事件冒泡,只指定一個事件處理程序,就能夠管理某一類型的全部事件。例如,click事件會冒泡到document層次。也就是說,咱們能夠爲整個頁面添加一個onclick事件處理程序,而沒必要給每一個可單擊的元素分別添加事件處理程序。

以下代碼,點擊3個li分別執行不一樣的操做:

 html代碼:

<ul id="myLinks">
    <li id="goSomewhere">Go somewhere</li>
    <li id="doSomething">Do something</li>
    <li id="sayHi">Say hi</li>
</ul>

JS代碼:

var list = document.getElementById('myLinks');
EventUtil.addHandler(list,'click',function(event){
    event = EventUtil.getEvent(event);
    var target = EventUtil.getTarget(event);
    switch(target.id){
        case "goSomewhere":
            document.title = "I changed the document's title";
            break;
        case "doSomething":
            window.location.href = "https://www.baidu.com";
            break;
        case "sayHi":
            alert('hi');
            break;
    }
})

 移除事件處理程序

html:

<div id="myDiv">
    <input type="button" value="click me" id="myBtn"/>
</div>

JS:

var btn = document.getElementById('myBtn');
btn.onclick = function(){
    //先執行某些操做
    btn.onclick = null;
    document.getElementById('myDiv').innerHTML = 'Processing...';
}

咱們在設置<div>的innerHTML屬性以前,先移除了按鈕的事件處理程序。這樣就確保了內存能夠被再次利用。

注意,在事件處理程序中刪除按鈕也能阻止事件冒泡。目標元素在文檔中是事件冒泡的前提。

模擬事件

DOM中的事件模擬

能夠在document對象上使用createEvent()方法建立event對象。這個方法接收一個參數,表示要建立的事件類型的字符串。在DOM2級中,全部這些字符串都使用英文複數形式,而在DOM3級中都變成了單數。這個字符串能夠是下列字符串之一:

  • UIEvents:通常化的UI事件。鼠標事件和鍵盤事件都繼承自UI事件。DOM3級是UIEvent。
  • MouseEvents:通常化的鼠標事件。DOM3級是MouseEvent。
  • MutationEvents:通常化的DOM變更事件。DOM3級中是MutationEvent。
  • HTMLEvents:通常化的HTML事件。沒有對應的DOM3級事件(HTML事件被分散到了其餘類別中)。

在建立了event對象後,還須要使用與事件有關的信息對其進行初始化。每種類型的event對象都有一個特殊的方法,爲它傳入適當的數據就能夠初始化該event對象。不一樣類型的方法的名字也不相同,這個取決於createEvent()中使用的參數。

事件模擬的最後一步就是觸發事件,使用dispatchEvent()方法。調用dispatchEvent()方法須要傳入一個參數,即表示要觸發事件的event對象。

模擬鼠標事件

爲createEvent()方法傳入「MouseEvents」,返回的對象有一個名爲initMouseEvent()方法,用於指定與該鼠標事件有關的信息。這個方法接收15個參數,分別與鼠標事件中的每一個典型的屬性一一對應,這些參數以下:

  • type(字符串):表示要觸發的事件類型,例如「click」。
  • bubbles(布爾值):表示事件是否應該冒泡。爲精確的模擬鼠標事件,應該把這個參數設置爲true。
  • cancelable(布爾值):表示事件是否能夠取消。爲精確的模擬鼠標事件,應該把這個參數設置爲true。
  • view:與事件關聯的視圖。這個參數幾乎老是要設置爲document.defaultView。
  • detail(整數):與事件有關的詳細信息。這個值通常只有事件處理程序使用,但一般都設置爲0。
  • screenX(整數):事件相對於屏幕的X座標。
  • screenY(整數):事件相對於屏幕的Y座標。
  • clientX(整數):事件相對於視口的X座標。
  • clientY(整數):事件相對於視口的Y座標。
  • ctrlKey(布爾值):表示是否按下的Ctrl鍵。默認值爲false。
  • altKey(布爾值):表示是否按下的Alt鍵。默認值爲false。
  • shiftKey(布爾值):表示是否按下的Shift鍵。默認值爲false。
  • metaKey(布爾值):表示是否按下的Meta鍵。默認值爲false。
  • button(整數):表示按下了哪個鼠標鍵。默認值爲0。
  • relatedTarget(對象):表示與事件相關的對象,這個參數只有在模擬mouseover或mouseout時使用。

默認對按鈕的單擊事件:

var btn = document.getElementById('myBtn');
//建立事件對象
var event = document.createEvent('MouseEvents');
//初始化對象
event.initMouseEvent('click',true,true,document.defaultView,0,0,0,0,0,false,false,false,false,0,null);
//觸發事件
btn.dispatchEvent(event);

2.模擬鍵盤事件

DOM3級規定,調用createEvent()並傳入「KeyboardEvent」就能夠建立一個鍵盤事件。返回的事件對象包含一個initKeyboardEvent()方法,這個方法接收以下參數:

  • type(字符串):表示要觸發的事件類型,例如「click」。
  • bubbles(布爾值):表示事件是否應該冒泡。爲精確的模擬鼠標事件,應該把這個參數設置爲true。
  • cancelable(布爾值):表示事件是否能夠取消。爲精確的模擬鼠標事件,應該把這個參數設置爲true。
  • view:與事件關聯的視圖。這個參數幾乎老是要設置爲document.defaultView。
  • key(布爾值):表示按下的鍵的鍵碼。
  • location(整數):表示按下的哪裏的鍵。0表示默認的主鍵盤,1表示左,2表示右,3表示數字鍵盤,4表示移動設備(即虛擬鍵盤),5表示手柄。
  • modifiers(字符串):空格分隔的修改鍵列表,如「Shift」。
  • repeat(整數):在一行中按了這個鍵多少次。

因爲DOM3級不提倡使用keypress事件,所以只能利用這種技術來模擬keydown和keyup事件。

var textbox = document.getElementById('myTextbox'),
    event;
//以DOM3級方式建立事件對象
if(document.implementation.hasFeature("KeyboardEvents","3.0")){
    event = document.createEvent('KeyboardEvent');
    //初始化對象
    event.initKeyboardEvent('keydown',true,true,document.defaultView,'a',0,'shift',0);
}

//觸發事件
textbox.dispatchEvent(event);

通用事件模擬:

var textbox = document.getElementById('myTextbox');
var event = document.createEvent('Events');

//初始化對象
event.initEvent('keydown',true,true);
event.view = document.defaultView;
event.altKey = false;
event.ctrlKey = false;
event.shiftKey = false;
event.metaKey = false;
event.keyCode = 65;
event.charCode = 65;

//觸發事件
textbox.dispatchEvent(event);

3.模擬其餘事件

當須要模擬變更事件時,可使用createEvent(‘MutationEvents’)建立一個包含initMutationEvent()方法的變更事件對象。這個方法接收的參數包括:type、bubbles、cancelable、relatedNode、preValue、newValue、atrrName和attrChange。以下例子:

var event = document.createEvent('MutationEvents');
//初始化對象
event.initMutationEvent('DOMNodeInserted',true,fase,someNode,"","","",0);
//觸發事件
target.dispatchEvent(event);

模擬HTML事件:

var event = document.createEvent('HTMLEvents');
//初始化對象
event.initEvent('focus',true,true);
//觸發事件
target.dispatchEvent(event);

4.自定義DOM事件

DOM3級定義了「自定義事件」。自定義事件不是由DOM原生觸發的,它的目的是讓開發人員建立本身的事件。建立自定義事件,調用createEvent('CustomEvent'),返回的對象有一個名爲initCustomEvent()方法,接收4個參數:

  • type(字符串):表示要觸發的事件類型。
  • bubbles(布爾值):表示事件是否應該冒泡。
  • cancelable(布爾值):表示事件是否能夠取消。
  • detail(對象):任意值,保存在event對象的detail屬性中。
var div = document.getElementById('myDiv'),
    event;
    
EventUtil.addHandler(div,'myevent',function(event){
    console.log("DIV:" + event.detail);  //DIV:Hello world!
})

EventUtil.addHandler(document,'myevent',function(event){
    console.log("DOCUMENT:" + event.detail); //DOCUMENT:Hello world!
})

if(document.implementation.hasFeature("CustomEvents","3.0")){
    event = document.createEvent('CustomEvent');
    event.initCustomEvent("myevent",true,false,"Hello world!");
    div.dispatchEvent(event);
}

支持自定義DOM事件的瀏覽器有IE9+和Firefox6+。

IE中的事件模擬

在IE8及以前的版本中模擬事件跟DOM中的模擬事件的思路相似。只是每一個步驟採用了不同的方式。

調用document.createEventObject()方法能夠在IE中建立event對象,這個方法不接受參數,結果會返回一個通用的event對象。而後,手工給這個對象添加必要的信息。最後一步在目標上調用fireEvent()方法,這個方法接收兩個參數:事件處理程序的名稱和event對象。在調用fireEvent()方法時,會自動爲event對象添加srcElementtype屬性。

下面例子模擬了在一個按鈕上觸發click事件的過程:

var btn = document.getElementById('myBtn');
//建立對象
var event = document.createEventObject();

//初始化事件對象
event.screenX = 100;
event.screenY = 0;
event.clientX = 0;
event.clientY = 0;
event.altKey = false;
event.ctrlKey = false;
event.shiftKey = false;
event.button = 0;

btn.fireEvent('onclick',event);

注:這裏能夠爲對象添加任意屬性,不會有任何限制。

 附上上面完整的EventUtil對象:

var EventUtil = {
    
    //事件處理程序
    addHandler:function(element,type,handler){
        if(element.addEventListener){
            element.addEventListener(type,handler,false);
        }else if(element.attachEvent){
            element.attachEvent("on" + type,handler);
        }else{
            element["on" + type] = handler;
        }
    },
    
    //獲得event對象
    getEvent:function(event){
        return event ? event : window.event;
    },
    
    //獲得事件目標
    getTarget:function(event){
        return event.target || event.srcElement;
    },
    
    //獲得相關元素信息
    getRelatedTarget:function(event){
        if(event.relatedTarget){
            return event.relatedTarget;
        }
        else if(event.toElement){
            return event.toElement;
        }
        else if(event.fromElement){
            return event.fromElement;
        }else{
            return null;
        }
    },
    
    //取消默認行爲
    preventDefault:function(event){
        if(event.preventDefault){
            event.preventDefault();
        }else{
            event.returnValue = false;
        }
    },
    
    //獲得button屬性
    getButton:function(event){
        if(document.implementation.hasFeature("MouseEvents","2.0")){
            return event.button;
        }else{
            switch(event.button){
                case 0:
                case 1:
                case 3:
                case 5:
                case 7:
                    return 0;
                case 2:
                case 6:
                    return 2;
                case 4:
                    return 1;
            }
        }
    },
    
    //取得鼠標滾輪增量值(delta)
    getWheelDelta:function(event){
        if(event.wheelDelta){
            return event.wheelDelta;
        }else{
            return -event.detail * 40;
        }
    },
    
    //字符編碼
    getCharCode:function(event){
        if(typeof event.charCode == 'number'){
            return event.charCode;
        }else{
            return event.keyCode;
        }
    },
    
    //移除事件處理程序
    removeHandler:function(element,type,handler){
        if(element.removeEventListener){
            element.removeEventListener(type,handler,false);
        }else if(element.detachEvent){
            element.detachEvent("on" + type,handler);
        }else{
            element["on" + type] = null;
        }
    },
    
    //阻止事件捕獲或冒泡
    stopPropagation:function(event){
        if(event.stopPropagation){
            event.stopPropagation();
        }else{
            event.cancelBubble = true;
        }
    }
};
相關文章
相關標籤/搜索