點擊
如果在多層嵌套中,對每層都設置事件監視器,試試看
<!DOCTYPE html>
<html lang="cn">
<body><div id="container"><button>點我!</button></div><pre id="output"></pre><script src="button.js"></script>"
</body>
</html>
const output = document.querySelector("#output");
const container = document.querySelector("#container");
const button = document.querySelector("button");function handleClick(e) {output.textContent += `你在 ${e.currentTarget.tagName} 元素上進行了點擊\n`;
}document.body.addEventListener("click", handleClick);
container.addEventListener("click", handleClick);
button.addEventListener("click", handleClick);
在 JS 腳本中先用 querySelector (querySelector是JS原生提供的DOM元素查找函數,是DOM API中的一部分,它的作用是通過 CSS選擇器 定位匹配的第一個元素)來確定了 output 和 container 位置并賦值給變量
(CSS選擇器通過特定語法匹配HTML元素并應用樣式,通過 .class 、 #id ,將其應用到JS里面來提取元素的好處就是不用更改語法)
接著設置了自定義函數 handleClick(e) ,參數e是一個事件對象(Event object),這是瀏覽器自動傳遞給事件處理函數的一個參數,比如?button.addEventListener("click", handleClick); 這告訴了如果點擊了button那么就調用 handleClick,這時會自動生成一個事件對象,其中 e.currentTarget 就是監聽對象 button ,而 e.currentTarget.tagName 就是 事件監聽器對象元素的標簽名
這個事件并不是通過 alert 實現輸出,而是直接更改 DOM元素output 的值,這會在點擊后直接顯示
你在 BUTTON 元素上進行了點擊
你在 DIV 元素上進行了點擊
你在 BODY 元素上進行了點擊
可以看到三層元素都觸發了單擊事件
可以觀察到
? ? ? ? 最先觸發的是按鈕的
? ? ? ? 然后是其父元素...
可以理解為:事件從點擊的最里層的元素冒泡而出
.target 和 .currentTarget 有什么區別
event.target 和 event.currentTarget 都指向DOM元素,但是
? ? ? ? event.target 指向的是觸發事件的元素,在冒泡過程中是保持不變的
? ? ? ? event.currentTarget 指向的是事件處理程序當前附加到的元素,也就是當前處理層的元素
注冊而非輪詢
也許你會好奇,為什么我都沒有加 while ,我一旦觸發事件還是會有相應的結果呢?
其實事件監視器是被動的、稱為“事件驅動程序”的模式
在瀏覽器內部有一個事件循環機制,它會不斷地持續檢查隊列中是否有事件要處理
異步處理:當用戶觸發事件時,瀏覽器會將這個事件放入事件隊列,事件循環一旦檢測到隊列中有事件就會用相應的事件處理(異步處理是一種編程和系統設計模式,其核心在于非阻塞執行,在發起耗時操作,如IO、網絡請求等后,程序不會等待操作完成,而是繼續執行后續任務,等操作完成再回調
(不阻塞:不會阻塞主線程或消耗CPU資源來主動檢查事件是否發生,只有事件觸發時相關代碼才會執行)
在線程、線程池、異步-CSDN博客有更詳細說明
隱藏
我們想要實現隱藏視頻,只有在點擊按鈕的時候才顯現,這時候點擊視頻會開始播放,點擊除視頻外的地方會隱藏視頻
<button>顯示視頻</button><div class="hidden"><video><source src="/shared-assets/videos/flower.webm" type="video/webm" /><p>你的瀏覽器不支持 HTML 視頻,這里有視頻的<a href="rabbit320.mp4">替代鏈接</a>。</p></video>
</div>
const btn = document.querySelector("button");
const box = document.querySelector("div");
const video = document.querySelector("video");btn.addEventListener("click", () => box.classList.remove("hidden"));
video.addEventListener("click", () => video.play());
box.addEventListener("click", () => box.classList.add("hidden"));
在 JS 里面添加了三個 'click' 事件處理器
classList 是 DOM 元素對象的一個屬性,提供了一個便捷的方式來操作元素的類(class屬性),它是 DOMTokenList 類型的對象,用于添加、刪除、檢查和切換 CSS 類
button 的點擊處理器會通過?box 的 classList 移除 div 的 "hidden" 類
在運行的時候會發現點擊按鈕的時候會顯示視頻,但是點擊視頻的時候盒子又被隱藏了,這是因為 video 在 div 里面,所以點擊視頻會觸發 video 的事件,也會觸發 div 的事件
這時候我們就想要阻止 video 向外界的感染,需要通過 stopPropagation( ) 方法
const btn = document.querySelector("button");
const box = document.querySelector("div");
const video = document.querySelector("video");btn.addEventListener("click", () => box.classList.remove("hidden"));video.addEventListener("click", (event) => {event.stopPropagation();video.play();
});box.addEventListener("click", () => box.classList.add("hidden"));
可以看到我們在 video 的事件里面添加了 event.stopPropagation();
事件捕捉
跟冒泡順序相反,從最外層到最里面,想實現這樣的操作只需要將 capture 參數設置為 true
比如剛開始的按鈕例子
document.body.addEventListener("click", handleClick, { capture: true });
container.addEventListener("click", handleClick, { capture: true });
button.addEventListener("click", handleClick);
順序就發生了顛倒
你在 BODY 元素上進行了點擊
你在 DIV 元素上進行了點擊
你在 BUTTON 元素上進行了點擊
事件委托
默認冒泡的話,我們可以通過子元素感染父元素,而不用一個個設置了,不直接在子元素上設置監聽器,而是將監聽器設置在父元素上,通過冒泡讓父元素監聽來自子元素的事件,
<!DOCTYPE html>
<html lang="en"></html>
<body><link href="button.css" rel="stylesheet" type="text/css"><div id="container"><div class="tile"></div><div class="tile"></div><div class="tile"></div><div class="tile"></div><div class="tile"></div><div class="tile"></div><div class="tile"></div><div class="tile"></div><div class="tile"></div><div class="tile"></div><div class="tile"></div><div class="tile"></div><div class="tile"></div><div class="tile"></div><div class="tile"></div><div class="tile"></div></div><script src="button.js"></script>"
</body>
</html>
.tile {height: 100px;width: 25%;float: left;
}
function random(number) {return Math.floor(Math.random() * number);
}function bgChange() {const rndCol = `rgb(${random(255)}, ${random(255)}, ${random(255)})`;return rndCol;
}const container = document.querySelector("#container");container.addEventListener("click", (event) => {event.target.style.backgroundColor = bgChange();
});
在這個例子中有很多 .title 元素在 #container 內
但是沒有對每個元素添加事件監聽器
而是在父元素 #container 上加了監聽器,通過 event.target 來確定是哪個子元素觸發了事件,并更改其 style.backgroundColor?為 bgChange