跳到主要内容

DOM事件传播机制

有三种事件传播方式:事件冒泡、事件捕获、事件代理

其中事件冒泡 Bubbing Phase事件捕获 Capture Phase两种方式相似,不同在于两种方式接收的事件顺序不同

冒泡与捕获

IE5:最先支持冒泡特性

Firefox:与IE对立采取捕获作为事件

后来W3C规定

浏览器应该同时支持两种调用顺序,先==捕获==再==冒泡==

首先:以==祖先 --> 爸爸 --> 儿子==的顺序监听函数 然后:以==儿子 --> 爸爸 --> 祖先==的顺序监听函数

术语总结

  1. 从外到内监听叫事件捕获
  2. 从内到外监听叫事件冒泡

事件监听

监听对象

  1. event.target()用户操作的元素
  2. 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文档查看事件的BubblesCancelable是否冒泡与可否阻止冒泡。

support-bubbles

不是所有的事件都可以取消冒泡,例如:滑动事件无法阻止,但是可以通过阻止默认事件的触发来达到目的。

// 事件监听
Object.addEventListener(Object',(e)=>{e.preventDefault()}) //pc端禁止滑动,注意取消css的滚动条
Object.addEventListener('touchstrat',(e)=>{e.preventDefault()}) //手机端禁止滑动

事件委托

委托:需要请他人协助完成本该自己完成的事情,如:找中介协助找房子。

主要有两个优点:

  1. 省监听内存
  2. 动态监听元素并添加对应事件

通过监听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)
}

自定义事件

MDN:事件参考

自带事件一共有100多个事件,分类约有:

  1. 资源事件、网络事件、焦点事件
  2. WebSock 事件、会话历史事件
  3. CSS动画事件、CSS 过渡事件
  4. 表单事件、打印事件、文本事件、视图事件
  5. 剪切板、键盘、鼠标事件、拖放事件
  6. 媒体事件、进度事件、存储事件、更新事件
  7. 值变化事件

使用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.