1. 程式人生 > >模擬事件【JavaScript高級程序設計第三版】

模擬事件【JavaScript高級程序設計第三版】

lpad 同時 布爾 href table 添加屬性 hang 精確 init

事件,就是網頁中某個特別值得關註的瞬間。事件經常由用戶操作或通過其他瀏覽器功能來觸發。
但很少有人知道,也可以使用JavaScript 在任意時刻來觸發特定的事件,而此時的事件就如同瀏覽器創建的事件一樣。也就是說,這些事件該冒泡還會冒泡,而且照樣能夠導致瀏覽器執行已經指定的處理它們的事件處理程序。在測試Web 應用程序,模擬觸發事件是一種極其有用的技術。DOM2 級規範為此規定了模擬特定事件的方式,IE9、Opera、Firefox、Chrome 和Safari 都支持這種方式。IE 有它自己模擬事件的方式。

13.6.1 DOM中的事件模擬

可以在document 對象上使用createEvent()方法創建event 對象。這個方法接收一個參數,即表示要創建的事件類型的字符串。在DOM2 級中,所有這些字符串都使用英文復數形式,而在DOM3級中都變成了單數。這個字符串可以是下列幾字符串之一。

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

要註意的是,“DOM2 級事件”並沒有專門規定鍵盤事件,後來的“DOM3 級事件”中才正式將其作為一種事件給出規定。IE9 是目前唯一支持DOM3 級鍵盤事件的瀏覽器。不過,在其他瀏覽器中,在現有方法的基礎上,可以通過幾種方式來模擬鍵盤事件。
在創建了event 對象之後,還需要使用與事件有關的信息對其進行初始化。每種類型的event 對象都有一個特殊的方法,為它傳入適當的數據就可以初始化該event 對象。不同類型的這個方法的名字也不相同,具體要取決於createEvent()中使用的參數。
模擬事件的最後一步就是觸發事件。這一步需要使用dispatchEvent()方法,所有支持事件的DOM 節點都支持這個方法。調用dispatchEvent()方法時,需要傳入一個參數,即表示要觸發事件的event 對象。觸發事件之後,該事件就躋身“官方事件”之列了,因而能夠照樣冒泡並引發相應事件處理程序的執行。

1. 模擬鼠標事件

創建新的鼠標事件對象並為其指定必要的信息,就可以模擬鼠標事件。創建鼠標事件對象的方法是為createEvent()傳入字符串"MouseEvents"。返回的對象有一個名為initMouseEvent()方法,用於指定與該鼠標事件有關的信息。這個方法接收15 個參數,分別與鼠標事件中每個典型的屬性一一對應;這些參數的含義如下。

  • type(字符串):表示要觸發的事件類型,例如"click"。
  • bubbles(布爾值):表示事件是否應該冒泡。為精確地模擬鼠標事件,應該把這個參數設置為true。
  • cancelable(布爾值):表示事件是否可以取消。為精確地模擬鼠標事件,應該把這個參數設置為true。
  • view(AbstractView):與事件關聯的視圖。這個參數幾乎總是要設置為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時使用。

顯而易見,initMouseEvent()方法的這些參數是與鼠標事件的event 對象所包含的屬性一一對應的。其中,前4 個參數對正確地激發事件至關重要,因為瀏覽器要用到這些參數;而剩下的所有參數只有在事件處理程序中才會用到。當把event 對象傳給dispatchEvent()方法時,這個對象的target屬性會自動設置。下面,我們就通過一個例子來了解如何模擬對按鈕的單擊事件。

1 2 3 4 5 6 7 8 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);

運行一下
在兼容DOM的瀏覽器中,也可以通過相同的方式來模擬其他鼠標事件(例如dblclick)。

2. 模擬鍵盤事件

前面曾經提到過,“DOM2 級事件”中沒有就鍵盤事件作出規定,因此模擬鍵盤事件並沒有現成的思路可循。“DOM2 級事件”的草案中本來包含了鍵盤事件,但在定稿之前又被刪除了;Firefox 根據其草案實現了鍵盤事件。需要提請大家註意的是,“DOM3 級事件”中的鍵盤事件與曾包含在“DOM2 級事件”草案中的鍵盤事件有很大區別。
DOM3 級規定,調用createEvent()並傳入"KeyboardEvent"就可以創建一個鍵盤事件。返回的事件對象會包含一個initKeyEvent()方法,這個方法接收下列參數。

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

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

