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