addEventListener 實作:從綁定事件到修改 DOM 屬性
知道了 事件的種類 與 事件級別 之後,這篇就來把它們串起來:選取 DOM → 掛上監聽器 → 改變畫面。
建立 id 屬性
在 <img> 的屬性中建立一個 id 為 Pic 的屬性,讓 JavaScript 來選取。
<body>
<img src="./images/logo.jpg" id="Pic" width="300" height="400" alt="Logo">
</body>小提醒
Pic 為自行定義的名稱,Away 習慣為 id 設置的值以大駝峰命名。
為不同的事件類型建立監聽器
- 從文件中,選取欲綁定事件的 DOM 物件。js
document.querySelector('#Pic') - 建立事件監聽器。js
document.querySelector('#Pic').addEventListener(); - 加入 事件類型 與 執行事項 兩個參數。js
document.querySelector('#Pic').addEventListener('click', () => {});
以下為授課的教學案例:
document.querySelector('#Pic').addEventListener('click', () => {
console.log('click');
});
document.querySelector('#Pic').addEventListener('mouseenter', () => {
console.log('mouseenter');
});
document.querySelector('#Pic').addEventListener('mouseleave', () => {
console.log('mouseleave');
});小提醒
同一個元素綁三個不同事件完全沒問題,這正是 addEventListener() 相對於 onclick 的優勢。若上面的 console.log() 沒有反應,請先確認 選取 有沒有成功。
設定 DOM 的屬性、值
- 從文件中,選取欲設定屬性的 DOM 物件。js
document.querySelector('#Pic'); - 設定影像
src的屬性,影像的路徑以字串來表示。jsdocument.querySelector('#Pic').src = ''; - 寫入影像的路徑。js
document.querySelector('#Pic').src = './images/b5.jpg';
把它與事件合起來,就是最經典的 滑過換圖:
const elemPic = document.querySelector('#Pic');
elemPic.addEventListener('mouseenter', () => {
elemPic.src = './images/b5.jpg';
});
elemPic.addEventListener('mouseleave', () => {
elemPic.src = './images/logo.jpg';
});想改的是文字或 HTML 內容,請看 節點的屬性。
利用索引值來選取對象
<figure class="wrap-head" id="Thumb">
<img class="img-resp img-rounded hover-weak" src="./images/s1.jpg" width="100" height="65">
<img class="img-resp img-rounded hover-weak" src="./images/s2.jpg" width="100" height="65">
<img class="img-resp img-rounded hover-weak" src="./images/s3.jpg" width="100" height="65">
<img class="img-resp img-rounded hover-weak" src="./images/s4.jpg" width="100" height="65">
<img class="img-resp img-rounded hover-weak" src="./images/s5.jpg" width="100" height="65">
</figure>querySelectorAll()
可選取出相同的 DOM 並以 NodeList 類陣列的方式回傳。
console.log(document.querySelectorAll('#Thumb .img-resp'));之後便可以用索引方式選取 DOM,索引值由 0 開始。
// 選取此選取器的第一個
console.log(document.querySelectorAll('#Thumb .img-resp')[0]);
// 選取此選取器的第二個
console.log(document.querySelectorAll('#Thumb .img-resp')[1]);children
可選取出指定 DOM 的子層元素並以 HTMLCollection 類陣列的方式回傳。
console.log(document.querySelector('#Thumb').children);之後便可以用索引方式選取 DOM,索引值由 0 開始。
// 選取指定 DOM 的子層元素並取出第一個
console.log(document.querySelector('#Thumb').children[0]);
// 選取指定 DOM 的子層元素並取出第二個
console.log(document.querySelector('#Thumb').children[1]);一次為多個元素綁定事件
有了類陣列,就能用 forEach() 一次綁完:
document.querySelectorAll('#Thumb .img-resp').forEach((elemThumb) => {
elemThumb.addEventListener('click', (e) => {
console.log(e.target.src);
});
});小提醒
元素數量很多,或子元素之後還會動態新增時,改把監聽器綁在父層會更省效能,這個做法叫做 事件委派。
常見問題
addEventListener 的兩個必填參數是什麼?
第一個是事件類型的字串,例如:click;第二個是事件觸發時要執行的 函式。兩個都給了,監聽器才會運作。
為什麼綁定事件後點了沒反應?
最常見的原因是選取不到元素,程式比 DOM 早執行時 querySelector() 會回傳 null。先印出選取結果確認,再檢查選取器的大小寫與拼字,也可回頭看 JavaScript 寫在哪裡 的 defer 說明。
querySelectorAll 與 children 有什麼不同?
querySelectorAll() 依選取器搜尋所有符合的元素,範圍不限直接子層;children 只回傳指定元素的直接子元素。前者回傳 NodeList,後者回傳 HTMLCollection。
索引值為什麼從 0 開始?
這是多數程式語言的慣例,第一個元素的索引是 0。因此第三張圖片要寫 [2],最後一個則常用 length 減 1 取得。