1 2 3 4 5 6 7 8 9 10 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);

運行一下
這個例子模擬的是按住Shift 的同時又按下A 鍵。在使用document.createEvent
("KeyboardEvent")之前,應該先檢測瀏覽器是否支持DOM3 級事件;其他瀏覽器返回一個非標準的KeyboardEvent 對象。
在Firefox 中,調用createEvent()並傳入"KeyEvents"就可以創建一個鍵盤事件。返回的事件對象會包含一個initKeyEvent()方法,這個方法接受下列10 個參數。

  • type(字符串):表示要觸發的事件類型,如"keydown"。
  • bubbles(布爾值):表示事件是否應該冒泡。為精確模擬鼠標事件,應該設置為true。
  • cancelable(布爾值):表示事件是否可以取消。為精確模擬鼠標事件,應該設置為true。
  • view(AbstractView):與事件關聯的視圖。這個參數幾乎總是要設置為document.default-View。
  • ctrlKey(布爾值):表示是否按下了Ctrl 鍵。默認值為false。
  • altKey(布爾值):表示是否按下了Alt 鍵。默認值為false。
  • shiftKey(布爾值):表示是否按下了Shift 鍵。默認值為false。
  • metaKey(布爾值):表示是否按下了Meta 鍵。默認值為false。
  • keyCode(整數):被按下或釋放的鍵的鍵碼。這個參數對keydown 和keyup 事件有用,默認值為0。
  • charCode(整數):通過按鍵生成的字符的ASCII 編碼。這個參數對keypress 事件有用,默認值為0。

將創建的event 對象傳入到dispatchEvent()方法就可以觸發鍵盤事件,如下面的例子所示。

1 2 3 4 5 6 7 8 9 //只適用於Firefox var textbox = document.getElementById("myTextbox") //創建事件對象 var event = document.createEvent("KeyEvents"); //初始化事件對象 event.initKeyEvent("keypress", true, true, document.defaultView, false, false, false, false, 65, 65); //觸發事件 textbox.dispatchEvent(event);

運行一下
在Firefox 中運行上面的代碼,會在指定的文本框中輸入字母A。同樣,也可以依此模擬keyup 和keydown 事件。
在其他瀏覽器中,則需要創建一個通用的事件,然後再向事件對象中添加鍵盤事件特有的信息。
例如:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 var textbox = document.getElementById("myTextbox"); //創建事件對象 var event = document.createEvent("Events"); //初始化事件對象 event.initEvent(type, bubbles, cancelable); 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);

以上代碼首先創建了一個通用事件,然後調用initEvent()對其進行初始化,最後又為其添加了鍵盤事件的具體信息。在此必須要使用通用事件,而不能使用UI 事件,因為UI 事件不允許向event對象中再添加新屬性(Safari 除外)。像這樣模擬事件雖然會觸發鍵盤事件,但卻不會向文本框中寫入文本,這是由於無法精確模擬鍵盤事件所造成的。

3. 模擬其他事件

雖然鼠標事件和鍵盤事件是在瀏覽器中最經常模擬的事件,但有時候同樣需要模擬變動事件和HTML 事件。要模擬變動事件, 可以使用createEvent("MutationEvents") 創建一個包含initMutationEvent() 方法的變動事件對象。這個方法接受的參數包括: type 、bubbles 、cancelable、relatedNode、preValue、newValue、attrName 和attrChange。下面來看一個模擬變動事件的例子。

1 2 3 var event = document.createEvent("MutationEvents"); event.initMutationEvent("DOMNodeInserted", true, false, someNode, "","","",0); targ et.dispatchEvent(event);

以上代碼模擬了DOMNodeInserted 事件。其他變動事件也都可以照這個樣子來模擬,只要改一改參數就可以了。
要模擬HTML 事件,同樣需要先創建一個event 對象——通過createEvent("HTMLEvents"),然後再使用這個對象的initEvent()方法來初始化它即可,如下面的例子所示。

1 2 3 var event = document.createEvent("HTMLEvents"); event.initEvent("focus", true, false); targ et.dispatchEvent(event);

