事件委托就是利用事件冒泡,只指定一个事件处理程序,就可以管理某一类型的所有事件
我委托一个元素帮我监听我本该监听的东西,比如onclick
<ul id="box">
<li>1</li>
<li>2</li>
<li>3</li>
<li>4</li>
</ul>
1.常规思路利用for循环的机制添加
window.onload = function(){
var box = document.getElementById("box");
var li = box.getElementsByTagName('li');
for(var i=0;i<li.length;i++){
li[i].onclick = function(){
alert(123);
}
}
}
如果li标签有100个,那就要循环遍历100次,这样操作DOM次数太多 极大的消耗性能
2.利用事件委托来添加事件
// 监听父元素 ul#box
box.addEventListener('click', (e)=> {
//通过浏览器传进来的e参数,找到当前点击元素
const t = e.target
// 判断当前元素是不是Li标签
if(t.matches('li') {
console.log('用户点击了li')
}
})
- 首先监听父元素,
- 然后根据浏览器传进去的事件信息,拿到当前点击元素,
- 再判断当前点击元素是不是 li 元素, 如果是,就 console.log('用户点击 li 标签')
然后我们可以封装一个事件委托函数
on("click", "#box", "li", () => {
console.log("用户点击了li");
});
function on(eventType, parentElement, selector, fn) {
// 先判断是不是element,
//如果传进来的是选择器,不是element本身,就先变成element,
// 因为只有element才能监听事件
if (!(parentElement instanceof Element)) {
parentElement = parentElement.querySelectorAll(parentElement);
}
parentElement.addEventListener(eventType, (e) => {
let target = e.target;
if (target.matches(selector)) {
fn(e);
}
});
}
但是以上这种实现有一个小问题,那就是如果被点击元素有多个父元素怎么办?
<ul id="box">
<li>
<p>
<span>1</span>
</p>
</li>
<li>
<p>
<span>2</span>
</p>
</li>
<li>
<p>
<span>3</span>
</p>
</li>
<li>
<p>
<span>4</span>
</p>
</li>
</ul>
复制代码
我们需要做的就是:
递归地向上多找几层父节点,直到找到 li 标签,
同时还必须限定,寻找的范围不能超过 parentElement,
拿上面的例子来说,不可以越过 ul 标签,去找 body 标签
on("click", "#box", "li", () => {
console.log("用户点击了li");
});
function on(eventType, element, selector, fn) {
if (!(element instanceof Element)) {
element = document.querySelectorAll(element);
}
element.addEventListener(eventType, (e) => {
let target = e.target;
// 如果匹配到了selector就跳出循环
while (!target.matches(selector)) {
if (target === element) {
//已经找到了父元素,说明还没找到,就设置为null
target = null;
break;
}
target = target.parentNode;
}
// 找到了target, 就调用函数
target && fn.call(target, e);
});
}