DOM 事件机制

185 阅读3分钟

DOM事件流

代码示例

<div class=爷爷>
 <div class=爸爸>
    <div class=儿子>
      文字
    </div>
  </div>
</div>

分析

  • 即. 爷爷> .爸爸> .儿子 ,给三个div分别添加事件监听 fnYe/ fnBa /fnEr

  • 提问:点击了文字算点击了谁

  • 答:算点击了儿子、爸爸、和爷爷 三个都算

  • 提问:点击文字后调用顺序是怎么样

  • 答:从爷爷到儿子 也行(网景), 从儿子到爷爷也行(IE5)

浏览器在发展的过程中出现了两种不同的规范

  • IE5*: 从儿子 => 爸爸 => 爷爷,逐步向上传播
  • 网景:从爷爷 => 爸爸 => 儿子,逐步向下传播
  • W3C:2002 年,发布标准规定浏览器应该同时支持两种调用顺序事件捕获和事件冒泡都可以。首先按捕获然后按冒泡

DOM事件模型

DOM事件模型分为捕获和冒泡

事件捕获: 从外到内找监听函数

事件冒泡:从内到外找监听函数

示意图

addEventListener

  • 事件绑定API

    • IE5:baba.attachEvent('onclick',fn) //冒泡
    • 网景:baba.addEventListener('click',fn) //捕获
    • W3C制定:baba.addEventListener('click',fn,bool)
  • 如果 bool不传或 值为falsy

    • 就让fn走冒泡,即可浏览器在冒泡阶段发现baba有 fn监听函数,就会调用fn,并提供事件信息
  • 如果bool 为 true

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

事件委托

什么是事件委托?

  • 事件委托又称之为事件代理,JavaScript中常用绑定事件的常用技巧。顾名思义,“事件委托”即是把原本需要绑定在子元素的响应事件(click、keydown......)委托给父元素,让父元素担当事件监听的职务。

  • 事件委托的原理是DOM元素的事件冒泡。

为什么要用事件委托?

  • 举例一:当要给100个按钮添加点击事件时,怎么做?
  • 答:监听这100个按钮的祖先,等冒泡的时候判断target 是不是这100个按钮中的一个
<body>
  <div id=div1>
    <span>span1</span>
    <button data-id='1'>1</button>
    <button data-id='2'>2 </button>
    ...
    <button>100</button>
  </div>
</body>
<script>
  div1.addEventListener('click',(e)=>{
    const t = e.target
    if(t.tagName.toLowerCase() === 'button'){
      console.log('button被点击了')
      console.log('button 内容是' + t.textContent) // 知道被点击是哪一个
       console.log('button data-id是' + t.dataset.id) //dataset 可以获取到以data开头的元素属性的值
    }
  })
</script>
  • 举例二:你要监听目前不存在的元素的点击事件,咋办?
  • 答:监听祖先,等点击的时候看看是不是我想要监听的元素即可
setTimeout(()=>{
  const button = document.createElement('button')
  button.textContent = 'click 1'
  div1.sppendChild(button)
},1000)
div1.addEventListener('click',(e)=>{
  const t = e.target
  if(t.tagName.toLowerCaase()=== 'button'){
    console.log('button 被 click')
  }
})

优点

  • 节省监听内存
  • 可以监听动态元素

Event对象常见应用

stopPropagation()

用途:取消事件冒泡或捕获

举例

<body>
    <div id="grandfather">
        <div id="parent">
            <div id="son">
                点我
            </div>
        </div>
    </div>
</body>
<script>
    son.addEventListener('click', e => {
        console.log('点了儿子')
        e.stopPropagation()// 当加了这个就只会打印点了儿子,下面的函数将不会进行
    })
    parent.addEventListener('click', e => {
        console.log('点了爸爸')
    })
    grandfather.addEventListener('click', e => {
        console.log('点了爷爷')
    })
</script>

preventDefault()

用途:取消事件的默认行为

举例

<body>
    <a id="test" href="http://www.google.com">链接</a>
</body>
<script>
    test.onclick = function (e) {
        e.preventDefault()
    }
</script>

target vs currentTarget

  • 区别

    • e.target 用户操作的元素
    • e.currentTarget - 程序员监听的元素
    • this 是 e.currentTarget
  • 举例

    • div > span {文字} ,用户点击文字
    • e.target 就是 span
    • e.currentTarget 就是div