W3Cschool
恭喜您成為首批注冊用戶
獲得88經(jīng)驗(yàn)值獎(jiǎng)勵(lì)
許多事件會(huì)自動(dòng)觸發(fā)瀏覽器執(zhí)行某些行為。
例如:
如果我們使用 JavaScript 處理一個(gè)事件,那么我們通常不希望發(fā)生相應(yīng)的瀏覽器行為。而是想要實(shí)現(xiàn)其他行為進(jìn)行替代。
有兩種方式來告訴瀏覽器我們不希望它執(zhí)行默認(rèn)行為:
event
? 對象。有一個(gè) ?event.preventDefault()
? 方法。on<event>
?(而不是 ?addEventListener
?)分配的,那返回 ?false
? 也同樣有效。在下面這個(gè)示例中,點(diǎn)擊鏈接不會(huì)觸發(fā)導(dǎo)航(navigation),瀏覽器不會(huì)執(zhí)行任何操作:
<a href="/" onclick="return false">Click here</a>
or
<a href="/" onclick="event.preventDefault()">here</a>
在下一個(gè)示例中,我們將使用此技術(shù)來創(chuàng)建 JavaScript 驅(qū)動(dòng)的菜單。
從處理程序返回 ?
false
? 是一個(gè)例外事件處理程序返回的值通常會(huì)被忽略。
唯一的例外是從使用
on<event>
分配的處理程序中返回的return false
。
在所有其他情況下,
return
值都會(huì)被忽略。并且,返回true
沒有意義。
考慮一個(gè)網(wǎng)站菜單,如下所示:
<ul id="menu" class="menu">
<li><a href="/html">HTML</a></li>
<li><a href="/javascript">JavaScript</a></li>
<li><a href="/css">CSS</a></li>
</ul>
下面經(jīng)過 CSS 渲染的外觀:
菜單項(xiàng)是通過使用 HTML 鏈接 <a>
實(shí)現(xiàn)的,而不是使用按鈕 <button>
。這樣做有幾個(gè)原因,例如:
<button>
? 或 ?<span>
?,這個(gè)效果就無法實(shí)現(xiàn)。<a href="...">
? 鏈接。所以我們在標(biāo)記(markup)中使用了 <a>
。但通常我們打算處理 JavaScript 中的點(diǎn)擊。因此,我們應(yīng)該阻止瀏覽器默認(rèn)行為。
像這樣:
menu.onclick = function(event) {
if (event.target.nodeName != 'A') return;
let href = event.target.getAttribute('href');
alert( href ); // ...可以從服務(wù)器加載,UI 生成等
return false; // 阻止瀏覽器行為(不前往訪問 URL)
};
如果我們省略 return false
,那么在我們的代碼執(zhí)行完畢后,瀏覽器將執(zhí)行它的“默認(rèn)行為” —— 導(dǎo)航至在 href
中的 URL。
順便說一句,這里使用事件委托會(huì)使我們的菜單更靈活。我們可以添加嵌套列表并使用 CSS 對其進(jìn)行樣式設(shè)置來實(shí)現(xiàn) “slide down” 的效果。
后續(xù)事件
某些事件會(huì)相互轉(zhuǎn)化。如果我們阻止了第一個(gè)事件,那就沒有第二個(gè)事件了。
例如,在
<input>
字段上的mousedown
會(huì)導(dǎo)致在其中獲得焦點(diǎn),以及focus
事件。如果我們阻止mousedown
事件,在這就沒有焦點(diǎn)了。
這是因?yàn)闉g覽器行為在
mousedown
上被取消。如果我們用另一種方式進(jìn)行輸入,則仍然可以進(jìn)行聚焦。例如,可以使用 ?Tab
? 鍵從第一個(gè)輸入切換到第二個(gè)輸入。但鼠標(biāo)點(diǎn)擊則不行。
addEventListener
的可選項(xiàng) passive: true
向?yàn)g覽器發(fā)出信號(hào),表明處理程序?qū)⒉粫?huì)調(diào)用 preventDefault()
。
為什么需要這樣做?
移動(dòng)設(shè)備上會(huì)發(fā)生一些事件,例如 touchmove
(當(dāng)用戶在屏幕上移動(dòng)手指時(shí)),默認(rèn)情況下會(huì)導(dǎo)致滾動(dòng),但是可以使用處理程序的 preventDefault()
來阻止?jié)L動(dòng)。
因此,當(dāng)瀏覽器檢測到此類事件時(shí),它必須首先處理所有處理程序,然后如果沒有任何地方調(diào)用 preventDefault
,則頁面可以繼續(xù)滾動(dòng)。但這可能會(huì)導(dǎo)致 UI 中不必要的延遲和“抖動(dòng)”。
passive: true
選項(xiàng)告訴瀏覽器,處理程序不會(huì)取消滾動(dòng)。然后瀏覽器立即滾動(dòng)頁面以提供最大程度的流暢體驗(yàn),并通過某種方式處理事件。
對于某些瀏覽器(Firefox,Chrome),默認(rèn)情況下,touchstart
和 touchmove
事件的 passive
為 true
。
如果默認(rèn)行為被阻止,那么 event.defaultPrevented
屬性為 true
,否則為 false
。
這兒有一個(gè)有趣的用例。
你還記得我們在 冒泡和捕獲 一章中討論過的 event.stopPropagation()
,以及為什么停止冒泡是不好的嗎?
有時(shí)我們可以使用 event.defaultPrevented
來代替,來通知其他事件處理程序,該事件已經(jīng)被處理。
我們來看一個(gè)實(shí)際的例子。
默認(rèn)情況下,瀏覽器在 contextmenu
事件(單擊鼠標(biāo)右鍵)時(shí),顯示帶有標(biāo)準(zhǔn)選項(xiàng)的上下文菜單。我們可以阻止它并顯示我們自定義的菜單,就像這樣:
<button>Right-click shows browser context menu</button>
<button oncontextmenu="alert('Draw our menu'); return false">
Right-click shows our context menu
</button>
現(xiàn)在,除了該上下文菜單外,我們還想實(shí)現(xiàn)文檔范圍的上下文菜單。
右鍵單擊時(shí),應(yīng)該顯示最近的上下文菜單:
<p>Right-click here for the document context menu</p>
<button id="elem">Right-click here for the button context menu</button>
<script>
elem.oncontextmenu = function(event) {
event.preventDefault();
alert("Button context menu");
};
document.oncontextmenu = function(event) {
event.preventDefault();
alert("Document context menu");
};
</script>
問題是,當(dāng)我們點(diǎn)擊 elem
時(shí),我們會(huì)得到兩個(gè)菜單:按鈕級(jí)和文檔級(jí)(事件冒泡)的菜單。
如何修復(fù)呢?其中一個(gè)解決方案是:“當(dāng)我們在按鈕處理程序中處理鼠標(biāo)右鍵單擊事件時(shí),我們阻止其冒泡”,使用 event.stopPropagation()
:
<p>Right-click for the document menu</p>
<button id="elem">Right-click for the button menu (fixed with event.stopPropagation)</button>
<script>
elem.oncontextmenu = function(event) {
event.preventDefault();
event.stopPropagation();
alert("Button context menu");
};
document.oncontextmenu = function(event) {
event.preventDefault();
alert("Document context menu");
};
</script>
現(xiàn)在按鈕級(jí)菜單如期工作。但是代價(jià)太大,我們拒絕了任何外部代碼對右鍵點(diǎn)擊信息的訪問,包括收集統(tǒng)計(jì)信息的計(jì)數(shù)器等。這是非常不明智的。
另一個(gè)替代方案是,檢查 document
處理程序是否阻止了瀏覽器的默認(rèn)行為?如果阻止了,那么該事件已經(jīng)得到了處理,我們無需再對此事件做出反應(yīng)。
<p>Right-click for the document menu (added a check for event.defaultPrevented)</p>
<button id="elem">Right-click for the button menu</button>
<script>
elem.oncontextmenu = function(event) {
event.preventDefault();
alert("Button context menu");
};
document.oncontextmenu = function(event) {
if (event.defaultPrevented) return;
event.preventDefault();
alert("Document context menu");
};
</script>
現(xiàn)在一切都可以正常工作了。如果我們有嵌套的元素,并且每個(gè)元素都有自己的上下文菜單,那么這也是可以運(yùn)行的。只需確保檢查每個(gè) contextmenu
處理程序中的 event.defaultPrevented
。
event.stopPropagation() 和 event.preventDefault()
正如我們所看到的,
event.stopPropagation()
和event.preventDefault()
(也被認(rèn)為是return false
)是兩個(gè)不同的東西。它們之間毫無關(guān)聯(lián)。
嵌套的上下文菜單結(jié)構(gòu)
還有其他實(shí)現(xiàn)嵌套上下文菜單的方式。其中之一是擁有一個(gè)具有
document.oncontextmenu
處理程序的全局對象,以及使我們能夠在其中存儲(chǔ)其他處理程序的方法。
該對象將捕獲任何右鍵單擊,瀏覽存儲(chǔ)的處理程序并運(yùn)行適當(dāng)?shù)奶幚沓绦颉?
但是,每段需要上下文菜單的代碼都應(yīng)該了解該對象,并使用它的幫助,而不是使用自己的
contextmenu
處理程序。
有很多默認(rèn)的瀏覽器行為:
mousedown
? —— 開始選擇(移動(dòng)鼠標(biāo)進(jìn)行選擇)。<input type="checkbox">
? 上的 ?click
? —— 選中/取消選中的 ?input
?。submit
? —— 點(diǎn)擊 ?<input type="submit">
? 或者在表單字段中按下 ?Enter
? 鍵會(huì)觸發(fā)該事件,之后瀏覽器將提交表單。keydown
? —— 按下一個(gè)按鍵會(huì)導(dǎo)致將字符添加到字段,或者觸發(fā)其他行為。contextmenu
? —— 事件發(fā)生在鼠標(biāo)右鍵單擊時(shí),觸發(fā)的行為是顯示瀏覽器上下文菜單。如果我們只想通過 JavaScript 來處理事件,那么所有默認(rèn)行為都是可以被阻止的。
想要阻止默認(rèn)行為 —— 可以使用 event.preventDefault()
或 return false
。第二個(gè)方法只適用于通過 on<event>
分配的處理程序。
addEventListener
的 passive: true
選項(xiàng)告訴瀏覽器該行為不會(huì)被阻止。這對于某些移動(dòng)端的事件(像 touchstart
和 touchmove
)很有用,用以告訴瀏覽器在滾動(dòng)之前不應(yīng)等待所有處理程序完成。
如果默認(rèn)行為被阻止,event.defaultPrevented
的值會(huì)變成 true
,否則為 false
。
保持語義,不要濫用
從技術(shù)上來說,通過阻止默認(rèn)行為并添加 JavaScript,我們可以自定義任何元素的行為。例如,我們可以使鏈接
<a>
像按鈕一樣工作,而按鈕<button>
也可以像鏈接那樣工作(重定向到另一個(gè) URL 等)。
但我們通常應(yīng)該保留 HTML 元素的語義。例如
<a>
應(yīng)該表現(xiàn)為導(dǎo)航(navigation),而不是按鈕。
除了“只是一件好事”之外,這還會(huì)使你的 HTML 具有更好的可訪問性。
另外,如果我們考慮使用帶有
<a>
的示例,那么請注意:瀏覽器允許我們在新窗口中打開此類鏈接(通過右鍵單擊它們以及其他方式)。大家都喜歡這么做。但是,如果我們使用 JavaScript 讓按鈕行為表現(xiàn)得像鏈接,甚至使用 CSS 將其樣式設(shè)置成看起來也像鏈接,即使這樣,但仍然無法在按鈕上使用特定于<a>
的瀏覽器功能。
為什么下面這段代碼中的 return false
不起作用?
<script>
function handler() {
alert( "..." );
return false;
}
</script>
<a rel="external nofollow" target="_blank" rel="external nofollow" target="_blank" rel="external nofollow" target="_blank" onclick="handler()">the browser will go to w3.org</a>
瀏覽器在點(diǎn)擊時(shí)會(huì)根據(jù) URL 進(jìn)行跳轉(zhuǎn),但這不是我們想要的。
如何修復(fù)它?
當(dāng)瀏覽器讀取諸如 onclick
之類的 on*
特性(attribute)時(shí),瀏覽器會(huì)根據(jù)其內(nèi)容創(chuàng)建對應(yīng)的處理程序。
對于 onclick="handler()"
來說,函數(shù)是:
function(event) {
handler() // onclick 的內(nèi)容
}
現(xiàn)在我們可以看到 handler()
的返回值并沒有被使用,也沒有對結(jié)果產(chǎn)生影響。
修復(fù)起來很簡單:
<script>
function handler() {
alert("...");
return false;
}
</script>
<a rel="external nofollow" target="_blank" rel="external nofollow" target="_blank" rel="external nofollow" target="_blank" onclick="return handler()">w3.org</a>
我們也可以使用 event.preventDefault()
,像這樣:
<script>
function handler(event) {
alert("...");
event.preventDefault();
}
</script>
<a rel="external nofollow" target="_blank" rel="external nofollow" target="_blank" rel="external nofollow" target="_blank" onclick="handler(event)">w3.org</a>
使所有包含 ?id="contents"
? 的元素內(nèi)的鏈接詢問用戶是否真的要離開。如果用戶不想離開,那就不離開。
細(xì)節(jié):
<a href=".."><i>...</i></a>
?。這是一個(gè)很好的使用事件委托模式的案例。
在現(xiàn)實(shí)生活中,我們可以向服務(wù)器發(fā)送一個(gè) “l(fā)ogging” 請求而不是詢問,該請求會(huì)保存關(guān)于訪問者離開位置的信息?;蛘撸覀兛梢约虞d內(nèi)容,并將其顯示在頁面中(如果允許的話)。
我們只需要捕獲 contents.onclick
,然后使用 confirm
來詢問用戶。一個(gè)好主意是使用 link.getAttribute('href')
來代替 link.href
。詳情請參見解決方案。
重要程度: 5
創(chuàng)建一個(gè)圖冊,通過點(diǎn)擊縮略圖可以更改主圖片。
P.S. 使用事件委托。
解決方案是將處理程序分配給容器,并追蹤點(diǎn)擊。如果點(diǎn)擊在 <a>
鏈接上,則將 #largeImg
的 src
修改為該縮略圖的 href
。
Copyright©2021 w3cschool編程獅|閩ICP備15016281號(hào)-3|閩公網(wǎng)安備35020302033924號(hào)
違法和不良信息舉報(bào)電話:173-0602-2364|舉報(bào)郵箱:jubao@eeedong.com
掃描二維碼
下載編程獅App
編程獅公眾號(hào)
聯(lián)系方式:
更多建議: