iT邦幫忙

2018 iT 邦幫忙鐵人賽
DAY 16
19
Modern Web

重新認識 JavaScript系列 第 16

重新認識 JavaScript: Day 16 那些你知道與不知道的事件們

  • 分享至 

  • xImage
  •  

本系列文章已重新編修,並在加入部分 ES6 新篇章後集結成書,有興趣的朋友可至天瓏書局選購,感謝大家支持。

購書連結 https://www.tenlong.com.tw/products/9789864344130

讓我們再次重新認識 JavaScript!


在前面兩篇文章中,我們已介紹過事件的傳遞機制,以及如何阻擋事件的冒泡與預設的行為。 那麼,作為「事件三部曲」的最後一篇,我們就來大略介紹一下,DOM 規範當中究竟提供了哪些事件。

事件的種類

瀏覽器可能發生的事件有很多種,了解這些事件的情境及效果,是開發 Web 應用程式不能忽略的基礎。

  • 介面相關事件

介面相關的事件不一定會與使用者對 DOM 的操作有關係,反而大多數與 window 物件比較相關。

  • load 事件:
    註冊在 window 物件上,指的是網頁資源 (包括CSS、JS、圖片等) 全數載入完畢後觸發。
    如果是 img 元素的 load 事件,則表示是此圖片載入完畢後觸發。

  • unloadbeforeunload 事件:
    load 事件相反,unloadbeforeunload 事件分別會在離開頁面或重新整理時觸發,而 beforeunload 會跳出對話框詢問使用者是否要離開目前頁面。

https://ithelp.ithome.com.tw/upload/images/20171219/20065504Ap3UvTrII8.png

  • error 事件:
    error 事件會在 document 或是圖片載入錯誤時觸發。
    值得一提的是,由於維護性的考量,大多事件的註冊我會強烈建議使用「非侵入式 JavaScript」的寫法,另外寫在 <script> 標記,只有 error 事件最適合以 on-event handler 的寫法來處理:
<img src="image.jpg" onerror="this.src='default.jpg'">

像這樣,當 image.jpg 這個圖檔不存在的時候,馬上就會觸發 error 事件。此時就會透過 this.src<img> 的 src 屬性替換成指定的預設圖檔,是相當實用的技巧。

若是在網頁 load 完成後才註冊 error 事件的 handler,你只會看到叉燒包或者破圖的結果,因為 error 事件不會再次被觸發,後來掛上去的 handler 就等於沒有一樣。

  • resize 事件:當瀏覽器 (window) 或指定元素 (element) 的「尺寸變更」時觸發。

  • scroll 事件:當瀏覽器 (window) 或指定元素 (element) 的「捲軸被拉動」時觸發。

  • DOMContentLoaded 事件:
    類似於 load 事件,但不同的是,load 事件是在網頁「所有」資源都已經載入完成後才會觸發,而 DOMContentLoaded 事件是在 DOM 結構被完整的讀取跟解析後就會被觸發,不須等待外部資源讀取完成。

簡單來說, DOMContentLoadedload 的時機可以用這張圖來解釋:

https://ithelp.ithome.com.tw/upload/images/20171219/20065504och2Xekk7T.png
圖片修改自: Web Browsers : What is Web Browser

我們在 Day 12 透過 DOM API 查找節點 曾介紹過,<script> 標籤要是放在 <head> ... </head> 之間,因為還沒解析到網頁本體會有選取不到 DOM 的問題對吧?

<!DOCTYPE html>
<html>
<head>
  <script>
    // 因 Document 結構未載入,無效
    document.getElementById('hello').textContent = 'Hello';
  </script>
</head>
<body>

  <div id="hello"></div>

</body>
</html>

那麼,改成這樣

<head>
  <script>
    document.addEventListener("DOMContentLoaded", function() {
      // 當 document 結構已解析完成才會執行
      document.getElementById('hello').textContent = 'Hello';
    }, false);
  </script>
</head>

