前端-捕获冒泡、代理、派发代码示例及封装
今天被实习生问到事件捕获冒泡、代理和派发的问题,举举例子讲了一番后,然后有了此文 作一个记录
目录大纲
- 捕获冒泡示例
- 事件代理示例
- 事件派发示例
- 原理介绍:发布订阅者模式
- 事件派发,完整流程的封装(发布订阅者模式)
1. 捕获冒泡示例
事件监听api:addEventListener,有第三个参数,isCapture,意思是:是否捕获,默认false 冒泡
html部分
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>事件冒泡</title>
</head>
<body>
<div>
<p id="parEle">
我是父元素
<span id="sonEle">
---我是子元素---
</span>
</p>
</div>
</body>
</html>
script部分
- 测试例子1:(全冒泡,先子后父)(addEventListener第三个参数不写,默认冒泡)
<script type="text/javascript"> var sonEle = document.getElementById('sonEle'); var parEle = document.getElementById('parEle'); // 测试例子1:(全冒泡,先子后父) parEle.addEventListener('click', function () { console.log('父父父父父父级111111111'); }); sonEle.addEventListener('click', function (e) { console.log('子级111'); // e.stopPropagation() // 阻止冒泡,可以解开注释 调试看看 }); parEle.addEventListener('click', function () { console.log('父父父父父父级'); }); sonEle.addEventListener('click', function () { console.log('子级'); }); </script> 点击“---我是子元素---”,打印: 子级111 子级 父父父父父父级111111111 父父父父父父级 点击“我是父元素”,打印: 父父父父父父级111111111 父父父父父父级
- 测试例子2:(全捕获,先父后子)(addEventListener第三个参数写 true)
<script type="text/javascript"> var sonEle = document.getElementById('sonEle'); var parEle = document.getElementById('parEle'); // 测试例子2:(全捕获,先父后子) parEle.addEventListener('click', function (e) { console.log('父父父父父父级111111111'); // e.stopPropagation() // 阻止捕获,可以解开注释 调试看看 }, true); sonEle.addEventListener('click', function () { console.log('子级111'); }, true); parEle.addEventListener('click', function () { console.log('父父父父父父级'); }, true); sonEle.addEventListener('click', function () { console.log('子级'); }, true); </script> 点击“---我是子元素---”,打印: 父父父父父父级111111111 父父父父父父级 子级111 子级 点击“我是父元素”,打印: 父父父父父父级111111111 父父父父父父级
- 测试例子3:(前2个冒泡,后2个捕获)
<script type="text/javascript"> var sonEle = document.getElementById('sonEle'); var parEle = document.getElementById('parEle'); // 测试例子3:(前2个冒泡,后2个捕获) parEle.addEventListener('click', function () { console.log('父父父父父父级111111111'); }); sonEle.addEventListener('click', function () { console.log('子级111'); }); parEle.addEventListener('click', function () { console.log('父父父父父父级'); }, true); sonEle.addEventListener('click', function () { console.log('子级'); }, true); </script> 点击“---我是子元素---”,打印: 父父父父父父级 子级 子级111 父父父父父父级111111111 点击“我是父元素”,打印: 父父父父父父级 父父父父父父级111111111
2. 事件代理示例
好处:不用一个个绑定子元素事件了,直接绑父元素,然后用event.target来判断对应的子元素
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>事件冒泡</title>
</head>
<body>
//我们有一个div元素,它包含三个按钮:
<div id="box">
<input type="button" value="按钮">
<input type="button" value="按钮2">
<input type="button" value="按钮3">
</div>
<script>
var box = document.getElementById('box');
// 好处: 不用一个个绑定子元素事件了
box.addEventListener('click', function(event) {
console.log(1)
if (event.target.tagName.toLowerCase() === 'input') {
// some code
console.log(event.target)
}
});
</script>
</body>
</html>
3. 事件派发示例
可以自定义事件new Event()。
要触发自定义事件需要用dispatchEvent 事件派发
- dom.dispatchEvent,可以派发给对应的dom
- dispatchEvent只接受Event对象(自定义事件)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>派发事件</title>
</head>
<body>
<div id="box">点我派发事件</div>
<script>
// 初始化事件 NBA
var eventNBA = new Event("NBA", {"bubbles":true, "cancelable":false});
// 订阅者1 订阅NBA
document.addEventListener('NBA', function (event) {
console.log(7227)
console.log(event)
}, false);
// 订阅者2 订阅NBA
document.addEventListener('NBA', function (event) {
console.log(234243223)
console.log(event)
}, false);
document.getElementById('box').addEventListener('click', function(event) {
document.dispatchEvent(eventNBA); // 事件派发:通知所有订阅了NBA的订阅者,触发他们的回调函数
});
</script>
</body>
</html>
1. 原理介绍:发布订阅者模式
可看我的另一篇: juejin.cn/post/695425…
2. 事件派发,完整流程的封装(发布订阅者模式)
class EventEmitter {
constructor () {
this._event = {}
}
on (dom, eventName, fn) { // 为dom绑定一个自定义event
this._event[eventName] = new Event(eventName)
dom.addEventListener(eventName, fn)
}
off (dom, eventName, fn) { // 为dom注销一个自定义event
this._event[eventName] = null
dom.removeEventListener(eventName, fn)
}
emit (dom, eventName) { // 事件派发
if (!this._event[eventName]) throw '不存在' + eventName
dom.dispatchEvent(this._event[eventName])
}
once (dom, eventName, fn) { // 事件派发一次 就注销
this.emit(dom, eventName)
this.off(dom, eventName, fn)
}
}
var myEmit = new EventEmitter()
var fn = function (e) {
console.log(e)
}
myEmit.on(document, 'okok', fn)
myEmit.off(document, 'okok', fn)
myEmit.once(document, 'okok', fn)
myEmit.emit(document, 'okok')
码字不易,点赞鼓励