事件委托
事件委托就是利用事件冒泡,只指定一个事件处理程序,就可以管理某一类型的所有事件。
场景一:
要给100个按钮添加click
事件,咋办?
<body>
<div id="div1">
<button>click 1</button>
<button>click 2</button>
<button>click 3</button>
...
<button>click 99</button>
<button>click 100</button>
</div>
答:监听这100个按钮的祖先,等冒泡的时候判断target
是不是这100个那按钮中的一个。
div1.addEventListener('click',(e) => {
const t = e.target
if(t.tagName.toLowerCase() === "button")
console.log(t.textContent+'按钮被点击了')
})
场景二:
要监听目前不存在的元素的click
事件,咋办?
<body**>**
<div id="div1">
</div>
<script>
setTimeout(() => {
const button = document.createElement('button')
button.textContent = 'click 1'
div1.appenChild(button)
},100)
</script>
</body>
答:监听祖先,等点击的时候看看是不是我想要监听的元素即可。
div1.addEventListener('click',(e) => {
const t = e.target
if(t.tagName.tolowerCase() === "button"){ // target元素的标签名变小写后匹配
console.log('button被点击了')
}
})
从上两个场景可看出事件委托有两个优点:
1、省监听数(内存)。
2、可以监听动态元素。
根据事件委托原理和上述代码示例,可以封装事件委托。根据场景#testDiv>li
写出这样一个函数on('click', '#testDiv', 'li', fn)
,当用户点击#testDiv
里的li
元素时,调用fn
函数。即判断target
是否匹配'li'
。
示例代码1
function on(eventType, element, selector, fn) {
if(!(element instanceof Element)) {
element = document.querySelector(element)
}
element.addEventListener(eventType, (e) => {
const t = e.target
if(t.matches(selector)){
fn(e)
}
})
}
然而示例代码1只解决了target
元素与选择器元素完全相等(匹配)的情形,即
t.matches(selector)?
。当遇到下面情况上述函数就无法委托相关事件。
示例代码2
setTimeout(() => {
const button = document.createElement('button')
const span = document.createElement('span')
span.textContent = "click 1"
button.appendChild(span)
div1.appendchild(button)
},1000)
on('click', '#div1', 'button', () => {
console.log('button 被点击了')
})
在示例代码2中,selector
元素(button
)与target
元素(span
)不匹配,on
函数没有执行。所以我们需要改写一下on
函数。根据冒泡原理,从target
元素开始,按冒泡顺序遍历target
的祖先元素和选择器元素进行匹配,直到传播到委托元素范围(scope)边界,即冒泡到#div1
结束。当途中的某个祖先元素与选择器元素相匹配时,就传入匹配的祖先元素执行fn
函数。
改写的示例代码1,on
函数
function on(eventType, element, selector, fn) {
element.addEventListener(eventType, e => {
let el = e.target
while (!el.matches(selector)) { // 递归冒泡target的祖先元素
if (element === el) { // 当与selector元素匹配时
el = null
break
}
el = el.parentNode
}
el && fn.call(el, e, el) // 传入匹配的祖先元素和target元素执行fn
})
return element
},
有兴趣,可将示例代码2整合进jQuery
,实现
$('#xxx').on('click', 'li', fn)
其他内容
提问:JS支持事件吗?
答:支持,也不支持。
本简述DOM事件不属于JS的功能,属于浏览器提供的DOM功能。JS只是调用了DOM提供的addEventListener
。要让JS支持事件,可以用JS手写一个事件系统即可。
参考资料 ©转载声明