我们知道,在 HTML 中为一个元素绑定事件,需要对这个元素添加事件监听,如 onclick
属性或者 addEventListener()
。
1 2 3 4 5 6 7 8
| <ul id="list"> <li id="item-1" onclick="console.log('I am Item 1)">Item 1</li> <li id="item-2" onclick="console.log('I am Item 2)">Item 2</li> <li id="item-3" onclick="console.log('I am Item 3)">Item 3</li> <li id="item-4" onclick="console.log('I am Item 4)">Item 4</li> <li id="item-5" onclick="console.log('I am Item 5)">Item 5</li> <li id="item-6" onclick="console.log('I am Item 6)">Item 6</li> </ul>
|
1 2 3 4 5 6
| document.querySelector('#item-1').addEventListener('click', () => { console.log('I am Item 1') }) document.querySelector('#item-2').addEventListener('click', () => { console.log('I am Item 2') }) document.querySelector('#item-3').addEventListener('click', () => { console.log('I am Item 3') }) document.querySelector('#item-4').addEventListener('click', () => { console.log('I am Item 4') }) document.querySelector('#item-5').addEventListener('click', () => { console.log('I am Item 5') }) document.querySelector('#item-6').addEventListener('click', () => { console.log('I am Item 6') })
|
这样看起来实在是太笨啦!并且在动态增减列表元素的场景下,它无法及时地添加或移除事件监听。怎么让事情变得更简单些呢?那就是使用事件委托来进行监听:
1 2 3 4 5
| document.querySelector('#list').addEventListener('click', (e) => { if (e.target.tagName === 'LI') { console.log(`I am ${e.target.textContent}`) } })
|
代码是不是立马就变得清爽起来~我们只用一个事件监听就实现在每个 <li>
元素点击时都能执行某些操作。
但现实中,我们的页面结构往往不会这么简单,需要产生交互的元素里面可能包含了很多子元素,例如图标、被其他标签包裹的文本等,这使得 event.target
不一定是目标元素。这个时候我们就得对当前点击的元素逐层向上寻找父元素,若找到目标元素,则说明我们需要执行对应的操作。所以,我们需要写一个更通用的事件委托:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| function delegate(eventType, targetSelector, fn) { document.addEventListener(eventType, (e) => { let currentEl = e.target; while(!currentEl.matches(targetSelector)) { if(currentEl === document.documentElement) { currentEl = null; break; } currentEl = currentEl.parentElement; } if(currentEl) { fn(currentEl); } }) }
delegate('click', 'li', () => { ... })
|
最后,你可以到这里来 Try And Code 一下。