Day28 | JS Event 事件
❒ 本篇會介紹到的部分
addEventListener 監聽、Event Bubbling 事件冒泡、Event Capturing 事件捕捉、stopPropagation 終止冒泡事件、preventDefault 取消預設、e.target 搭配 nodeName 與 keyCode 、表單中 blur, focus, click 事件、keydown 事件、mousemove, mouseleave 事件、網頁座標 ( screen、page、client )。
❒ 各種事件綁定的差異
➊ 不推薦在 HTML 中執行 JS,此為很老式的寫法
1 | <input type="button" onclick="alert('Hello')" value="點擊" class='btn'> |
在 HTML 中執行 JS ( 以 onclick
為例 ),不推薦的原因 :
- 在 JS 中可以設定在某些條件下才做
綁定
執行onclick
,不像 HTML 中onclick
會一直執行。 - 可能在
onclick
中被插入惡意程式碼。
➋ 監聽的推薦寫法
1 | btn.addEventListener('click' , function(){ },false) |
❒ addEventListener - 事件監聽
結構
addEventListener( '參數1' ,參數2 ,參數3 );
參數1
: 選擇事件,例如"click"
亦可換為其他事件。滑鼠的各種事件可參閱 w3school - HTML DOM Events 。參數2
:function(e){}
,函式內可加入e
,會回傳event
事件。參數3
:false
。true
: 從外層往內層找指定元素。false
( 預設 ) : 指定元素往外層找,預設為false
,所以如果沒寫就會是預設狀態。
範例 1. 寫一個加法器,點選按鈕下方數字會做加總
出處 : JavaScript 工程師養成直播班 - 2021 春季班影音
為什麼 let count = 0;
不能放在 function(e){}
內 ?
函式內的大括號 {}
執行完會釋放記憶體,如果不希望值因函式運算完而消失,就需要放在外層,函式內運算完就會往外送依序做加總。
❒ 如何觀看 DOM 有註冊事件監聽
出處 : JavaScript 工程師養成直播班 - 2021 春季班影音
可搭配 chorme 開發者工具查看按鈕是否有註冊監聽事件
- 於監聽的按鈕點選滑鼠右鍵 / 檢查
- 開發者工具 Elements / Event Listeners 可看到目前的監聽事件們。
❒ event 物件 - 告訴你當下元素資訊
DOM events 事件,例如滑鼠的各種事件可參閱 w3school - HTML DOM Events ,但現在不推薦在 HTML 使用 JS,因爲較老式用法有許多缺點下方會說到。
範例 1. 以按鈕為例,點擊按鈕跳出彈跳視窗 hello
範例 2. 點擊按鈕知道滑鼠的位置或出現此按鈕的詳細資訊 ( type ) 需透過甚麼方式呢 ?
- 點擊按鈕知道滑鼠位置,應用在點擊某個 XY 軸位置,會出現資訊視窗。
- MouseEvent 為滑鼠事件,會把當下點擊的那一瞬間的所有資訊都記錄在物件裡,可運用裡面的物件去做對應的事情,例如下方要讀取點擊按鈕的 X 軸座標位置
clientX
,可從函式參數e
去讀取裡面的屬性clientX
。 - 執行監聽時,會在你命名的函式中帶入一個參數
e
( 參數名可自訂 ),所以點擊按鈕後會把所有物件 ( MouseEvent 為物件 object ) 資訊紀錄到這個參數內。- 開啟開發者工具 Console 可見 MouseEvent ( MouseEvent 為物件 object ) 顯示按鈕的詳細資訊。
- W3school - HTML DOM MouseEvent 相關方法
- 開啟開發者工具 Console 可見 MouseEvent ( MouseEvent 為物件 object ) 顯示按鈕的詳細資訊。
❒ 綁定事件的語法差異
onclick
與 addEventListener( "click" , function(e){},false)
差異 ?
onclick
不能同時綁定兩個事件。如果綁定兩個事件以上,只會讀取最後一個。addEventListener( "click", function(e){}, false);
可以同時綁定多個事件。
❒ Event Bubbling 事件冒泡、Event Capturing 事件捕捉的差異
addEventListener ()
中 true
與 false
差異?
true
( Event Capturing 事件捕捉 )- 從外層往內層找指定元素。
false
( Event Bubbling 事件汽泡 )- 指定元素往外層找。
- 預設為
false
,所以如果沒寫就會是預設狀態。 - 較常使用
false
,點擊最近的東西來去觸發事件。
範例 1. 如果 .body
與 .box
為 false,點選 box 區域時會先執行哪個呢 ?
false
( Event Bubbling 事件汽泡 ) : 指定元素往外找 。以範例來說,當下點的指定元素.box
往外層找.body
→ 先跳出 box 彈跳視窗再出現 body 彈跳視窗true
( Event Capturing 事件捕捉 ) : 從最外層往內找到指定元素。以範例來說,外層.body
往內找到指定元素.box
→ 先跳出 body 彈跳視窗再出現 box 彈跳視窗- 所以較常使用
false
,點擊最近的東西來去觸發事件。
範例 2. 承範例 1.,如果 body
為 true , .box
為 false,點選 box 區域時會先執行哪個呢 ?
.box
區域於body
範圍內,而body
的addEventListener
是設定true
( 從外層找到指定元素 ),所以會先跳body
再跳.box
。
❒ stopPropagation - 中止冒泡事件
防止監聽的元素互相衝突
當點選到重疊的元素時,只希望執行一個元素。可使用 stopPropagation
當指定元素和上層元素有重疊時,會阻止指定元素往外層找。
範例 1
- 如果未加上
stopPropagation
,點擊.box
會彈出box
與body
視窗 ( 事件冒泡由指定元素往外執行 )。 .box
加上stopPropagation
後終止冒泡事件,點擊.box
後就只會彈出box
的 alert 視窗。- 如果外層沒有另外的事件會被觸發,那就不需要
stopPropagation
囉!
❒ preventDefault 取消預設觸發行為
常使用的情境:
- a 連結。
- submit 按鈕 ,先透過 JS 去查詢表單是否有誤,再透過 post 傳送。而非默認的 submit 後就直接傳送到後端做驗證。
範例 1. 使用 preventDefault,取消 a 連結觸發
- 使用
preventDefault
取消預設觸發行為後,點擊 a 連結就不會轉跳到 google 頁面。
❒ e.target - 了解目前所在元素位置
e.target
可知道目前點擊的 DOM 範圍。
範例 1
- 監聽最外層 ul 標籤,分別點擊
li
與 a 標籤,可於開發者工具看見這兩個 DOM 的範圍。 - 另外
console.log (e);
部分,開啟開發者工具也可以看到點擊元素的目前所在位置target : li
。
❒ nodeName 是什麼
nodeName
為 DOM 元素的節點,可搭配 e.target
使用。
❒ e.target 搭配 nodeName 節點,抓到你預期要做的事情
出處 : JavaScript 工程師養成直播班 - 2021 春季班影音
範例 1
1 | <input type="button" value="點擊" class='btn'> |
1 | const btn = document.querySelector('.btn'); |
範例 2. 使用 nodeName 判斷是否有點擊到按鈕,正確點擊到顯示”你目前點到按鈕了“
1 | <ul class="list"> |
- 先宣告變數,綁
.list
這個 DOM。所以ul.list > li *2
以內的範圍都會被綁定。 - 在
ul.list > li *2
範圍內觸發 click,就會觸發函式。 e.target.nodeName
會顯示 DOM 元素點擊到的節點為INPUT
標籤。- 使用判斷式判斷
e.target.nodeName
與點擊到的DOM 節點是否相同,是的話就觸發大括號{}
內的程式碼。
❒ change - 表單內容,更動內容時觸發
範例 1. 選擇區域顯示每個區域的農夫名
1 | <select id="areaId"> |
1 | const country = [ |
- 選單在
change
時會觸發後方函式。 - 透過
forEach
撈出變數country
內的陣列資料,並使用判斷式篩選,讓選單的值與forEach
撈出的place
相同時,列出農夫名字。e.target.value
可撈出選單中文字欄位內的值。
- 因為要撈出某個區域的農夫,所以先寫一個空字串
let str = '';
,等跑完forEach
再帶到 li 內 。- innerHTML 特性 → 新帶入的值會把舊的值蓋過,為了防止資料被蓋掉寫一個空字串
let str = '';
,來做累加字串。 - 最後再把 if 篩選完的資料賦予
list.innerHTML
。
- innerHTML 特性 → 新帶入的值會把舊的值蓋過,為了防止資料被蓋掉寫一個空字串
備註
list.innerHTML = str;
寫在 forEach
內與外雖然網頁皆顯示無誤,但對運行程式碼的效能考量不理想。
- 寫在
forEach
內運行的次數會和陣列長度一樣。 - 寫在
forEach
外只會運行一次。
❒ 表單中 blur 與 focus
focus
所在焦點blur
離開焦點- 當元素聚焦時,會觸發
focus
事件,當元素失去焦點時,會觸發blur
事件。
以表單為例,滑鼠點擊表單要輸入數字 ( 點擊了表單即觸發 focus
事件 ),但沒輸入就離開 ( 再點擊表單外的區域 ),就會觸發 blur
事件。
範例 1. 使用計算機計算出餐點金額,並搭配 blur 事件做欄位防呆機制
1 | <h2>六角西餐廳 - 顧客點餐系統</h2> |
1 | //歸戶 |
- 用到
isNaN
判斷是否為數字- JavaScript
isNaN
函數用來判斷參數是否為特殊的非數字值NaN
,如果是就回傳true
,不是則回傳false
,要注意的是isNaN
函數會將參數轉換成數字 ( number ),如果轉換成功就會回傳true
,失敗則回傳false
。 - 相關資訊:Wibibi - JavaScript isNaN 函數、MDN - isNaN()。
- JavaScript
❒ return 和 return false 的差異
Q1. isNaN 中使用到 return false ,而 return
和 return false
的差異為何呢 ? ( 助教回覆 )
Ans :return
在函式內使用,會回傳一個值,並終止函式往下執行。return false
通常用來阻止提交表單或是繼續執行下面的程式碼。簡單來說就是阻止執行預設的行為。
.
Q2. return false;
return false;
下方 1 與 2 的事情它都會做:
- 他會做
event.preventDefault();
。 - 他會做
event.stopPropagation();
。 - 停止 callback function 的執行並且立即
return
回來。
出處 : event.preventDefault()跟return false的差別是?
❒ keyCode - 點擊鍵盤,射發火箭
鍵盤式移動的遊戲,或互動式網站可使 keyCode
範例 1. 如何查詢鍵盤上每個按鍵的 keyCode
1 | const body = document.body; |
- 監聽使用
keydown
指按下鍵盤的那個剎那,任何的鍵盤按鍵按下都可以取得對應的鍵盤代碼,也就是所謂的keyCode
。 - 輸入下面程式碼,開啟開發人員工具,於網頁上輸入鍵盤上任一按鍵,會回傳
keyCode
。
範例 2. 按下鍵盤任一按鍵,顯示 keyCode 在網頁上
- 監聽整個網頁,當在這個頁面執行 keydown 時 (按下鍵盤任一鍵),會觸發函式。
- 使用
renderCode()
把keyCode
渲染在畫面上。
範例 3. 火箭遊戲練習 : 按下數字分別發射火箭,按 1 發射第一個火箭,按 2 發射第二個火箭,按 3 發射第三個火箭
- 用
keydown
查出按鍵 1、2、3 的keyCode
。 - 監聽整個網頁,當有人按下鍵盤 ,執行函式內的程式碼。
- 因知道明確數字 ( 上方以查出鍵盤 1、2、3 的 keyCode ),所以可以使用
switch
做控制判斷。
關於 Document.body
- Document 介面代表所有在瀏覽器中載入的網頁,也是作為網頁內容 DOM Tree(包含
<body>
、<table>
與其它的元素 )的進入點。 Document.body
主要是「 回傳目前文件的<body>
節點,如元素不存在則回傳null
」,因此只有body
能使用。
❒ mousemove 當滑鼠滑入指定內容時觸發
mousemove
滑鼠移動到指定元素後觸發。
mouseleave
滑鼠離開指定元素後觸發。
範例 1. 滑鼠滑入方形區塊內,變圓形
- 使用
mousemove
搭配setAttribute
讓滑鼠進入 box 區域後,讓 box 由方形變圓形。
範例 2. 延續範例1. 滑鼠離開後再由圓形變回方形
- 使用
mousemove
搭配setAttribute
讓滑鼠進入 box 區域後,讓 box 由方形變圓形。 - 為了讓整個過程順暢點,再加入
mouseleave
讓滑鼠離開 box 區域後再變回方形。- 使用
classList.remove
刪除圓形boxRadius
,再使用classList.add
新增回原本方形box
- 使用
1 | // 刪除單個 |
範例 3. 使用 animation
與 JS 的 mousemove
做小遊戲
animation
屬性的應用可參閱 「 Day8 | Animation、Transition、Transform 動畫效果 」- 範例
- 老師範例
- 自我練習 CodePen 範例
關於 classList
DOM 裡每個節點都有一個 classList
物件,可以使用來新增、刪除、修改節點上的 CSS 。
- 刪除
remove
- 新增
add
- 切換
toggle
❒ 網頁座標 - 了解 screen、page、client 箇中差異
可先開啟 CodePen 範例 搭配下方解說
screen
螢幕 ( 手機、平板 、電腦等裝置 ) 解析度的座標點
page
- 以整個網頁寬高為主
- 如果頁面太長出現卷軸,Y 軸也會跟著持續增加。
- 範例中 當右邊卷軸往下滑動,
pageY
座標點數字會持續增加,而clientY
只會顯是當下瀏覽器高度的座標點。
- 範例中 當右邊卷軸往下滑動,
client
- 瀏覽器窗口的座標點
- 如果瀏覽器頁面太長出現卷軸,Y軸也只會出現當下視窗高度的座標點
- 範例中 當右邊卷軸往下滑動,
pageY
座標點數字會持續增加,而clientY
只會顯是當下瀏覽器高度的座標點。
- 範例中 當右邊卷軸往下滑動,
範例 1
1 | function showPosition(e){ |
解說
- 程式碼中的
e
為showPosition
函式中的參數,可顯示出showPosition
函式的所有資訊。
- event 內有很多方法,如下示意圖
console.log(e)
顯示的函式資訊。- 以其中的方法之一的
screenX
為例 →e.screenX
( 亦可換screenX
、screenY
、target
.. 等 ) 搭配textContent
,可把screen X
座標資訊顯示在網頁上。
- 以其中的方法之一的
範例 2. 網頁座標 ( 圖案取代鼠標 - clientXY )
CSS
- 避免原來的滑鼠指標 & 圖案取代的滑鼠指標 同時出現,於 body CSS 中使用
cursor:none;
.mouseImg
使用 fixed 可以不會因為旁邊的捲軸滾動而影響到位置 → 如圖 ,但使用absolute
就會因為卷軸滾動而讓 icon 移動到上方 如圖 。
JS
如何讓圖案取代的鼠標 & 原來的鼠標同步 ?
- 透過 DOM API 來操作 HTML 元素的 CSS 樣式 (style)。
- 因 client XY 會顯示滑鼠於瀏覽器窗口的座標點,所以使用它來運算。
- 讓滑鼠運算窗口的位置
❒ 事件監聽優化篇 - 從父元素來監聽子元素內容
範例 1. 父元素內有很多子元素,querySelector()
只會讓第一個子元素會獲得監聽
- 滑鼠點選湯姆, console.log 出現湯姆,但點選捷克時,並不會顯示捷克。因為
querySelector()
的特性只會抓第一筆資料做更新。
範例 2. 透過 noedName 確定點擊的東西是不是我要的節點
- 監聽 ul 區域,搭配
e.target.nodeName
點擊區域內項目可確定是否有點擊到需要的節點。
範例 3. 不想點擊到 ul 範圍出現所有 li 內容,希望點擊 li 才觸發事件該怎麼做
使用情境:
- 以
ul>li
為例:- 如果子元素有很多個,希望每個都獲得監聽,可直接監聽父元素
ul
,每個子元素li
皆可獲得監聽。 - 好處為可以透過 event 顯示出父元素內的所有資訊,但缺點就會像下方示意圖,點擊了 ul 範圍出現所有 li 內容。
- 只想要點選 li ,Console 顯示 li 的值 ( 湯姆、捷克 ),點選其他元素 Console 皆不顯示,可加入判斷式 if。
- 如果子元素有很多個,希望每個都獲得監聽,可直接監聽父元素
解析:
- 加入判斷式 if。
- 透過
nodeName
方式查詢到 li 們的節點為 LI 把它帶入判斷式 if 中 →if( e.target.nodeName !== 'LI' ){return}
點擊到的節點不等於 li 回傳 return- retuen 可當回傳值外亦可當中斷點。 當中 retuen 也可不寫讓它是空值,這樣後方程式碼就會停止運作 ( 後方的
console.log()
就不執行 ) 。 - 以
console.log(e.target.nodeName);
點選 li 內的湯姆與捷克, Console 出現的節點為 LI 所以判斷式內寫!== 'LI'
- 最後,除了 li 內的值會顯示外,點選其他元素 Console 皆不會顯示。
- retuen 可當回傳值外亦可當中斷點。 當中 retuen 也可不寫讓它是空值,這樣後方程式碼就會停止運作 ( 後方的
範例 2. 點選左方 box 內的文字,文字會顯示於右方 box 內
1 | <div class="box"> |
- 於全域設定一個空陣列
box2Data
,點擊.boxList>li
的文字可以 push 到空陣列中,再渲染到.box2List
內。
資料來源
- JavaScript 入門篇 - 學徒的試煉
- Wibibi - JavaScript isNaN 函數
- MDN - isNaN()
- animation 應用可參閱 - CSS | Animation 與 keyframes 、 Animation 與 transiton差異
- JS基礎篇 - HTML DOM classList 屬性
- JavaScript DOM CSS ( 修改 CSS 樣式 )