目录
- 点击事件?
- 事件的捕获和冒泡
- addEventListener
- 代码图解
- target VS currentTarget
- 事件委托
- 自定义事件
三、点击事件
以下引出一个讨论的问题
<div class='爷爷'>
<div class='爸爸'>
<div class='儿子'>
文字
</div>
</div>
</div>
<script>
/* 均添加点击事件 */
爷爷.addEventListener('click',fn);
爸爸.addEventListener('click',fn);
儿子.addEventListener('click',fn);
</script>
提问1:点击了谁,又算谁?点击了文字,算不算点击了儿子、算不算点击了爸爸、算不算点击了爷爷?
答:都算!
提问2:调用顺序? 最先调用fn爷、fn爸、fn儿中的那一个函数?
答:都行!
四、事件的捕获与冒泡
-
2002年,W3C发布标准
文档名为:DOM Level 2 Events Specification
规定浏览器应该同时支持两种调用顺序
首先
按 “爷爷 -> 爸爸 -> 儿子” 的顺序看看有没有函数监听。然后
按 “儿子 -> 爸爸 -> 爷爷” 的顺序看看有没有函数监听。有函数监听就调用,并提供事件信息。没有就跳过。
-
事件捕获:从外向内找监听函数 -- 捕获不可以取消
-
事件冒泡:从内向外找监听函数 -- 冒泡可以取消
-
问题来了:那岂不是fn爷fn爸fn儿都要调用两次?非也!开发者
自己选择
把函数监听调用放在捕获阶段
还是冒泡阶段
。
五、addEventlistener
-
事件绑定API
绑定事件对象.addEventListener(事件类型,回调函数,布尔值)
- 如果
不传布尔值
或为falsy值
– fn就会走冒泡阶段
, - 如果
布尔值为true
– fn 就会走捕获阶段
- 你可以选择将fn放在那一边。
- 如果
- 代码示例
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>冒泡demo</title>
</head>
<body>
<div class="level1 x">
<div class="level2 x">
<div class="level3 x">
<div class="level4 x">
<div class="level5 x">
<div class="level6 x">
<div class="level7 x">
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<script>
/* 要演示捕获阶段,将addEcentListener添加上第三个参数true即可 */
const level1 = document.querySelector('.level1')
const level2 = document.querySelector('.level2')
const level3 = document.querySelector('.level3')
const level4 = document.querySelector('.level4')
const level5 = document.querySelector('.level5')
const level6 = document.querySelector('.level6')
const level7 = document.querySelector('.level7')
let n = 1
level1.addEventListener('click', (e)=>{
const t = e.currentTarget
setTimeout(()=>{
t.classList.remove('x')
},n*1000)
n+=1
})
level2.addEventListener('click', (e)=>{
const t = e.currentTarget
setTimeout(()=>{
t.classList.remove('x')
},n*1000)
n+=1
})
level3.addEventListener('click', (e)=>{
const t = e.currentTarget
setTimeout(()=>{
t.classList.remove('x')
},n*1000)
n+=1
})
level4.addEventListener('click', (e)=>{
const t = e.currentTarget
setTimeout(()=>{
t.classList.remove('x')
},n*1000)
n+=1
})
level5.addEventListener('click', (e)=>{
const t = e.currentTarget
setTimeout(()=>{
t.classList.remove('x')
},n*1000)
n+=1
})
level6.addEventListener('click', (e)=>{
const t = e.currentTarget
setTimeout(()=>{
t.classList.remove('x')
},n*1000)
n+=1
})
level7.addEventListener('click', (e)=>{
const t = e.currentTarget
setTimeout(()=>{
t.classList.remove('x')
},n*1000)
n+=1
})
</script>
</body>
</html>
-
W3C事件模型
1、先捕获,在冒泡
2、注意 e对象被传给所有的监听函数
3、事件结束后,e 对象也就不存在了。
4、事件列表
六、target VS currentTarger
区别
e.target
– 用户操作的元素e.currentTarget
– 程序员监听的元素this就是e.currentTarget
举例
<div> <span>文字</span> </div> | 当用户点击文字后, | e.target就是span | e.currengtTarget就是div
一个特例
只有一个元素被监听(不考虑父子同时被监听),fn 分别在捕获和冒泡阶段监听click事件,用户点击的元素就是开发者监听的
<div>这是被监听的div</div> <script> let div=document.querySelector('div'); div.addEventListener('click',()=>{ console.log(1); }) //冒泡阶段 div.addEventListener('click',()=>{ console.log(2); },true) //捕获阶段 </script>
请问:输出 1 2 ,还是输出 2 1?
正确答案:谁先监听谁先执行。
七、事件委托
情景一
你要给100个按钮做监听事件,咋办?
情景二
你要监听目前不存在的元素的事件,咋办?
正确方案:监听这些元素的祖先,等冒泡或事件触发时判断是不是我想要监听的元素即可。
代码示例
<!DOCTYPE> <html> <head> <meta charset='UTF-8'> <title>事件委托</title> <head> <body> <div class='max'> <button class='btn-1'>btn1</button> <button class='btn-2'>btn2</button> <button class='btn-3'>btn3</button> . . (省略96个button) . <button class='btn-100'>btn100</button> </div> <script> /* 监听祖先元素 */ let btn_max=document.querySelector('.max'); btn_max.addEventListener('click',()=>{ const t = e.target; //为了保存e.target对象,因为它是一个异步的结果,点击后e.target对象就会消失。 if(e.tagName.toLowerCase()==='button'){ console.log('button被点击了'); console.log('被点击的button是'+ t.className); //这样就能知道点击了哪一个button } }) </script> </body> <html>
八、自定义事件
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>JS Bin</title>
</head>
<body>
<div id=div1>
<button id=button1>点击触发 frank 事件
</button>
</div>
<script>
button1.addEventListener('click', ()=>{
const event = new CustomEvent("frank", {"detail":{name:'frank', age: 18}})
button1.dispatchEvent(event)
})
button1.addEventListener('frank', (e)=>{
console.log('frank')
console.log(e)
})
</script>
</body>
</html>
以上代码就创建了一个自定义事件
,详情更多内容。