重複註冊相同的 eventListener,會發生什麼事?

 事情是這樣的:

我加入了 AlphaCamp 的學期 2-1:JavaScript 前端開發課程(2020 年 11 月班),有一份作業,是要讓籃球隊計分板的計分按鈕,具備加減分的功能。

我首先利用 displayPlayerList() 函數,將每個球員的得分狀況更新到網頁上,然後再為網頁上的計分按鈕加上計分功能。觀摩其他同學繳交的作業時,我發現一份很特別的作業:有位同學把按鈕的計分功能封裝在 displayPlayerList() 的函數裡了,程式碼如下:

See the Pen 學期2-1_A21Q1: 彩子的計分版:按鈕功能 _觀摩同學作業 by Flora (@rossignol) on CodePen.

這個寫法啟發了我,讓我好奇一件事:如果我執行了許多次 displayPlayerList(),那麼,在 dataPanel 這個元素上,到底會註冊幾個 eventListener?

我猜測,如果執行了 n 次 displayPlayerList(),就會註冊 n 個 eventListener 的話,那麼,點擊一次加分鈕就會加 n 分,而點擊一次減分鈕就會扣 n 分。我著手驗證這個猜測,結果是,不管我執行幾次 displayPlayerList(),每次點擊都只會 +1 或 -1;但是每次點擊,DevTools 的 console 上跳出來的警告訊息數量,會隨著 displayPlayerList() 執行次數增加而增加。這究竟意味著什麼?執行 displayPlayerList() 的次數,跟 eventListener 註冊的數量之間到底是什麼關係?

因為完全不知道該如何自己解開謎題(我試著用「eventListener count」搜尋,跳出一些「怎麼用 eventListener 計算點擊次數」的討論串,然後我就放棄用這組關鍵字尋找答案了),所以我直接留言請問助教大神。

助教大神直接找來文件 EventTarget.addEventListener() - MDN(我這才發現,自己完全沒意識到,這種時候可以去查文件),在 Multiple identical event listeners 這個小節裡提到,針對同一個 EventTarget ,無法重複註冊相同的 EventListener;但,若 EventListener 的 handler 是匿名函式,就會重複註冊。

計分功能是用匿名函式寫的,所以重複執行 displayPlayerList(),的確會重複註冊 EventListener。至於,「註冊 n 個 eventListener 的話,那麼,點擊一次加分鈕就會加 n 分,而點擊一次減分鈕就會扣 n 分」這樣的結果為什麼沒出現,是因為點擊按鈕後,是用「parentElement.innerHTML = `${score} ${icon}`」修改計分結果,修改後,瀏覽器還沒把新的 parentElement.innerHTML 渲染出來,此時重複註冊的 EventListener 卻已經要去讀取 parentElement.innerHTML 這個節點了,所以才會讀取不到該節點,導致後續的加減分功能沒有發揮作用。

如果是用「parentElement.firstChild.textContent = `${score} `」修改計分結果,便不會出現讀取不到節點的問題,就能順利地達成「註冊 n 個 eventListener 的話,那麼,點擊一次加分鈕就會加 n 分,而點擊一次減分鈕就會扣 n 分」。

感謝助教大神,和一起寫作業的同學。

Comments

Popular posts from this blog

Alpha Camp 全端開發課程學習心得

在 javascript 用 regular expression 為金額加上千位數分隔符號

shop_platform - sqlalchemy.exc.TimeoutError