DOM事件委托

事件的三个特点

  1. TOM (代码)订阅/关注/监听了 XXX(用户)
  2. XXX (用户)发生了变化
  3. TOM(代码)得到通知

捕获&冒泡

  1. 操作系统最先知道用户点击了鼠标,浏览器次之

  2. child 被点击了,意味着 parent 也被点击了

  3. 如果同时监听了child和parent,那么

    • 捕获阶段:先通知parent,后通知child

      1
      2
      3
      4
      5
      6
      child.addEventListener('click',function(){
      console.log('child')
      },true)
      parent1.addEventListener('click',function(){
      console.log('parent1')
      },ture) //添加ture参数,表示捕获阶段
    • 冒泡阶段:先通知chlid,后通知parent

      1
      2
      3
      4
      5
      6
      child.addEventListener('click',function(){
      console.log('child')
      },flase)
      parent1.addEventListener('click',function(){
      console.log('parent1')
      },flase) //默认为flase,冒泡阶段
  4. W3C事件模型:支持两种阶段

    1
    2
    button.addEventListener('click',fn,true) //永远别用
    button.addEventListener('click',fn)

如何监听事件

当同一个对象使用.onclick的写法触发多个方法的时候,后一个方法会把前一个方法覆盖掉,也就是说,在对象的onclick事件发生时,只会执行最后绑定的方法。而用事件监听则不会有覆盖的现象,每个绑定的事件都会被执行。

  1. DOM level 0 事件: button.onclick = function(){}
  2. DOM level 2 事件: button.addEventListener(‘click’, function(){})

e/event

1
2
3
4
child1.addEventListener('click',function(e){
console.log(e)
})
//当出发事件的时候,e告诉你相关事件的任何信息

e.target&e.currentTarget

  • target:触发事件的元素,即点击的元素
  • currentEvent:被监听的元素

以下图片先点击绿色边框(parent),然后再点击红色区域(child)

阻止默认事件 e.preventDefault()

如果加在父元素上,对其子元素也有阻止事件的作用,所以尽量用在要阻止事件的具体子元素上,以免让其他事件失效(图中阻止了a链接的跳转作用,点击百度不会发生跳转)

停止冒泡e.stopPropagation()

propagation:传播的意思。 用户点击了子元素,不要向父元素通知。

事件委托

事件委托就是利用事件冒泡,只指定一个事件处理程序,就可以管理某一类型的所有事件。简而言之就是委托它们的父级代为执行事件。

  • 没有时间委托,逐个监听事件

  • 监听是占用内存的,既然4个li做了相同的事情,那么直接监听父元素,同样点击li元素可以实现。(但是有一个问题就是父元素里面的所有内容都会触发事件,包括点击父元素的padding。)

  • 那么添加一个if条件e.target.nodeName === 'LI',让触发事件的目标在元素LI 上,此时解决了上面li元素外和父容器之间也会触发事件的问题。(但是又引申出了一个新的问题,就是点击LI的后代元素无法触发事件。这和初衷不相符。)

  • 再修改一下if条件,e.target.nodeName ==='LI'||e.target.parentNode.nodeName === 'LI' , 让LI的span元素也能触发事件,此时的委托事件正好满足这个例子。(但是如果li有多个后代元素,又无法满足条件了。)

  • 再完善一下,我要点击后代元素触发事件,要判断其父元素是不是LI ,如果不是,继续网上找,直到找到li 。(但这样会出现找不到LI的情况,一直网上找到document,再网上就返回null了。)

  • 继续完善,网上找的过程中,如果一直到监听的哪一层元素还没找到,那就停止,返回null。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    //较为完整的事件委托代码
    var ul = document.querySelector('ul')
    function f(e){
    let el = e.target
    while(el.nodeName !== 'LI'){
    if(el === ul){
    el = null
    break;
    }
    el = el.parentNode
    }
    if(el){
    console.log('ok')
    }else {
    console.log('no')
    }
    }
    ul.addEventListener('click',f)