DOM事件分为捕获阶段,事件阶段和冒泡阶段。程序员
咱们能够看到,其执行顺序为,捕获 => 目标 => 冒泡api
因此,若是用户点击了td元素,而且,td元素的祖先元素拥有监听函数,那么其祖先元素也算被点击了。bash
可是,若是被点击的元素没有父元素,且他被用户点击了,那么,冒泡与捕获的顺序取决于JS代码的顺序dom
上文提到,DOM事件拥有捕获和冒泡阶段,那么监听函数会不会被调用两次呢?函数
答案是不会,W3C 制定了一个标准,其api为ui
xxx.addEventListener('eventType',fn,bool)
复制代码
若bool为falsy值或不传(0,'',NaN,undefined,null),那么,绑定为冒泡,若为true则为捕获。spa
须要特别注意的是,若是咱们须要延迟触发事件3d
那么直接监听,是监听不到事件的,由于1s后,点击事件已经结束了,因此咱们须要将点击事件记录下来。code
target 与 currentTarget 的区别在于,target是用户操做的元素,currentTarget是程序员监听的元素。cdn
例如 div>span{文字},那么用户点击文字
span就是target
div就是currentEvent
一般状况下,咱们能够经过stopPropagation阻止冒泡(捕获没法被取消)。
(例如当咱们点击一个内部标签时,不想触发外部标签的事件,可使用这种方法)
例如,scroll 事件就没法取消冒泡,因此咱们只能经过其余办法取消,例如使滚动条的宽度为0,阻止滚轮的默认事件,阻止移动端touchstart的默认事件。
dom事件类型有不少,具体咱们能够参考MDN
button1.addEventLsitener('click',()=>{
const event = new CustomEvent('name',{
{'detail':{name:'jack',age:'18'}},
bubbles:true,
cancelable:true
})
button1.dispatchEvent(event)
})
复制代码
事件委托就是监听祖先元素,判断被点击的元素是不是咱们想监听的元素,如果,则执行函数。 其优势是节省内存以及能够监听动态生成的元素
function on(eventType,element,selector,fn){
if(!(element instanceof Element)){
element = document.querySelector(element)
}
element.addEventListener(eventType,(e)=>{
t = e.target
if (t.tagName.toLowercase() === selector){
fn(e)
}
})
}
复制代码
若是咱们将事件委托函数封装成以上形式,咱们会发现一个问题,当用户点击时,target不必定是咱们监听的元素。
因此,咱们须要使用递归,来寻找target的祖先元素,直到祖先元素是element为止。
function on(eventType,element,selector,fn){
if (!(element instanceof Element)){
element = document.querySelector(element)
}
element.addEventListener(eventType,(e)=>{
let t = e.target
while(t.tagName.toLowercase() !== selector){
if(t === element){
t = null
break
}
t = t.parentNode
}
t && fn.call(t,e,t)
})
return element
}
复制代码