提出问题
如果一个列表(ul)中的的每一个子项(li)都有同样的点击事件你会如何编写代码呢?
- 每一个li都添加点击事件
<ul class="list">
<li class="list-item">我在等世间唯一契合的灵魂</li>
<li class="list-item">能哭的地方只有厕所和爸爸的怀里</li>
<li class="list-item">你将不再是工具,而是名副其实的人</li>
<li class="list-item">我为何在哭泣</li>
<li class="list-item">如果这一切是梦就好了</li>
<li class="list-item">那女孩对我说</li>
<li class="list-item">我是否走进了你的心房?没脱鞋就进来了呢!</li>
<li class="list-item">前天是小鹿,昨天是小兔子,今天是你。 </li>
</ul>
<script>
const lis = document.querySelectorAll('.list-item')
lis.forEach(li => {
li.addEventListener('click', () => {
console.log(li.innerHTML) // 点击li,控制台会输出相应的句子
})
})
- 在li 的父容器ul 上添加点击事件
利用事件的冒泡,点击li,事件会沿着DOM 树向上冒泡到document/window
// 页面结构同上
const ul = document.querySelector('.list')
ul.addEventListener('click', (e) => {
console.log(e.target.innerHTML) // 点击li,控制台会输出相应的句子
})
问题升级
如果列表的子节点更加复杂呢?我只想点击第二个span(.words) 会在控制台输出相应句子。
<ul class="list">
<li class="list-item">
<span>晚婚</span>
<span class="words">我在等世间唯一契合的灵魂</span>
</li>
<li class="list-item">
<span>CLANNAD</span>
<span class="words">能哭的地方只有厕所和爸爸的怀里</span>
</li>
<li class="list-item">
<span>紫罗兰的永恒花园</span>
<span class="words">你将不再是工具,而是名副其实的人</span>
</li>
<li class="list-item">
<span>紫罗兰的永恒花园</span>
<span class="words">我为何在哭泣</span>
</li>
<li class="list-item">
<span>Lemon</span>
<span class="words">如果这一切是梦就好了</span>
</li>
<li class="list-item">
<span>那女孩对我说</span>
<span class="words">那女孩对我说</span>
</li>
<li class="list-item">
<span>四月是你的谎言</span>
<span class="words">我是否走进了你的心房?没脱鞋就进来了呢!</span>
</li>
<li class="list-item">
<span>CLANNAD</span>
<span class="words">前天是小鹿,昨天是小兔子,今天是你。</span>
</li>
</ul>
基本思路
通过事件对象的一些属性和目标元素的一些特征来找到目标元素
- 利用event 对象中的path 属性(path 有些浏览器不兼容)
const ul = document.querySelector('.list')
ul.addEventListener('click', (e) => {
// console.log(e.path)
const path = e.path
path.forEach(el => {
// 根据目标元素的特有的东西来判断是否是目标元素,这里特有的是类名为words
// 判断是否为元素节点,如果是,再查看是否存在class 属性
if (el.nodeType === 1 && el.hasAttribute('class')) {
const className = el.getAttribute('class')
// 类名为words 就是目标元素
if (className === 'words') {
console.log(el.innerHTML)
}
}
})
})
- 使用event.target来确定目标元素
const ul = document.querySelector('.list')
ul.addEventListener('click', (e) => {
const target = e.target
// console.log(target)
if (target.nodeType === 1 && target.hasAttribute('class')) {
const className = target.getAttribute('class')
// 类名为words 就是目标元素
if (className === 'words') {
console.log(target.innerHTML)
}
}
})
为什么需要减少绑定事件的个数
绑定事件需要消耗性能。如果有一个可以下拉加载更多的列表,你向每一个li 都添加一个点击事件,那么这个页面一定会越来越卡。
总结
- 添加的事件过多会影响性能,造成页面卡顿
- 可以利用事件冒泡机制来减少事件数量
- DOM结构复杂时,可以通过event 对象的path 属性来确定目标节点,然后做对应操作
- 事件对象的一些属性存在兼容性问题
- event.target是触发事件的元素,event.currentTarget是事件绑定的元素
- 低版本IE浏览器不支持addEventListener...(IE是万恶之源)