『新手日記』Day-13 JavaScript DOM Event
JavaScript 是一個事件驅動 (Event-driven) 的程式語言,當瀏覽器載入網頁開始讀取後,雖然馬上會讀取 JavaScript 事件相關的程式碼,但是必須等到「事件」被觸發(如使用者點擊、按下鍵盤等)後,才會再進行對應程式的執行。
Event Flow(事件流程)
『網頁元素接收事件的順序』
事件流程可以分成兩種機制:
- Event Bubbling(事件冒泡)
- Event Capturing(事件捕獲)
Event Bubbling(事件冒泡)
事件冒泡指的是「從啟動事件的節點 (event.target) 開始,逐層往上傳遞」,直到整個網頁的根節點 document
為止。(由下而上)
例如在 Todo List 裡監聽刪除事件時,click 事件發生在刪除按鈕 i.delete
上,但由於事件為往上傳遞,因此 ul
也收到了 click 事件,因而啟動了監聽器裡的 handler。
在「事件冒泡」的機制下,觸發事件的順序會是:
如果到某一層你想阻擋事件繼續往下一層傳播,你可以在該層使用 event.stopPropagation()
來阻止事件的傳播。
Event Capturing(事件捕獲)
跟Event Bubbling 相反,由上而下的傳遞則稱為「事件捕獲」。Capture 機制很少在實作中被運用,因此他通常是隱藏起來的。
依照 W3C DOM Events 標準的說明,當你點擊某個元素時,瀏覽器會先從最高層開始 capturing,一路前往到事件 target,再向上 bubbling:
由於元素之間有這樣的上下回報機制,我們的事件才能順利地動起來。
但在實作上,幾乎不會遇到情境需要運用 capture,但刻意想打開時,可以運用事件監聽器的第三個參數。這個參數用來切換 capture,預設值為 false
,刻意想打開時,可以設定為 true
:
1 | element.addEventListener("click", handler, useCapture) |
EventTarget.addEventListener()事件監聽
設置事件時,需要考慮到幾個基本要件:
- 觸發事件的 HTML 元素
- 事件類型,如 click、submit、keyup….
- 想要觸發的程式,會是一個函式,該函式會稱為事件處理器 (event handler)
而實務上,會多做一層「事件監聽器 (event listener)」,扮演 HTML 和 JavaScript 之間的接線生
1 | // listener |
addEventListener
可以傳入三個參數:
- event type,如
'click'
- handler,要啟動的 function,也可以直接使用匿名
- 函式 (見下例)
element.addEventListener('click', function () { alert('handling event') },useCapture)
- useCapture,切換 capturing 機制 ,預設值為
false
,刻意想打開時,可以設定為true。
一般不會用到第三個參數。
Event Type
事件的種類實在太多,以下就列出幾個常見的事件,剩下的就附上MDN網址:
滑鼠事件
鼠標在網頁上滑動時會跨越不同的元素邊界,它的事件設置有豐富的可能性,你可以點擊這裡查看完整的清單,但一般而言,最常見的有以下三種:
click
- 鼠標點擊元素mousemove
- 鼠標滑過元素mouseout
- 鼠標離開元素
鍵盤事件
最常用常見的鍵盤事件有:
keydown
- 點擊且長按一個鍵時keyup
- 放開按鍵時
完整的清單可看這裡。
表單事件
表單是網頁上獲取使用者回饋的重要元件,當你在網站上使用表單時,你的行為會受到 DOM 物件FormElementInterface
監控。關於表單需要另開一個主題討論,常見事件有:
submit
- 提交表單時focus
- 點擊某個輸入框時input
- 輸入框內容改變時
document 事件
DOMContentLoaded
- 當 HTML 下載完成並完整的建立 DOM 模型時觸發
就和我們總是在 </body>
前引入 JavaScript 檔案一樣,如果 HTML 還沒下載完就先進行 DOM 操作,勢必會遇到奇怪問題。在實務上除了注意檔案引入位置,在 JavaScript 時還會再包一層 DOMContentLoaded
,確保萬無一失。
其他事件
Event Delegation(事件指派)
事件指派是利用前面介紹的「事件流程」以及「單一事件監聽器」來處理多個事件目標。
1 | // html: |
我們看到我們的監聽器放在最外層的
這邊我們看到了target = [event.target](<http://event.target>)
這是什麼意思?其實他是『event object』
Event inheritance
在進入event object 時,要先提到inheritance
inheritance(繼承)指的是一個物件擁有另一個物件的和
💡 JavaScript使用的是 原型繼承 prototypal inheritance
如上圖,我們可以知道所有的Event 都是inheritance 最上方的Event Object,在w3schools裡也有提到:
因此我們需要學習的就是Event Object的property(屬性)
和 method(方法)
event object:
當監聽的事件發生時,瀏覽器會去執行我們透過 addEventListener()
註冊的 Event Handler (EventListener) ,也就是我們所指定的 function
。
這個時候,EventListener 會去建立一個「事件物件」 (Event Object),裡面包含了所有與這個事件有關的屬性,並且以「參數」的形式傳給我們的 Event Handler
其中有三個屬性要特別注意:
target(目標)
用途:需要知道到底我們觸發哪一個 DOM 元素時可以使用
1 | const target = event.target |
💡 this
跟 event.target
有點像,但是他們之間的區別是: 隨著 js 冒泡事件的發生,this
是會變化的,但 event.target
不會變化,它永遠是指觸發事件的 DOM 物件
preventDefault
用途:取消它們的預設行為,但並不會阻止事件向上傳遞 (事件冒泡) 。
有使用過的2個時機:
- 按鈕用a標籤,因為a標籤必須要寫上連結,但點擊就是跳轉頁面。
- form的submit就會自動傳送資料。
1 | <form> |
stopPropagation()
用途:阻擋事件向上冒泡傳遞
結語
終於複習完一遍DOM了,沒有想到會複習這麼久。如果只是單純看教案其實我感覺我一天左右就可以全部複習完,但因為要整理筆記,那整理過程中發現有些東西不了解又回去看教案或是網路上的資料才會導致比預期多花了一天。基礎理論的東西差不多都複習過了,接下來就是實作練習用大量的練習增加自己對這幾天複習內容的印象了。