目录
第一章 理解addEventListener与removeEventListener
第三章 解决addEventListener多次触发,removeEventListener不生效问题
第一章 理解addEventListener与removeEventListener
1.1 addEventListener的理解
1.1.1 语法
element.addEventListener(event, function, boolean)
1.1.2 参数解释
- element:获取的标签元素
- event:指定事件名(注意不需要使用on,例如,使用 "click" ,而不是使用 "onclick")
-- 常见的事件名
- click 单击鼠标左键触发
- dblclick 双击鼠标左键触发
- mousedown 单击任意一个鼠标按钮时触发
- mouseup 松开任意一个鼠标按钮时触发
- mousemove 鼠标在某个元素上移动时触发
- mouseenter 鼠标移入标签时触发
- mouseleave 鼠标移入标签是触发
……
- function:指定要事件触发时执行的函数(注意:默认事件对象event会作为第一个参数传入函数)
- boolean:指定是事件捕获还是事件冒泡(默认为false,false --> 事件冒泡,true--> 事件捕获)
1.1.3 使用方法
- (注意:小编用的项目里的代码做的demo,为大家提供思路)
const bookListFileUplodDom = this.$refs[`upload-book-list-file-${id}`] // 这里是获取dom标签
bookListFileUplodDom.addEventListener('change', function (e) { // 添加监听事件
connsole.log(e)
}, false)
1.2 removeEventListener的理解
1.2.1 语法
element.removeEventListener(event, function, boolean)
1.2.2 使用说明
const bookListFileUplodDom = this.$refs[`upload-book-list-file-${id}`] // 这里是获取dom标签
bookListFileUplodDom.removeEventListener('change', function (e) { // 移除监听事件
connsole.log(e)
}, false)
第二章 事件捕获与事件冒泡
2.1 理解事件捕获
- 顾名思义:捕获—— 一个刑事案件,警察首先从表面的线索开始,然后一点一点的到向更深层次的去了解罪案人,最终抓到作案人,这个过程就是捕获。那好,事件捕获的原理类比的看,dom事件由外向里逐级传递的过程
<div class="father" style="width: 200px;height: 100px;background-color: red;margin: auto;">
father
<div class="son" style="width: 100px;height: 50px;background-color: skyblue;">
son
</div>
</div>
<script>
/*
事件捕获
*/
const son = document.querySelector('.son');
// 给son注册单击事件
son.addEventListener('click', function (event) {
alert('son');
}, true);
// 给father注册单击事件
const father = document.querySelector('.father');
father.addEventListener('click', function (event) {
alert('father');
}, true);
// 给document注册单击事件,省略第3个参数
document.addEventListener('click', function () {
alert('document');
}, true)
</script>
- 展示效果: 通过点击最里层的son标签,会从最外层的dom逐层遍历进去
- 实现顺序: document -> html ->body ->father ->son
2.2 理解事件冒泡
- 顾名思义:冒泡—— 一桶水,让我们烧开时,会有泡泡从桶的底部慢慢的浮向表面,这个过程就是冒泡。那好,事件冒泡的原理也是如此,dom事件由里向外逐级传递的过程
- 例子:
<div class="father" style="width: 200px;height: 100px;background-color: red;margin: auto;">
father
<div class="son" style="width: 100px;height: 50px;background-color: skyblue;">
son
</div>
</div>
<script>
/*
冒泡阶段 如果addEventListener 第三个参数是 false 或者 省略
如果参数为true,则为捕获阶段
*/
const son = document.querySelector('.son');
// 给son注册单击事件
son.addEventListener('click', function (event) {
alert('son');
}, false);
// 给father注册单击事件
const father = document.querySelector('.father');
father.addEventListener('click', function (event) {
alert('father');
}, false);
// 给document注册单击事件,省略第3个参数
document.addEventListener('click', function () {
alert('document');
})
</script>
- 展示效果: 通过点击最里层的son标签,会从最里层的son标签逐层遍历出来
- 实现顺序:son -> father ->body -> html -> document,逐层传递
2.3 解决事件冒泡的两种方法
2.3.1 方法一:阻止事件冒泡
- event.stopPropagation()
<div class="father" style="width: 200px;height: 100px;background-color: red;margin: auto;">
father
<div class="son" style="width: 100px;height: 50px;background-color: skyblue;">
son
</div>
</div>
<script>
const son = document.querySelector('.son');
// 给son注册单击事件
son.addEventListener('click', function (event) {
// 添加阻止事件冒泡
event.stopPropagation()
alert('son');
}, false);
// 给father注册单击事件
const father = document.querySelector('.father');
father.addEventListener('click', function (event) {
alert('father');
}, false);
// 给document注册单击事件,省略第3个参数
document.addEventListener('click', function () {
alert('document');
})
</script>
- 效果展示
- 描述,在son标签配置阻止冒泡事件之后,我们点击son标签,就不会再出现会逐层执行点击事件冒泡效果了
2.3.2 方法二:事件委托
- 什么是事件委托:对“事件处理程序过多”问题的解决方案就是事件委托。事件委托利用了冒泡,只指定一个事件处理程序,就可以管理某一类型的所有事件。(比如父标签与子标签都要执行相同的代码,这个时候我们只需要在父标签上绑定一个方法就好了,不需要在子标签上也绑定一个相同的方法)
<div class="father" style="width: 200px;height: 100px;background-color: red;margin: auto;">
father
<div class="son" style="width: 100px;height: 50px;background-color: skyblue;">
son
</div>
</div>
<script>
// 给father注册单击事件
const father = document.querySelector('.father');
father.addEventListener('click', function (event) {
event.stopPropagation();
alert('son/father');
}, false);
// 给document注册单击事件,省略第3个参数
document.addEventListener('click', function () {
alert('document');
})
</script>
- 效果展示:
- 描述:不管是点击son标签还是点击father标签,他们执行的方法都是相同的
第三章 解决addEventListener多次触发,removeEventListener不生效问题
3.1 项目bug
- 项目情况:有地方触发多次执行添加监听的代码,我需要做的是每次添加监听在完成逻辑之后马上移除对应的监听,从而防止调查一次函数时,多个监听执行逻辑
- bug效果图:
- 描述:小编每次点击导入,会添加一个监听事件, 当用户点击打开上传文件成功,数据变化,执行代码逻辑,但是当小编在不停的取消导入或者多次导入的时候,我们可以发现在数据发生变化的时候我们点击了多少次,就执行多少次逻辑,说明只是添加addEventListener监听事件,并没有及时removeEventListener移除对应的监听事件,而且小编踩过的坑:设置了移除监听事件removeEventListener,依然还是这种效果的情况,以下就是小编的具体实现方法:
3.2 处理过程
- 第一次尝试:
const bookListFileUplodDom = this.$refs[`upload-book-list-file-${id}`] // 这里是获取dom标签
// 配置效果无效
bookListFileUplodDom.addEventListener('change', function (e) { // 移除监听事件
connsole.log(e)
}, false)
bookListFileUplodDom.removeEventListener('change', function (e) { // 移除监听事件
connsole.log(e)
}, false)
结果:这种方法监听的事件看起来和移除的事件是一样的,但其实这两个方法并不相同,这就涉及到数据类型、堆与栈的知识了(小编说明一下:函数是引用数据类型,在js中也算是特殊的对象,它会被存储在堆中,而这种方法相当于在堆中开了两个内存分别存储这两个对象,从而导致他们不是同一事件,最终导致移除监听事件没有效果),他们的内存是不共用的,这点在使用时一定要注意,这次尝试也让我们知道了,要将两者搭配使用,首先要确保添加与移除引用的是同一内存里的监听函数
- 第二次尝试:
const bookListFileUplodDom = this.$refs[`upload-book-list-file-${id}`] // 这里是获取dom标签
function handler (e) { // 移除监听事件
connsole.log(e)
}
// 配置有效果(但是不能满足小编每次执行完成就移除的需求)
bookListFileUplodDom.addEventListener('change', handler, false)
bookListFileUplodDom.removeEventListener('change', handler, false)
结果:这种处理是先addEventListener再removeEventListener是可以把刚刚add的remove掉的,但是他不符合小编的要求,小编需要把上一次add的remove掉,再重新add,这样就不会触发多次执行。问题就在这:再执行一次时,这个方法也重新声明了,上一次add的function和这次remove的不是同一个function,他们的内存不同,所以小编又重新调整了下代码:
3.3 最终处理
<script>
let bookListHandler = null // 定义一个自变量的保存上一次的监听函数(移除监听函数使用)
export default {
methods: {
toImportBookList (id) {
const bookListFileUplodDom = this.$refs[`upload-book-list-file-${id}`]
bookListFileUplodDom.removeEventListener('change', bookListHandler, false) // 每次调用函数执行移除上一次的添加的监听
bookListFileUplodDom.click()
},
importBookList (id) {
const _this = this
const bookListFileUplodDom = this.$refs[`upload-book-list-file-${id}`]
bookListHandler = function (e) { // 将执行的监听函数赋值
console.log(e)
}
bookListFileUplodDom.addEventListener('change', bookListHandler, false) // 添加监听事件
},
}
}
</script>
- bug得到解决,成功!
3.4 总结
最后,说了这么多,特别重要的是要知道,addEventListener和removeEventListener使用时操作的一定要是同一个function(同一内存)。 还需要知道的知识有JavaScript的数据类型、堆、栈,最后也能了解到深浅克隆了。