這個例子展示了如何在給定目標上模擬focus 事件。模擬其他HTML 事件的方法也是這樣。

瀏覽器中很少使用變動事件和HTML 事件,因為使用它們會受到一些限制。

4. 自定義DOM 事件

DOM3 級還定義了“自定義事件”。自定義事件不是由DOM 原生觸發的,它的目的是讓開發人員創建自己的事件。要創建新的自定義事件,可以調用createEvent("CustomEvent")。返回的對象有一個名為initCustomEvent()的方法,接收如下4 個參數。

  • type(字符串):觸發的事件類型,例如"keydown"。
  • bubbles(布爾值):表示事件是否應該冒泡。
  • cancelable(布爾值):表示事件是否可以取消。
  • detail(對象):任意值,保存在event 對象的detail 屬性中。

可以像分派其他事件一樣在DOM 中分派創建的自定義事件對象。例如:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 var div = document.getElementById("myDiv"), event; EventUtil.addHandler(div, "myevent", function(event) { alert("DIV: " + event.detail); }); EventUtil.addHandler(document, "myevent", function(event) { alert("DOCUMENT: " + event.detail); }); if (document.implementation.hasFeature("CustomEvents", "3.0")) { event = document.createEvent("CustomEvent"); event.initCustomEvent("myevent", true, false, "Hello world!"); div.dispatchEvent(event); }

運行一下
這個例子創建了一個冒泡事件"myevent"。而event.detail 的值被設置成了一個簡單的字符串,然後在<div>元素和document 上偵聽這個事件。因為initCustomEvent()方法已經指定這個事件應該冒泡,所以瀏覽器會負責將事件向上冒泡到document。
支持自定義DOM事件的瀏覽器有IE9+和Firefox 6+。

13.6.2 IE中的事件模擬

在IE8 及之前版本中模擬事件與在DOM中模擬事件的思路相似:先創建event 對象,然後為其指定相應的信息,然後再使用該對象來觸發事件。當然,IE 在實現每個步驟時都采用了不一樣的方式。
調用document.createEventObject()方法可以在IE 中創建event 對象。但與DOM方式不同的是,這個方法不接受參數,結果會返回一個通用的event 對象。然後,你必須手工為這個對象添加所有必要的信息(沒有方法來輔助完成這一步驟)。最後一步就是在目標上調用fireEvent()方法,這個方法接受兩個參數:事件處理程序的名稱和event 對象。在調用fireEvent()方法時,會自動為event 對象添加srcElement 和type 屬性;其他屬性則都是必須通過手工添加的。換句話說,模擬任何IE 支持的事件都采用相同的模式。例如,下面的代碼模擬了在一個按鈕上觸發click 事件過程。

1 2 3 4 5 6 7 8 9 10 11 12 13 14 var btn = document.getElementById("myBtn"); //創建事件對象 var event = document.createEventObject(); //初始化事件對象 event.screenX = 100; event.screenY = 0; event.clientX = 0; event.clientY = 0; event.ctrlKey = false; event.altKey = false; event.shiftKey = false; event.button = 0; //觸發事件 btn.fireEvent("onclick", event);

運行一下
這個例子先創建了一個event 對象,然後又用一些信息對其進行了初始化。註意,這裏可以為對象隨意添加屬性,不會有任何限制——即使添加的屬性IE8 及更早版本並不支持也無所謂。在此添加的屬性對事件沒有什麽影響,因為只有事件處理程序才會用到它們。
采用相同的模式也可以模擬觸發keypress 事件,如下面的例子所示。

1 2 3 4 5 6 7 8 9 10 var textbox = document.getElementById("myTextbox"); //創建事件對象 var event = document.createEventObject(); //初始化事件對象 event.altKey = false; event.ctrlKey = false; event.shiftKey = false; event.keyCode = 65; //觸發事件 textbox.fireEvent("onkeypress", event);

運行一下
由於鼠標事件、鍵盤事件以及其他事件的event 對象並沒有什麽不同,所以可以使用通用對象來觸發任何類型的事件。不過,正如在DOM中模擬鍵盤事件一樣,運行這個例子也不會因模擬了keypress而在文本框中看到任何字符,即使觸發了事件處理程序也沒有用。

模擬事件【JavaScript高級程序設計第三版】