W3Cschool
恭喜您成為首批注冊用戶
獲得88經(jīng)驗(yàn)值獎勵
我們不僅可以分配事件處理程序,還可以從 JavaScript 生成事件。
自定義事件可用于創(chuàng)建“圖形組件”。例如,我們自己的基于 JavaScript 的菜單的根元素可能會觸發(fā) open
(打開菜單),select
(有一項被選中)等事件來告訴菜單發(fā)生了什么。另一個代碼可能會監(jiān)聽事件,并觀察菜單發(fā)生了什么。
我們不僅可以生成出于自身目的而創(chuàng)建的全新事件,還可以生成例如 click
和 mousedown
等內(nèi)建事件。這可能會有助于自動化測試。
內(nèi)建事件類形成一個層次結(jié)構(gòu)(hierarchy),類似于 DOM 元素類。根是內(nèi)建的 Event 類。
我們可以像這樣創(chuàng)建 Event
對象:
let event = new Event(type[, options]);
參數(shù):
"click"
? 的字符串,或者我們自己的像這樣 ?"my-event"
? 的參數(shù)。bubbles: true/false
? —— 如果為 ?true
?,那么事件會冒泡。cancelable: true/false
? —— 如果為 ?true
?,那么“默認(rèn)行為”就會被阻止。稍后我們會看到對于自定義事件,它意味著什么。默認(rèn)情況下,以上兩者都為 false:?{bubbles: false, cancelable: false}
?。
事件對象被創(chuàng)建后,我們應(yīng)該使用 elem.dispatchEvent(event)
調(diào)用在元素上“運(yùn)行”它。
然后,處理程序會對它做出反應(yīng),就好像它是一個常規(guī)的瀏覽器事件一樣。如果事件是用 bubbles
標(biāo)志創(chuàng)建的,那么它會冒泡。
在下面這個示例中,click
事件是用 JavaScript 初始化創(chuàng)建的。處理程序工作方式和點(diǎn)擊按鈕的方式相同:
<button id="elem" onclick="alert('Click!');">Autoclick</button>
<script>
let event = new Event("click");
elem.dispatchEvent(event);
</script>
event.isTrusted
有一種方法可以區(qū)分“真實(shí)”用戶事件和通過腳本生成的事件。
對于來自真實(shí)用戶操作的事件,
event.isTrusted
屬性為true
,對于腳本生成的事件,event.isTrusted
屬性為false
。
我們可以創(chuàng)建一個名為 "hello"
的冒泡事件,并在 document
上捕獲它。
我們需要做的就是將 bubbles
設(shè)置為 true
:
<h1 id="elem">Hello from the script!</h1>
<script>
// 在 document 上捕獲...
document.addEventListener("hello", function(event) { // (1)
alert("Hello from " + event.target.tagName); // Hello from H1
});
// ...在 elem 上 dispatch!
let event = new Event("hello", {bubbles: true}); // (2)
elem.dispatchEvent(event);
// 在 document 上的處理程序?qū)⒈患せ?,并顯示消息。
</script>
注意:
addEventListener
?,因?yàn)?nbsp;?on<event>
? 僅存在于內(nèi)建事件中,?document.onhello
? 則無法運(yùn)行。bubbles:true
?,否則事件不會向上冒泡。內(nèi)建事件(click
)和自定義事件(hello
)的冒泡機(jī)制相同。自定義事件也有捕獲階段和冒泡階段。
這是一個摘自于 UI 事件規(guī)范 的一個簡短的 UI 事件類列表:
UIEvent
?FocusEvent
?MouseEvent
?WheelEvent
?KeyboardEvent
?如果我們想要創(chuàng)建這樣的事件,我們應(yīng)該使用它們而不是 new Event
。例如,new MouseEvent("click")
。
正確的構(gòu)造器允許為該類型的事件指定標(biāo)準(zhǔn)屬性。
就像鼠標(biāo)事件的 clientX/clientY
一樣:
let event = new MouseEvent("click", {
bubbles: true,
cancelable: true,
clientX: 100,
clientY: 100
});
alert(event.clientX); // 100
請注意:通用的 Event
構(gòu)造器不允許這樣做。
讓我們試試:
let event = new Event("click", {
bubbles: true, // 構(gòu)造器 Event 中只有 bubbles 和 cancelable 可以工作
cancelable: true,
clientX: 100,
clientY: 100
});
alert(event.clientX); // undefined,未知的屬性被忽略了!
從技術(shù)上講,我們可以通過在創(chuàng)建后直接分配 event.clientX=100
來解決這個問題。所以,這是一個方便和遵守規(guī)則的問題。瀏覽器生成的事件始終具有正確的類型。
規(guī)范中提供了不同 UI 事件的屬性的完整列表,例如 MouseEvent。
對于我們自己的全新事件類型,例如 "hello"
,我們應(yīng)該使用 new CustomEvent
。從技術(shù)上講,CustomEvent 和 Event
一樣。除了一點(diǎn)不同。
在第二個參數(shù)(對象)中,我們可以為我們想要與事件一起傳遞的任何自定義信息添加一個附加的屬性 detail
。
例如:
<h1 id="elem">Hello for John!</h1>
<script>
// 事件附帶給處理程序的其他詳細(xì)信息
elem.addEventListener("hello", function(event) {
alert(event.detail.name);
});
elem.dispatchEvent(new CustomEvent("hello", {
detail: { name: "John" }
}));
</script>
detail
屬性可以有任何數(shù)據(jù)。從技術(shù)上講,我們可以不用,因?yàn)槲覀兛梢栽趧?chuàng)建后將任何屬性分配給常規(guī)的 new Event
對象中。但是 CustomEvent
提供了特殊的 detail
字段,以避免與其他事件屬性的沖突。
此外,事件類描述了它是“什么類型的事件”,如果事件是自定義的,那么我們應(yīng)該使用 CustomEvent
來明確它是什么。
許多瀏覽器事件都有“默認(rèn)行為”,例如,導(dǎo)航到鏈接,開始一個選擇,等。
對于新的,自定義的事件,絕對沒有默認(rèn)的瀏覽器行為,但是分派(dispatch)此類事件的代碼可能有自己的計劃,觸發(fā)該事件之后應(yīng)該做什么。
通過調(diào)用 event.preventDefault()
,事件處理程序可以發(fā)出一個信號,指出這些行為應(yīng)該被取消。
在這種情況下,elem.dispatchEvent(event)
的調(diào)用會返回 false
。那么分派(dispatch)該事件的代碼就會知道不應(yīng)該再繼續(xù)。
讓我們看一個實(shí)際的例子 —— 一只隱藏的兔子(可以是關(guān)閉菜單或者其他)。
在下面,你可以看到一個在其上分派了 "hide"
事件的 #rabbit
和 hide()
函數(shù),以使所有感興趣的各方面都知道這只兔子要隱藏起來。
任何處理程序都可以使用 rabbit.addEventListener('hide',...)
來監(jiān)聽該事件,并在需要時使用 event.preventDefault()
來取消該行為。然后兔子就不會藏起來了:
<pre id="rabbit">
|\ /|
\|_|/
/. .\
=\_Y_/=
{>o<}
</pre>
<button onclick="hide()">Hide()</button>
<script>
function hide() {
let event = new CustomEvent("hide", {
cancelable: true // 沒有這個標(biāo)志,preventDefault 將不起作用
});
if (!rabbit.dispatchEvent(event)) {
alert('The action was prevented by a handler');
} else {
rabbit.hidden = true;
}
}
rabbit.addEventListener('hide', function(event) {
if (confirm("Call preventDefault?")) {
event.preventDefault();
}
});
</script>
請注意:該事件必須具有 cancelable: true
標(biāo)志,否則 event.preventDefault()
調(diào)用將會被忽略。
通常事件是在隊列中處理的。也就是說:如果瀏覽器正在處理 onclick
,這時發(fā)生了一個新的事件,例如鼠標(biāo)移動了,那么它的處理程序會被排入隊列,相應(yīng)的 mousemove
處理程序?qū)⒃?nbsp;onclick
事件處理完成后被調(diào)用。
值得注意的例外情況就是,一個事件是在另一個事件中發(fā)起的。例如使用 dispatchEvent
。這類事件將會被立即處理,即在新的事件處理程序被調(diào)用之后,恢復(fù)到當(dāng)前的事件處理程序。
例如,在下面的代碼中,menu-open
事件是在 onclick
事件執(zhí)行過程中被調(diào)用的。
它會被立即執(zhí)行,而不必等待 onclick
處理程序結(jié)束:
<button id="menu">Menu (click me)</button>
<script>
menu.onclick = function() {
alert(1);
menu.dispatchEvent(new CustomEvent("menu-open", {
bubbles: true
}));
alert(2);
};
// 在 1 和 2 之間觸發(fā)
document.addEventListener('menu-open', () => alert('nested'));
</script>
輸出順序?yàn)椋? → nested → 2。
請注意,嵌套事件 menu-open
會在 document
上被捕獲。嵌套事件的傳播(propagation)和處理先被完成,然后處理過程才會返回到外部代碼(onclick
)。
這不只是與 dispatchEvent
有關(guān),還有其他情況。如果一個事件處理程序調(diào)用了觸發(fā)其他事件的方法 —— 它們同樣也會被以嵌套的方式同步處理。
不過有時候,這并不是我們期望的結(jié)果。我們想讓 onclick
不受 menu-open
或者其它嵌套事件的影響,優(yōu)先被處理完畢。
那么,我們就可以將 dispatchEvent
(或另一個觸發(fā)事件的調(diào)用)放在 onclick
末尾,或者最好將其包裝到零延遲的 setTimeout
中:
<button id="menu">Menu (click me)</button>
<script>
menu.onclick = function() {
alert(1);
setTimeout(() => menu.dispatchEvent(new CustomEvent("menu-open", {
bubbles: true
})));
alert(2);
};
document.addEventListener('menu-open', () => alert('nested'));
</script>
現(xiàn)在,dispatchEvent
在當(dāng)前代碼執(zhí)行完成之后異步運(yùn)行,包括 menu.onclick
,因此,事件處理程序是完全獨(dú)立的。
輸出順序變成:1 → 2 → nested。
要從代碼生成一個事件,我們首先需要創(chuàng)建一個事件對象。
通用的 Event(name, options)
構(gòu)造器接受任意事件名稱和具有兩個屬性的 options
對象:
bubbles: true
?。event.preventDefault()
? 應(yīng)該有效,則 ?cancelable: true
?。其他像 MouseEvent
和 KeyboardEvent
這樣的原生事件的構(gòu)造器,都接受特定于該事件類型的屬性。例如,鼠標(biāo)事件的 clientX
。
對于自定義事件,我們應(yīng)該使用 CustomEvent
構(gòu)造器。它有一個名為 detail
的附加選項,我們應(yīng)該將事件特定的數(shù)據(jù)分配給它。然后,所有處理程序可以以 event.detail
的形式來訪問它。
盡管技術(shù)上可以生成像 click
或 keydown
這樣的瀏覽器事件,但我們還是應(yīng)謹(jǐn)慎使用它們。
我們不應(yīng)該生成瀏覽器事件,因?yàn)檫@是運(yùn)行處理程序的一種怪異(hacky)方式。大多數(shù)時候,這都是糟糕的架構(gòu)。
可以生成原生事件:
使用我們自己的名稱的自定義事件通常是出于架構(gòu)的目的而創(chuàng)建的,以指示發(fā)生在菜單(menu),滑塊(slider),輪播(carousel)等內(nèi)部發(fā)生了什么。
Copyright©2021 w3cschool編程獅|閩ICP備15016281號-3|閩公網(wǎng)安備35020302033924號
違法和不良信息舉報電話:173-0602-2364|舉報郵箱:jubao@eeedong.com
掃描二維碼
下載編程獅App
編程獅公眾號
聯(lián)系方式:
更多建議: