1 捕获和冒泡
2002年,W3C发布标准
文档名为DOM Level 2 Events Specification
规定浏览器应该同时支持两种调用顺序
首先按 爷爷 => 爸爸 => 儿子 顺序看有没有函数监听
然后按 儿子 => 爸爸 => 爷爷 顺序看有没有函数监听
有监听函数就调用,并提供事件信息,没有就跳过
事件绑定API
IE5 *:baba.attachEvent('onclick',fn) //冒泡
网景:baba.addEventListener('click',fn) //捕获
W3C:baba.addEventListener('click',fn,boolean) ==>>>> W3C标准
如果boolean不传值或者为falsy
就让fn走冒泡,即当浏览器在冒泡阶段发现baba 有fn监听函数,就会调用fn ,并提供事件信息
如果boolean为true
就让fn走捕获,即当前浏览器在捕获阶段发现baba有fn监听函数,就会调用fn,并提供事件信息
术语
从外向内找监听函数,叫事件捕获
从内向外找监听函数,叫事件冒泡
实例链接 => 先捕获 后冒泡
2 target VS currentTarget
e.target - 用户操作的元素
e.currentTarget - 程序员监听的元素
this 是e.currentTarget,不推荐使用(因为用this你不知道你获取的是target 还是currentTarget ) =>>>> 在事件监听中 不推荐使用this
举个例子
div > span{文字} 当用户点击文字 =>>>>看下面理解
e.target就是span
e.currentTarget就是div
一个特例
背景
只有一个div被监听(不考虑父子同时被监听)
fn分别在捕获阶段和冒泡阶段监听click事件
用户点击的元素就是开发者监听的
代码
div.addEventLisenter('click',f1) 两行中 谁放在前面一行(第一行)谁就先执行 (也就是谁先监听谁先执行)
div.addEventLisenter('click',fn,true)
请问,f1先执行还是f2先执行? =>>>>>>>>> 谁先监听谁先执行
如果把两行调换位置后,请问哪个先执行? =>>>>>>>> 谁先监听谁先执行
3 取消冒泡
=>>捕获不可取消,但冒泡可以
e.stopPropagetion() 可中断冒泡,浏览器不再向上走
一般用于封装某些独立的组件
4 不可阻止默认动作
有些事件不能阻止默认动作
MDN 搜索srcoll event,看到Bubbles 和 Cancelable
Bubbles的意思是该事件是否冒泡,所有冒泡都可取消
Cancelable的意思是开发者是否可以阻止默认事件
Cancelable与冒泡无关
推荐看mnd英文版 中文版本内容不全
5 如何阻止滚动
scroll事件不可阻止默认动作
阻止scroll默认动作没用,因先有滚动才有滚动事件
要阻止滚动,可阻止wheel和touchstart的默认动作
注意你需要找准滚动条所有的元素
但是滚动条还能用,可用css让滚动条width:0
css也行
使用overflow:hidden可以直接取消滚动条
但此时JS依然可以修改scrollTop
6 自定义事件
各种默认事件链接
以下代码是如何自定义一个事件
const button = document.querySelector('.button1')
button.addEventListener('click',()=>{
const event = new CustomEvent('lulu',{
detail{name:'frank',age:'18'}, //设置内容
bubbles:true , //是否取消冒泡
composed:false //是否阻止冒泡 不阻止
})
div1.dispatchEvent(event)})div.addEventListener('lulu',(e)=>{
//div1为div标签id=div1
console.log(e.detail)
})
7 事件委托
事件委托:我委托一个人 帮我干本来我该干的事情
====>>>>> 小记 案例用到的一些方法
1 js字母大小写转换方法:
转换成大写:toUpperCase()
转换成小写:toLowerCase()
2 tagName 获取标签名
e.target.tagName 获取用户点击的标签名
e.currentTarget.tagName 获取程序员点击事件的标签名
3 e.target.dataset.id 具体可看下图1
获取html内容 data-id='1' 里面的1
213134 e.target.textContent
获取当前用户操作标签点击按钮的内容21313
21313
第一小节 100个按钮 怎么省内存监听
实例代码
box.addEventListener('click',(e)=>{
const t = e.target // console.log(t)
if(t.tagName.toLowerCase() === 'button'){
console.log('button被点击了,我的内容是:'+ t.textContent) //获取button上面展示的内容 如下图2
console.log('被点击了 ,我获取的内容是 标签上的data-id= ' + t.dataset.id) //图3图解
}
})
第二小节 怎么监听暂时不存在(后面动态生成或者是setTimeout(1000s)执行)的元素
上代码
// 获取暂时不存在的元素
setTimeout(()=>{
const button= document.createElement('button')
button.textContent = '我是button内容'
// console.log(button)
box.appendChild(button) //注意点 写的时候写错了 前面是父元素 后面是要添加的子元素
},1000)
box.addEventListener('click',(e)=>{
const t = e.target
if(t.tagName.toLowerCase()==='button'){
console.log('被点击')
}})
第三小节 封装事件委托
要求:
写出这样一个函数on('click','#testDiv','li',fn)
当用户点击#testDiv里面的li远不时,调用fn函数
要求用到事件委托
代码
setTimeout(()=>{
const button = document.createElement('button')
button.textContent = '我是动态1000s后button里面的内容'
box.appendChild(button)},1000)
on('click','#box','button',()=>{
console.log('元素被点击了')})
function on(eventType,element,selector,fn){
if(!(element instanceof Element)){ // 判断一个元素是不是真正的元素 判断它是不是对象也可以用它 instanceof Object 判断数组 instanof Array
element = document.querySelector(element)
}
element.addEventListener(eventType,(e)=>{
const t = e.target
if(t.matches(selector)){ //matches 判断它是不是一个选择器
fn(e)
}
}
)}
代码二:高级版 (给button里面的span加点击 代码一 不能实现点击)
setTimeout(()=>{
const button = document.createElement('button')
const span = document.createElement('span')
span.textContent = '我是动态1000s后span里面的内容'
box.appendChild(button)
button.appendChild(span)},1000)
on('click','#box','button',()=>{
console.log('button 被点击了')})
function on(eventType,element,selector,fn){
if(!(element instanceof Element)){
element = document.querySelector(element) }
element.addEventListener(eventType,(e)=>{
let t = e.target
while(!t.matches(selector)){
if(element === t){
t = null
break
}
t = t.parentNode
}
t && fn.call(t,e,t)
})
return element
}
JS支持事件吗?
答:
支持 也不支持 以上案例写的DOM事件不属于JS功能,术语浏览器提供的DOM功能
JS只是调用了DOM提供的addEventListener()而已