DOM事件传播机制
有三种事件传播方式:事件冒泡、事件捕获、事件代理
其中事件冒泡 Bubbing Phase
与事件捕获 Capture Phase
两种方式相似,不同在于两种方式接收的事件顺序不同
冒泡与捕获
IE5:最先支持冒泡特性
Firefox:与IE对立采取捕获作为事件
后来W3C规定
浏览器应该同时支持两种调用顺序,先==捕获==再==冒泡==
首先:以==祖先 --> 爸爸 --> 儿子==的顺序监听函数 然后:以==儿子 --> 爸爸 --> 祖先==的顺序监听函数
术语总结
- 从外到内监听叫事件捕获
- 从内到外监听叫事件冒泡
事件监听
监听对象
event.target()
用户操作的元素event.currentTarget
程序员监听的元素
有可能监听的是同一个元素也有可能不一样
body > div > p 用户点击
p
标签内容的文字 event.target 为p
event.currentTarget 为代码监听的对象,如:监听是div
则是div
,监听的是body
,那么 currentTarget 则为body
EventTarget.addEventListener(type, listener, options[可选], useCapture[可选])
type:触发事件的类型 listener:执行的函数 options:常用 3个选项。
{capture:Boolean}
是否冒泡、{once:Boolean
}函数是否只执行一次、{passive:Boolean}
是否阻止preventDefault()
默认事件 useCaptrue:Boolean 是否冒泡。默认false
为捕获行为
综合例子
function border(e){
e.target.style.border = "1px solid red";
console.log('e.currentTarget');
console.log(e.currentTarget);
console.log('e.target');
console.log(e.target)
}
let p = document.createElement('p');
p.innerHTML = '<span>Hi!</span>'
document.body.append(p)
var ps = document.getElementsByTagName('p');
for(var i = 0; i < ps.length; i++){
ps[i].addEventListener('click', border);
}
document.body.addEventListener('click', border);
取消冒泡
使用event.stopPropagation
取消冒泡,但无法阻止捕获
通过MDN文档查看事件的Bubbles
与Cancelable
是否冒泡与可否阻止冒泡。
不是所有的事件都可以取消冒泡,例如:滑动事件无法阻止,但是可以通过阻止默认事件的触发来达到目的。
// 事件监听
Object.addEventListener(’Object',(e)=>{e.preventDefault()}) //pc端禁止滑动,注意取消css的滚动条
Object.addEventListener('touchstrat',(e)=>{e.preventDefault()}) //手机端禁止滑动
事件委托
委托:需要请他人协助完成本该自己完成的事情,如:找中介协助找房子。
主要有两个优点:
- 省监听内存
- 动态监听元素并添加对应事件
通过监听div
,对所有子元素的事件进行操作。默认是动态监听DOM事件
<div id='test'>
<button id="1">按钮1</button>
<button data-id="2">按钮2</button>
<button>按钮3</button>
<button>按钮4</button>
<button>按钮5</button>
<div>
test.addEventListener('click',(e)=>{
const t = e.target
if(t.tagName.toLowerCase() === 'button'){
console.log("button内容为" + t.textContent)
}
if(t.id === "1"){
console.log("ID为1的按钮")
}
if(t.dataset.id === "2"){
console.log("data-id为2的按钮")
}
})
如何封装一个事件委托
setTimeout(()=>{
const button = document.createElement("input")
button.type = 'button'
button.value = "按钮1"
test.append(button)
},500)
on('click','#test','input',(e)=>{
const t = e.target
t.value = "按钮被点击了"
console.log(t)
},true)
//接收五个参数,once默认为false,true为点击事件只执行一次
function on(eventType, element, selector, fn, once){
if(once === true){
once = {once:true}
}else{
once = {once:false}
}
if(!(element instanceof Element)){
element = document.querySelector(element)
}
element.addEventListener(eventType,(e)=>{
const t = e.target
if(t.matches(selector)){
fn(e)
}
},once)
}
自定义事件
自带事件一共有100多个事件,分类约有:
- 资源事件、网络事件、焦点事件
- WebSock 事件、会话历史事件
- CSS动画事件、CSS 过渡事件
- 表单事件、打印事件、文本事件、视图事件
- 剪切板、键盘、鼠标事件、拖放事件
- 媒体事件、进度事件、存储事件、更新事件
- 值变化事件
使用new CustomEvent()自定义事件。
setTimeout(()=>{
const button = document.createElement("input")
button.type = 'button'
button.value = "按钮1"
button.id = "button1"
test.append(button)
},500)
test.addEventListener('click',(e)=>{
const t = e.target
if(t.id === 'button1'){
const event = new CustomEvent('aziz',{
detail:{name:"aziz",age:18},
bubbles: true, //是否冒泡
cancelable: false //是否可取消
})
button1.dispatchEvent(event)
}
})
test.addEventListener('aziz',(e)=>{
console.log('aziz')
console.log(e.detail)
})
end.