就可以排除 <script> 標籤放在 <head> ... </head> 抓不到 DOM 的問題了。
與大家所熟知的 jQuery $( document ).ready( handler ) 作用類似。


  • 滑鼠相關事件

    • mousedown / mouseup 事件: 這兩個事件分別會在滑鼠點擊了某元素「按下」(mousedown) 按鈕,以及「放開」(mouseup) 按鈕時觸發。

    • click 事件: 當滑鼠「點擊」了某元素時觸發。

    • dblclick事件: 當滑鼠「連點兩次」了某元素時觸發。

    • mouseenter / mousemove / mouseleave 事件:
      這三個事件要放在一起看

      1. 當滑鼠游標移入了某元素時,會先觸發 mouseenter 事件。
      2. 滑鼠游標在這個元素內「移動」時,會連續觸發 mousemove 事件。
      3. 直到滑鼠游標離開了這個元素,才觸發 mouseleave 事件。

      https://i.imgur.com/PIz8bJB.gif

    這些滑鼠相關的事件,都可以透過上一篇曾介紹的 event.pageXevent.pageY 屬性去取得目前在網頁對應的座標。


  • 鍵盤相關事件

    鍵盤相關事件有下列三種,在大多數情況下會將鍵盤事件註冊在 input 的輸入框上。

    • keydown 事件: 「壓下」鍵盤按鍵時會觸發 keydown 事件。

    • keypress 事件: 除了 Shift, Fn, CapsLock 這三種按鍵外按住時會觸發,若按著不放則會連續觸發。

    • keyup 事件: 「放開」鍵盤按鍵時觸發。

    如果我們針對同個元素同時綁定了這三個鍵盤事件,那麼這三個事件執行的順序會是:

    "keydown"
    "keypress"
    "keyup"
    

    若此時想要知道使用者按下的按鍵,則可以透過 event.keyCode 屬性來查詢。
    註: keyCode 的對應表可以到這裡查看:https://gist.github.com/tylerbuchea/8011573 。

    例如,今天你想要當使用者在 input 輸入框按下 「enter」 時,發動 submit,就可以這樣做:

    textBox.addEventListener('keydown', function(e){
      // enter 的 keyCode 是 13
      if( e.keyCode === 13 ){
        formSubmit();
      }
    }, false);
    

    像這樣,透過 e.keyCode 就可以判斷使用者目前按下的是哪個按鈕。


  • 表單相關事件

    • input 事件: 當 inputtextarea 以及帶有 contenteditable 的元素內容被改變時,就會觸發 input 事件。

    • change 事件: 當 inputselecttextarearadiocheckbox 等表單元素被改變時觸發。 但與 input 事件不同的是,input 事件會在輸入框輸入內容的當下觸發,而 change 事件則是在目前焦點離開輸入框後才觸發。

    • submit 事件:當表單被送出時觸發,通常表單驗證都會在這一步處理,若驗證未通過則 return false;

    • focus 事件:當元素被聚焦時觸發。

    • blur 事件:當元素失去焦點時觸發。


  • 特殊事件

    • Composition Event (組成事件):

    Composition Event 其實指的是 compositionstartcompositionend ,以及 compositionupdate 這三個事件。

    介紹 Composition Events 之前先來談談 DOM API 過去對輸入框偵測變化的幾個方式。

    前面介紹過,常見的表單輸入框如: <input type="text"> 如果要動態監聽輸入框的文字變化時, 大多會透過監聽 keydownkeypresskeyup 等鍵盤事件來判斷 value 是否變動,但如果是透過「複製貼上」之類的操作,就無法透過鍵盤事件來判斷。

    而即使是 change 事件則是要在使用者改變內容,且焦點離開輸入框的前一刻才會被觸發。

    所以後來有了 input 事件, input 事件會在輸入框的內容被改變時即時觸發,確實也解決了過去在 onChange 以及鍵盤相關事件功能不足所產生的問題。

    但是,新的問題來了!

    通常像這樣的搜尋框,我們會用類似 autocomplete (自動完成) 的方式給使用者搜尋建議 (以 google 為例):
    https://kuro.tw/static/img/google-autocompleted.png

    如上圖,在輸入中文的時候,通常會需要透過注音之類的輸入法來做拼字。

    但是在大部分的情況下,針對「注音符號」或是「拼音文字」去給搜尋建議是沒有太大意義的。

    這個時候就需要透過 Composition Events 來為輸入框做增強。

    透過 Composition Events 我們可以觀察使用者在輸入框內開啟輸入法 (Input Method Editor, IME) 時,組字或選字的狀態。

    Composition Events 提供三個事件給開發者監聽:分別是 compositionstartcompositionend ,以及 compositionupdate

    • compositionstart: 輸入框內開啟輸入法,且正在拼字時觸發。
    • compositionupdate: 輸入框內開啟輸入法,且正在拼字選字時更改了內容時觸發。
    • compositionend: 輸入框內開啟輸入法,拼字或選字完成,正要送出至輸入框時觸發。

    執行的時候像這樣:

    https://kuro.tw/static/img/composition-demo.png

    可以看到,如果要確認使用者輸入完成並送出文字時,就可以透過 compositionend 來做最後確認。


自訂事件

自訂事件可以用 Event constructor 建立,同樣透過 addEventListener 去監聽,由 dispatchEvent 決定觸發的時機。

var event = new Event('build');

// 監聽事件
elem.addEventListener('build', function (e) { ... }, false);

// 觸發事件
elem.dispatchEvent(event);

若是想要在自訂事件內增加更多資料,則可以改用 CustomEvent

var event = new CustomEvent('build', { 'detail': elem.dataset.time });

那麼在 Event Handler 就可以透過 event 來接收:

function eventHandler(e) {
  log('The time is: ' + e.detail);
}

當然,瀏覽器提供的事件相當多,今天分享的部分主要是比較常見,以及工作實務上需要特別注意的部分。

其他的多數事件你都可以在 MDN 的 Event reference 找到。


上一篇
重新認識 JavaScript: Day 15 隱藏在 "事件" 之中的秘密
下一篇
重新認識 JavaScript: Day 17 函式裡的「參數」
系列文
重新認識 JavaScript37
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中
0
iT邦新手 5 級 ‧ 2018-10-30 17:12:48

感謝分享呀~
找好久呢>"<

0
增廣建文
iT邦研究生 5 級 ‧ 2019-03-12 22:03:03

大推!
對於要處理中文/拼音的部分十分有幫助

我要留言

立即登入留言