DOM事件模型和事件委托

203 阅读4分钟

DOM 事件

DOM使JavaScript有能力对HTML上的事件做出反应。包括鼠标或键盘的点击事件,移动事件以及页面中内容的变化。HTML元素事件是浏览器内在自动产生的,当有事件发生时html元素会向外界发出各种事件。

DOM事件模型

  1. 让我们先从一个例子入手
<div class=爷爷>
<div class=爸爸>
<div class=儿子>
文字
</div>
</div>
</div>

当我们点击这个文字时,算不算点击了儿子呢,那么爸爸与爷爷呢,如果分别给这三个div做事件监听,那么先从谁开始呢。这个具有争议的话题,不同的浏览器有不同的看法,IE认为应该按照儿子->爸爸->爷爷 的顺序,但是当时的网景却认为应该按照爷爷->爸爸->儿子的顺序来执行,最后W3C出场了,他发布了标准规定浏览器应该同时支持两种调用顺序。先按照爷爷->爸爸->儿子的顺序来看有没有函数监听需要调用,然后再儿子->爸爸->爷爷的顺序来看看有没有函数监听需要调用。那岂不是一个函数需要调用两次了,并不,开发者可以自己决定到底使用哪种顺序。

事件捕获:从外向内找监听函数叫做事件监听

事件冒泡:从内向外找监听函数叫做事件冒泡

DOM事件模型分为两类,一种是IE所使用的冒泡模型事件,另一类就是w3c所制定的标准定义的冒泡型与捕获型

事件绑定的API W3C标准制定的浏览器事件模型使用addEventListener``removeEventListen两个函数.至于IE5使用的是attachEvent 例如:爸爸.addEventListener('click',fn,false) 爸爸.addEventListener*('click',fn).第一个参数是事件类项,第二个参数是处理函数,而第三个参数不传或是false就让fn走冒泡阶段。如果第三个参数传true就意为着在捕获阶段就处理函数。
区别 target和current.Target,例如<div><span>文字</span></div>当对div做事件监听时,当点击文字时,那么target指的是span标签,而currentTarget指的是div标签,前者指的是用户操作的元素而后者指的是用户监听的元素。 捕获阶段不可以中断,但是冒泡阶段确是可以中断,使用e.stopPropagation可以阻止冒泡,凡事都有例外,有些事件也不可以阻止冒泡,具体可以查看具体事件的MDN文档,Bubbles事件是否以冒泡,cancelable 开发者是否可以取消冒泡。 自定义事件 目前浏览器自带事件有很多,那么我们是否也可以自定义一个事件呢,答案当然是可以的了。[自定义一个事件](js.jirengu.com/tuhonowiga/…

事件委托

js里的事件委托:当事件触发时,把想要做的事情委托给父元素处理。抽象的东西当然是要有一个例子来说明就更加形象化了。

当我们要为这多个button按钮添加点击事件时,可以遍历

这样子做可以实现,但是十分浪费监听数,有25个button就有25个一模一样的函数创建了,十分浪费内存。最好的解决办法就是利用dom的事件模型何不把这件事情给他们的父亲呢,代码如下
场景二 假设这个div里面还有一个按钮叫26,要等会再出现,那么我们需要使得这26个按钮都都有点击事件呢,先试一试刚才的办法。

setTimeout(()=>{
  let button=document.createElement('button')
  button.textContent='26'
  div1.appendChild(button)
},1000)



let  buttonList=document.getElementsByTagName("button")
for(let i=0;i<buttonList.length;i++){
  buttonList[i].addEventListener("click",()=>{
    console.log('button被点击了')
  }
)
}
let div1=document.getElementsByTagName('div')[0]

很遗憾是没有任何效果的,所以只有监听祖先,等点击的时候再看是不是需要监听的元素即可. 既然事件委托这么有用,那么封装一个事件委托就显得极其重要了,下面函数可以参考

有问题的
function on(eventType,element,selector,fn){
  if(!(element instanceof Element)){
    element=document.querySelector(element)
  }
  element.addEventListener(eventType,(e)=>{ 
    const t=e.target
  if(t.matches(selector)){
    fn(e)
  }
    })
 
}
on('click','#div1','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
      }
      console.log(t)
      t = t.parentNode
      console.log(t)
    }
    t && fn.call(t, e, t)
  })
  return element
}


on('click', '#div1', 'button', () => {
  
  console.log('button被点击了')
})