DOM事件和事件委托
【前言】
在网页端、移动端H5、小程序等各个终端环境的前端开发中随处可见事件的运用,可见事件机制的是前端这一块的重中之重。
【目录】
一、从实例看事件传递
二、事件传递
三、事件添加
四、取消冒泡
五、事件委托
六、封装事件委托
七、自定义事件
【正文】
一、从实例看事件传递
以下面这个普通的html文件为例
<!DOCTYPE html><html lang="en" onclick="handleClickHtml()">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body onclick="handleBodyClick()">
<div id="div1" onclick="handleClick2()">
<button id="button1" onclick="handleClick1()">handleClick1</button>
</div>
<script>
function handleClick1(e) {
console.log('click1')
}
function handleClick2(e) {
console.log('click2')
}
function handleBodyClick(e) {
console.log('body clicked')
}
function handleClickHtml(e) {
console.log('html clicked')
}
document.addEventListener('click', e => {
console.log('document clicked')
})
</script>
</body>
</html>
例子中给document、html、body、div、button都添加了点击事件,其执行结果如下:
二、事件传递
事件产生后,从window对象自上而下向目标节点传递,抵达目标节点后会沿着相反方向传递
事件捕获:由内向外找监听函数
事件冒泡:由外向内找监听函数
W3C为了同时让IE和景文满意,只能规定浏览器应该同时支持两种调用方式
三、事件添加
- 事件绑定API
- IE5:X.attachEvent('onclick',fn)//冒泡
- 网景:X.addEventListener('cilck',fn)//捕获
- W3C:X.addEventListener('cilck',fn,bool)
- 如果bool不传或为falsy,
就让fn走冒泡
- 如果booltrue,
就让fn走捕获
四、取消冒泡
看下面这个例子,可以通过stopPropagation来阻止事件继续往上冒泡,window、document、html上添加的点击事件均不会生效
<!-- 省略了部分代码 --><div id="div1">
<button id="button1">button</button>
</div>
<script>
var div1 = document.getElementById('div1')
var button1 = document.getElementById('button1')
div1.addEventListener('click', (e) => {
console.log(e.currentTarget)
}, false)
button1.addEventListener('click', (e) => {
console.log(e.currentTarget)
e.stopPropagation() // 关键代码:阻止了click事件继续往上冒泡
}, false)
// 以下是监听html、document的点击事件
function handleClickHtml(e) {
console.log('html clicked')
}
document.addEventListener('click', e => {
console.log('document clicked')
})
</script>
点击button的打印结果如下:
五、事件委托
大批量事件监听性能问题考虑以下场景:
有个一个长消息列表实时接受新的消息,滚动到底部时加载更多消息,点击消息可进入回话窗口页面,消息左滑消息此条消息
问题:
如何给此消息列表添加左滑事件?
抽象来看这个问题就是如何高效的给大批量(甚至是无限量)节点添加事件,这里暂时不考虑vue、react等框架
性能问题原因
每注册一个事件监听监听都需要使用一定内存,同时在dom节点多了之后事件经过的捕获、冒泡阶段要处理事件传递也就慢了
事件委托
父节点嵌套的子节点的触发的事件会通过事件冒泡到达父节点,事件处理程序不直接绑定到子节点,统一由公共父节点进行事件监听。
e.target表示触发事件的元素,通过e.target可以判断具体响应那个元素的事件
e.currentTarget表示事件绑定的元素,在事件委托情况下指向同一个元素
采用事件委托改造本文例子
<!-- 省略了部分代码 --><div id="div1">
<button id="button1">button</button>
</div>
<script>
document.addEventListener('click', e => {
// e.target:触发事件的元素
// e.currentTarget:事件监听器绑定的元素
// console.log(e.target, e.currentTarget)
if (e.target.id === 'button1') {
return console.log('button1 clicked')
}
if (e.target.id === 'div1') {
return console.log('div1 clicked')
}
if (e.target.tagName === 'HTML') {
return console.log('html clicked')
}
if (e.target.tagName === 'BODY') {
return cosnole.log('body clicked')
}
})
</script>
依次点击html、div1、button1会依次打印结果:
六、封装事件委托
//部分代码on('click', '#div', 'button',() => {
console.log('button 被点击了')
})
functionon(eventType, element, selector, fn) {
if(!(element instanceof Elemnet) {
element = document.querySelector(element)
}
element.addEventListener(eventType, e => {
let el = e.target
while (!el.matches(selector)){
if (element === el) {
el = null
break
}
el = el.parentNode
}
el && fn.call(el, e, el)
})
return element
}
自定义事件
以上是 DOM事件和事件委托 的全部内容, 来源链接: utcz.com/a/32704.html