DOM事件模型与事件委托

DOM事件模型

 <div id="grandpa">
    <div id="father">
      <div id="son">文字
      </div>
    </div>
  </div>
复制代码

假如给上面3个 div 分别添加事件监听 fnYe/fnBa/fnEr ,那么当点击 文字 的时候会先调用哪个事件?是先调用fnYe?还是先调用fnEr?

此时则需要一种约定来规范事件执行的顺序。

IE5认为应该先调用 fnEr
网景则认为应该先调用 fnYe

2002年,W3C发布了标准。文档名为 DOM Level 2 Events Specification
规定了浏览器应该同时支持两种调用顺序

即:
首先按 爷爷 -> 爸爸 -> 儿子 顺序看有没有函数监听
然后按 儿子 -> 爸爸 -> 爷爷 顺序看有没有函数监听
有监听函数就调用,并提供事件信息,没有就跳过

术语:
从外向内找监听函数,叫事件捕获
从内向外找监听函数,叫事件冒泡

事件捕获、冒泡.jpg

事件绑定API

.addEventListener('click',fn,bool)
复制代码
  • 如果bool不传或者为false
    就让fn走冒泡,即当浏览器在冒泡阶段发现有监听函数,就回调用fn,并提供事件信息

  • 如果bool为true
    就让fn走捕获,即当浏览器在捕获阶段发现有监听函数,就回调用fn,并提供事件信息


--HTML--

<div id="grandpa">grandpa
    <div id="father">father
      <div id="son">son</div>
    </div>
  </div>

--Javascript--
  
let fnYe = document.getElementById('grandpa')
let fnBa = document.getElementById('father')
let fnEr = document.getElementById('son')

fnYe.addEventListener('click', function fn() {
  console.log('爷爷')
} )
fnBa.addEventListener('click', function fn() {
  console.log('爸爸')
} )
fnEr.addEventListener('click', function fn() {
  console.log('儿子')
})  
复制代码

上面代码没有bool值,打印顺序为 儿子 爸爸 爷爷


--HTML--

<div id="grandpa">grandpa
    <div id="father">father
      <div id="son">son</div>
    </div>
  </div>

--Javascript--
  
let fnYe = document.getElementById('grandpa')
let fnBa = document.getElementById('father')
let fnEr = document.getElementById('son')

fnYe.addEventListener('click', function fn() {
  console.log('爷爷')
},true)
fnBa.addEventListener('click', function fn() {
  console.log('爸爸')
},true)
fnEr.addEventListener('click', function fn() {
  console.log('儿子')
},true)  
复制代码

上面代码bool值为true,打印顺序为 爷爷 爸爸 儿子

事件委托

由于事件会在冒泡阶段向上传播到父级元素,因此可以把子元素的监听函数绑定在父级元素上,由父级元素的监听函数统一处理多个子元素的事件。这种方法叫做事件委托。

例如,我们想要租房子。如果是我们自己租就比较麻烦,需要跑小区。此时我们可以委托中介帮我们操作,所谓的委托就是委托中介帮我们操作本应该由我们自己操作的事情。

  • 情景一

给100个按钮添加点击事件 倘若给每一个按钮都绑定点击事件,那将会占用大量内存。我们可以监听这100个按钮的祖先,等冒泡的时候判断target是不是这100个中的一个。


--html--

<div id="div1">
    <span>span</span>
    <button data-id = "1号">click1</button>
    <button>click2</button>
    <button>click3</button>
    <button>click4</button>
    <button data-id = "5号">click5</button>
    <button>click6</button>
    ...
    <button>click100</button>
  </div>
  
--javascript--
  
div1.addEventListener('click', (e) => {
  const t = e.target
  if (t.tagName.toLowerCase() === 'button') {
    console.log('botton被点击')
    console.log('botton' + t.textContent + '被点击')
    console.log('botton' + t.dataset.id + '被点击')
  }
})
复制代码
  • 情景二

监听目前不存在的元素的点击事件 由于目前元素不存在,无法直接监听这个元素。但我们可以使用事件委托实现,监听祖先元素,等点击的时候看是不是想要的元素。


--html--

<div id="div1"> 
  </div>
  
--javascript--
  
setTimeout(()=>{
  const button = document.createElement('button')
  button.textContent = 'click'
  div1.appendChild(button)
},1000)

div1.addEventListener('click', (e) => {
  const t = e.target
  if (t.tagName.toLowerCase() === 'button') {
    console.log('botton被点击')
  }
})
复制代码

事件委托的优点

  • 省内存,提高性能。
  • 可以监听动态元素。
分类:
前端
标签: