事件監聽優化篇 - 從父元素來監聽子元素內容

「從父元素來監聽子元素內容」是一種更好的事件監聽方式

我現在要做的功能是:

  • 點擊<li></li>後,會顯示「<li></li>裡面的文字內容」
  • 如果是點擊「<li></li>以外的東西」,就不會顯示任何內容

我的 jsbin 範例

https://jsbin.com/jivelig/edit?html,output

html:

1
2
3
4
<ul class="list">
<li>查理</li>
<li>卡斯伯</li>
</ul>

css:

1
2
3
4
.list{
padding: 50px;
border: 10px solid #000;
}

❌ JS 錯誤寫法

🎃 建立變數list,用.querySelector來選取「.list li

🎃 在.list li做事件監聽–> 點擊.list li,就會執行「checkName 這個 function」

🎃 「checkName 這個 function」會印出「文字內容 e.target.textContent

JS:

1
2
3
4
5
6
7
8
// 點擊<li></li>後,會顯示「<li></li>裡面的文字內容」
var list = document.querySelector('.list li');

function checkName(e){
console.log(e.target.textContent);
};

list.addEventListener('click',checkName,false);

用開發者工具來看:

  • 點擊「第一個<li></li>」(查理),的確會在 Console 出現「查理」

😢 但是,點擊「第二個<li></li>」(卡斯伯),在 Console 就不會出現「卡斯伯」

原因為:

console.log(list);來看「變數list」選取到的東西,會發現–>
‼️ document.querySelector('.list li'); 只有選取到「第一個<li></li>」而已

🎃 .querySelector的特性:只會選取到「第一筆資料」

在「<ul></ul>」裡面,有很多個<li></li>,如果我希望,可以選取到「每一個<li></li>」來做事件監聽,我該怎麼做呢?

作法如下:

監聽父元素

💡 監聽父元素<ul></ul>–> 透過e.target,就可以去找出「父元素裡面的各種資訊」

💡 e.target - 了解目前點擊的是哪個元素

🎃 建立變數list,用.querySelector來選取「.list

🎃 在「父元素.list」做事件監聽–> 點擊.list,就會執行「checkName 這個 function」

🎃 「checkName 這個 function」會印出「e.target

1
2
3
4
5
6
7
8
// 點擊<li></li>後,會顯示「<li></li>裡面的文字內容」
var list = document.querySelector('.list');

function checkName(e){
console.log(e.target);
};

list.addEventListener('click',checkName,false);

用開發者工具來看

🍋 我點擊「周圍區域」,e.target就是「<ul class="list">...</ul>

🍋 我點擊「第一個<li></li>」,e.target就是「<li>查理</li>

🍋 我點擊「第二個<li></li>」,e.target就是「<li>卡斯伯</li>

🍋 就算我是「動態新增 html」,e.target也可以選取到「我點擊的元素」:

  • 在「<li>卡斯伯</li>」按右鍵,選擇「Edit as HTML」

  • 新增一個「<li>卡斯伯2</li>

  • 我點擊「第三個<li></li>」,e.target就是「<li>卡斯伯2</li>

但是,在「父元素<ul></ul>」裡面,有很多個子元素,我不希望點擊到「父元素 or 其他的子元素」,我只想要點擊到「<li></li>

🍋 因為,我要做的功能是:點擊<li></li>後,會顯示「<li></li>裡面的文字內容」

我該怎麼做呢?

1️⃣ 用e.target.nodeName來確認「我點擊的東西是哪個節點」

🎃 用e.target.nodeName來確認「我點擊的東西是哪個節點」

🍋 先確認:我點擊的東西是不是「<li></li>

1
2
3
4
5
6
7
8
// 點擊<li></li>後,會顯示「<li></li>裡面的文字內容」
var list = document.querySelector('.list');

function checkName(e){
console.log(e.target.nodeName);
};

list.addEventListener('click',checkName,false);

用開發者工具看:
🍋 我點擊「查理」,Console 就會出現「LI」,代表我點擊的是<li></li>

2️⃣ 秀出<li></li>的文字內容

❌ JS 錯誤寫法

💡 e.target.textContent代表「點擊目標的文字內容」

🎃 我在function checkName(e){}裡面寫「console.log(e.target.textContent);

1
2
3
4
5
6
7
8
9
// 點擊<li></li>後,會顯示「<li></li>裡面的文字內容」
var list = document.querySelector('.list');

function checkName(e){
console.log(e.target.textContent);
// console.log(e.target.nodeName);
};

list.addEventListener('click',checkName,false);

用開發者工具看

  • 如果我點擊的是「<li></li>」,的確會出現「<li></li>的文字內容」

😢 但是,如果我點擊的是「<ul></ul>」,就會出現這種奇怪的狀態:

✅ 我希望「只有點擊<li></li>」時,才會出現「<li></li>的文字內容」。在點擊其他元素時,都不會出現任何內容。

該怎麼做呢?

3️⃣ 用e.target.nodeName去篩選元素

🎃 用if做判斷–> 當e.target.nodeName「不等於'LI'」時,就用return「回傳一個空值」

🍋 在return後面「不寫任何東西」,就代表「回傳一個空值」

🍋 只要執行了return,後面的程式碼console.log(e.target.textContent);就不會執行了

JS:

1
2
3
4
5
6
7
8
9
10
// 點擊<li></li>後,會顯示「<li></li>裡面的文字內容」
var list = document.querySelector('.list');

function checkName(e){

if(e.target.nodeName !== 'LI'){return};
console.log(e.target.textContent);
};

list.addEventListener('click',checkName,false);

用開發者工具看:

🍋 點擊「<li></li>以外的東西」–> 不等於li–> 執行return–> 後面的程式碼console.log(e.target.textContent);不會再繼續執行

–> Console 不會出現任何內容

return搭配 function 的特性

🎃 return除了可以「回傳值」之外,return也可以做為「function 的中斷點」

🍋 在 function 裡面:一旦執行了return,在return後面的程式碼就不會繼續執行了

1
2
3
4
5
function checkName(e){

if(e.target.nodeName !== 'LI'){return};
console.log(e.target.textContent);
};

JS 這樣寫的好處

✅ 因為我監聽的是「父元素<ul></ul>」,我可以用「if判斷式」,去篩選出「父元素中,我想要選取的元素」

❌ 傳統的做法是「每個元素都綁定“監聽事件.addEventListener”」–> 效能會比較低落,因此不建議這麼做

1
2
3
4
5
6
7
8
9
10
// 點擊<li></li>後,會顯示「<li></li>裡面的文字內容」
var list = document.querySelector('.list');

function checkName(e){

if(e.target.nodeName !== 'LI'){return};
console.log(e.target.textContent);
};

list.addEventListener('click',checkName,false);