昨天我們說到,如果今天我決定要用 JS 來寫一個動畫效果,當使用者按下網頁的按鈕之後,把本來的文字更改為「按下按鈕囉!」,要怎麼寫。
但是最關鍵的一步:怎樣讓電腦因為我按下按鈕而做出反應,卻沒有提到。今天就讓我們繼續深入探索這一塊吧!
想像今天有一個球丟過來,你可能會選擇接住它(或是躲開)。除非你可以預知未來,不然應該不太會有你已經先躲開,才有球飛過來的事情吧。對電腦來說也一樣,你丟給它一個事件,它收到以後去執行相對應的事情。
我們昨天有提到 HTML Dom 的架構樹。讓我們一邊回想昨天的架構樹,一邊思考一下這蛋生雞雞生蛋的問題:當我點擊按鈕的時候,事件是從按鈕的 <button>
一路傳遞到上面的<body>
、<head>
、<document>
,還是要從<document>
一路傳下來呢?
其實這兩種都會執行。
從 <document>
一路傳到最底層,順序由上而下,我們稱呼為事件捕獲 Event Capturing。反之,順序由下而上的,我們稱呼為事件冒泡 Event Bubbling。用下面這張圖會比較清楚:
當我點擊的物件有父層(物件本身是子層),順序依序為:
點子層>父捕獲>子捕獲>子冒泡>父冒泡。
當我點擊的物件本身就是父層時,順序依序為:
點父層>父捕獲>父冒泡。
當我們要讓電腦收到事件,做出對應事項,有好幾種做法,其中一種,是在 html 標籤後面放事件處理程式碼,也就是「on + 事件名稱 = "要做的事情"」。
舉例來說有這幾種:
事不宜遲讓我們來使用看看!當滑鼠點擊按鈕時,我要讓它的字變成紅色的。我們可以在 html 裡這樣寫:
<button onclick = "style.color = 'red';">click</button>
這邊的 style.color = 'red'
運用到之前的觀念,不熟的人可以回去讀昨天的文章。
在使用這種方式時要注意幾點:
請輪流使用雙引號跟單引號:
就像上面的範例一樣,因為外圍已經包了一層雙引號,裏頭的部分就改用單引號包住。否則電腦會不知道這個雙引號的另一半是哪個雙引號,導致判別錯誤!
善用 this 來減少程式複雜度,直接在 JS 的地方去定義函式:
相信大家寫 html 跟 css 時,都會盡量避免直接把 css 寫在 html 裡面,因為可能要重複寫好幾次同樣的東西,而且版面會變得又亂又難閱讀。 JS 也是一樣,雖然 onclick 的詳細設定可以直接寫在 html 裡,但為了避免未來的你穿越到現在砍死你,還是別吧!
要怎麼寫呢?在 html 的部分我們這樣寫:
<span onmouseover = "over(this);" onmouseout = "out(this);"> 移過來移出去顏色不一樣</span>
<div onmouseover = "over(this);" onmouseout = "out(this);"> 移過來移出去,這個顏色也不一樣</div>
在 js 的部分我們這樣寫:
function over(e){
e.style.color = "red";
}
function out(e){
e.style.color = "black";
}
發現了嗎?我們重複使用了 over 和 out 這兩個函式,但是不需要每次都寫一次 over 是什麼? out 又是什麼。
this 的概念很難簡單描述完,這邊請先理解成英文代名詞的概念(在 span 的地方 this 代表 span , div 的地方代表 div ),之後會再放上 this 的文章供大家閱讀。
學會了上面的以後,讓我們來介紹另一種方式吧!
事件監聽的意思是,當我監聽到有事件,就自動去做後面的事情。寫法是把 addEventListener 加在你想要監聽的東西後面,並且告訴他要監聽怎樣的行為,跟執行什麼動作。
用昨天的例子來說,使用者按下網頁的按鈕之後,把本來的文字更改為「按下按鈕囉!」,我們可以這樣理解:
我要告訴電腦要監聽按下的這個行為,並執行把本來的文字更改為「按下按鈕囉!」的動作,要監聽的對象則是按鈕。
html 中我們這樣寫:
<button id = "btnChange">按鈕</button>
JS 中我們這樣寫:
let btnChange = document.getElementById("btnChange");
function change(){
btnChange.innerHTML = "按下按鈕囉!";
}
btnChange.addEventListener("click",change);
要監聽哪個東西寫最前面,要監聽怎樣的動作,必須用雙引號包住。而逗號後面放的,則是要做什麼對應的函式。
但接下來你可能會勤奮不倦,用 getElementsByClassName 依樣畫葫蘆寫一次,然後就發現...完了,怎樣都是錯誤訊息(崩潰)。這是因為,你的 HTML 中可能有好幾個同樣的 class 名稱,這些會通通被放進陣列裡,你可以這樣改寫程式碼:
let btn = document.getElementsByClassName("btn")[0]; //加[0]選擇第一個DOM元素
function change(){
btn.innerHTML = "按下按鈕囉!";
}
btn.addEventListener("click",change);
當然也可以使用 forEach 來幫每個元素都加上監聽!使用 document.querySelectorAll 選取所有 class 也會遇到同樣的狀況,解決方法也是一樣的。
以上就是我們今天的學習,是不是很簡單呢?
JS 學徒特訓班教學影片及練習題 14 關
事件監聽跟 getElementsByClassName: https://hovertree.com/h/bjaf/e4osnper.htm
HTML DOM getElementsByClassName() 方法: https://www.runoob.com/jsref/met-element-getelementsbyclassname.html