目录
- 点击事件?
- 事件的捕获和冒泡
- 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>
以上代码就创建了一个自定义事件,详情更多内容。