0%

JS核心-第四章筆記

JS是一個「事件驅動」的程式語言,必須等到事件被觸發後,才會去執行相對應的內容。

事件機制的原理

事件觸發->去做相對應的內容

事件流程

網頁元素接收事件的順序
分別有以下兩種:
Event Capturing: 事件捕獲 -從document,由上往下傳遞,直到找到啟動元素為止。
Event Bubbling: 事件冒泡 - 從觸發事件的目標元素,由下往上傳遞,直到找到document為止。

那在addEventListener的事件監聽的第三個參數,true代表捕獲,false代表冒泡。

事件傳遞

我覺得在事件流程中的捕獲和冒泡的機制,我最不習慣的是事件會傳遞的部分。阿不就點到誰就該由誰跳出來嗎(誤XDD)
在JS中,事件會因為捕獲和冒泡的機制而往上傳遞 或 往下傳遞,那這類的應用在本章的最後面的事件指派的部分,
你就會感受到事件傳遞的麻煩,阿不是,是事件傳遞的威力囉~~

事件的註冊綁定

on-event(HTML屬性)

直接在html文件中,直接在目標元素上的html標籤綁定觸發事件。
ex:

1
2
---HTML---
<button onclick="console.log('Hello')">按下去</button>

on-event(非HTML屬性)

ex:

1
2
3
4
5
6
7
8
9
10
---HTML---
<button>按鈕</button>

---JavaScript---
let el = document.querySelector('button');
el.onclick = function(){
console.log('Hello');
}

el.onclick = null; // 將該事件指向null就可以解除事件綁定

addEventListener 事件監聽

用這種方式註冊事件的好處是可以同時對同一個元素綁定多個事件。
像下面這個範例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
---HTML---
<button class="btn">按鈕</button>

---JavaScript---
let el = document.querySelector('.btn');
let clickHandle = function(){
console.log('HI');
}
let clickHandle2 = function(){
console.log('Hello');
}
el.addEventListener('click',clickHandle,false);
el.addEventListener('click',clickHandle2,false);

el.removeEventListener('click', clickHandle); // 以此種方法來解除事件綁定

網頁事件

網頁介面事件

onload事件

它是註冊在windo物件上,這個事件的意思是網頁內的資源全數載入之後,才會觸發onload事件裡面的內容。
在我做過的專案檔中,像是口罩地圖,就會希望在圖資在入完成後,觸發後續的內容,故我們就會對這個圖資使用onload事件喔~

error事件

書中提到了這個我不太常用的事件,但是,我覺得還蠻好用的。
作者提到,這個是一個很適合用on-event 的方式做註冊的事件,也就是直接寫在該物件的HTML標籤上。
使用時機建議在img元件,當圖片在入失敗後,就會觸發綁定的onerror事件,跳出圖片載入應該呈現的圖片,
好讓使用者知道,這張圖片載入失敗了。

書中也特別註明了,如果你將這個圖片的onerror寫在onload事件裡面的話是會沒有任何效果的喔,因為,圖片的錯誤已經發生了,
等到全部的網頁資源都入完成後,才將onerror事件掛在在那的圖片元件上的話,就沒有任何意義了。

隱藏在事件中的event

當我們利用addEventListener去註冊一個事件的時候,Event Listener會去註冊一個「事件物件」,而這個事件物件會包含與這個
事件所有相關的內容,而這個「事件物件」就是隱藏在事件中的event,他會以參數的方式傳入你所註冊的addEventListener,此時,
你就可以透過它來觀察觸發元素的種種行為或內容。

target

這個功能我自己蠻常用到的,在我自己的部落格中的這一篇有介紹到e.target的使用方式,
我們時常會透過它來得到觸發該事件元素的內容文字阿 或是 它的標籤屬性等等的內容,還蠻好用的。

阻止事件冒泡event.stopPropagation()

這邊的話,書中有提到一個蠻實用的例子。
在一般的labelinput的組合會像下面醬:

1
2
<label for="xxx">Output</label>
<input type="checkbox" name="ckbox" id="xxx">

但是,有時候會因為排版的問題我們會將上面的排版改成下面這種寫法

1
2
3
<label class="lbl">
Ouput1 <input type="checkbox" name="chbox" class="ckbox">
</label>

功能完全一樣,但是,排版的部分我個人是覺得下面的寫法label和checkbox高度部分會比較對齊。
但是,用下面這種寫法會有事件冒泡的問題,也就是當我們按下label的時候,checkbox會因為事件冒泡的關係,
連帶的也觸發了click事件。
那我們想要防止這種觸發兩次label的click的狀況,我們就需要使用到stopPropagation囉。

以上這個範例,就可以看到,我們是在checkbox上加入stopPropagation喔,並不是加在label上面。
如此一來,就可以防止checkbox再次觸發了label的click事件囉。

阻擋預設行為 event.preventDefault()

在網頁中的a連結 和 form表單的提交的預設行為,我們就可以利用event.preventDefault()來取消這些預設行為。

1
2
3
4
5
6
7
8
9
---HTML---
<a id="link" href="https://www.google.com">按鈕</a>

---JavaScript---
let btn = document.querySelector('a');
btn.addEventListener('click', function(e){
e.preventDefault();
console.log('Google');
}, false);

以上這個範例,我們在a連結的點擊事件中加入了event.preventDefault()就可以取消原本會跳轉到Google頁面的效果囉~~

不要搞混event.preventDefault() 和 event.stopPropagation()

不要搞混致這兩個功能的功用喔~ 一個是用來阻止冒泡的機制 另一個是阻止該元素的預設行為的。

在事件中找回「自己」- this

書中特別提到了this 是代表e.currentTarget,也就是「觸發事件的目標元素」,
e.target是代表「觸發事件的元素」,
它們兩者會產生差別的時機是在於,當該事件會有事件冒泡的狀況的時候,他們兩個人所代表的元素是會有不一樣的時候的,
但如果沒有事件冒泡的話,他們兩個其實沒差,代表的對象都是一樣的。

事件指派 - 監聽父元素

在這邊書中有提到了一個實用的例子,也是我在專案中常用的方法。
若今天在ul中,有多個li子元素,我們想要為這些li子元素都綁定click事件,那這個時機,我們就可以利用監聽父元素ul,
接著,利用事件冒泡的事件傳遞機制來為這些子元素綁上click事件。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
---HTML---
<ul class="list-group">
<li class="list-group-item">1</li>
<li class="list-group-item">2</li>
<li class="list-group-item">3</li>
</ul>

---JavaScript---
let list = document.querySelector('.list-group');

list.addEventListener('click' function(e){
if(e.target && e.target.nodeName.toLowerCase === 'li') {
console.log(e.target.textContent);
}
})

以上這個範例,我們將click的監聽事件綁定在父元素ul上面,當我們點擊了ul父元素,接著,等待事件從它的子元素里冒泡上来,
並可以知道這個事件是從哪個元素觸發的,如此一來,就達成為所有子元素綁定了click事件的目的囉。

解釋事件傳遞的文章
解釋事件指派的文章