美文网首页
DOM事件模型

DOM事件模型

作者: zzzZink | 来源:发表于2018-05-04 19:04 被阅读0次

    DOM leave 0:

    • xxx.onclick = function(){ ... }
    onclick

    onclick="xxx()" == eval(xxx())

    eval

    onclick是属性,具有唯一性

    DOM leave 2:
    xxx.onclick = function() {}只能给元素绑定一个onclick事件
    xxx.addEventListener('click', function(){ ... }) 可以绑定多个事件,可看作队列(先进先出)。
    xxx.removeEventListener('click', function() {}) 取消click队列。

    删除事件队列:

    function f1(){
      console.log(1)
    }
    function f2(){
      console.log(2)
    }
    function f3(){
      console.log(3)
    }
    xxx.addEventListener('click', f1)
    
    xxx.addEventListener('click', f2)
    
    xxx.removeEventListener('click', f1)
    
    xxx.addEventListener('click', f3)
    
    xxx.removeEventListener('click', f3)
    

    只有一次事件监听的按钮:

    function f1(){
      console.log(1)
      xxx.removeEventListener('click', f1)
    }
    xxx.addEventListener('click', f1)
    

    事件捕获与冒泡
    html:

    <div id="grand1">
      爷爷
      <div id="parent1">
        父亲
        <div id="child1">
          儿子
        </div>
      </div>
    </div>
    
    

    js:

    //1 当我点击儿子的时候,是否点击了父亲和爷爷?
    // yes1
    
    //2 当我点击儿子的时候,三个函数是否调用
    grand1.addEventListener('click', function fn1(){
      console.log('爷爷')
    })
    parent1.addEventListener('click', function fn2(){
      console.log('父亲')
    })
    child1.addEventListener('click', function fn3(){
      console.log('儿子')
    })
    // yes2 
    
    //3 请问fn1 fn2 fn3 的执行顺序
    // 123  or  321
    // 321
    
    //不传第三个参数/传  儿子爸爸爷爷
    //第三个参数传true   爷爷爸爸儿子
    
    DMO事件模型

    先从上往下(捕获阶段),再从下往上(冒泡阶段)。
    如果第三个参数传的是false(或者不传,默认false),走右边(冒泡)。儿子爸爸爷爷。
    如果第三个参数传的是true,走左边(捕获)。爷爷爸爸儿子。

    child1.addEventListener('click', function fn3(){
      console.log('儿子2')
    },true)
    child1.addEventListener('click', function fn3(){
      console.log('儿子1')
    })
    

    如果是在本身触发,不区分捕获冒泡,按照代码顺序执行。

    DOM事件模型续...
    如何做到点击按钮显示浮层,点击别处隐藏浮层?
    <html>:

    <!DOCTYPE html>
    <html>
    <head>
      <meta charset="utf-8">
      <title>JS Bin</title>
    </head>
    <body>
    <div class="wrapper" id="wrapper">
      <button id="clickMe">点我</button>
      <div class="popover" id="popover">
        浮层
      </div>
    </div>
    </body>
    </html>
    

    <css>:

    body{
      border: 1px solid red;
    }
    .wrapper{
      position: relative;
      display: inline-block;
    }
    .popover{
      background: white;
      border: 1px solid red;
      position: absolute;
      left: 100%;
      top: 0;
      white-space: nowrap;
      padding: 10px;
      margin-left: 10px;
      display: none;
    }
    .popover::before{
      position: absolute;
      right: 100%;
      top: 5px;
      border: 10px solid transparent;
      border-right-color: red;
      content: '';
    }
    .popover::after{
      position: absolute;
      right: 100%;
      top: 5px;
      border: 10px solid transparent;
      border-right-color: white;
      content: '';
      margin-right: -1px;
    }
    
    这是一个很丑很丑的浮层

    <js>:

    clickMe.addEventListener('click', function fn1(){
      popover.style.display = 'block'
      console.log('block')
    })
    document.addEventListener('click', function fn2(){
      popover.style.display = 'none'
      console.log('none')
    })
    

    ok,首先这个方法是行不通的,因为当我们点击button时,我们也相当于点击document,在冒泡阶段会执行到fn2,并且我们知道在冒泡阶段函数的执行过程是fn1-fn2,所以最后点击按钮是不会显示浮层的。
    接下来我们让button阻止冒泡:

    clickMe.addEventListener('click', function fn1(e){
      popover.style.display = 'block'
      console.log('block')
     e.stopPropagation()  //阻止冒泡向上传播
    })
    

    这段代码会在button处中断冒泡过程,导致document不能执行函数fn2,所以这样做可以达到我们所要的效果,但是这里有一个bug,如果我们给浮层加上一个checkbox:

    <div class="popover" id="popover">
      <input type="checkbox">
        浮层
      </div>
    
    有checkbox的浮层

    这个时候我们点击checkbox,会隐藏浮层,为什么?
    因为button和浮层都是在wrapper中,为兄弟关系,botton并不会被点击,但浮层依然在document中,事件会通知到document,导致浮层隐藏,那么怎么解决呢?
    很简单,我们在浮层和button的父元素wrapper上添加事件并删除button的阻止冒泡代码:

    clickMe.addEventListener('click', function fn1(e){
      popover.style.display = 'block'
      console.log('block')
    })
    
    wrapper.addEventListener('click', function(e){
       e.stopPropagation()  //阻止冒泡向上传播
    })
    

    因为冒泡阶段是从小到大,会经过父元素wrapper,我们只需要在wrapper上中断冒泡过程就ok了。
    此时再点击checkbox就不会导致浮层隐藏。
    但是这种方法会浪费内存,因为document和popover相对监听,如果有很多很多popover,那么document上就要做对应个数的监听,非常浪费内存,所以这个版本不太实用。
    更新版:

    $(clickMe).on('click',function () {
        $(popover).show();
    });
    $(wrapper).on('click',false);  
    
    $(document).on('click',function () {
        $(popover).hide();
    });
    

    请注意,这是个错误的示范,false会阻止所有默认事件以及冒泡,导致checkbox无法点击,并且依然没有改善内存问题。
    再次更新:

    $(clickMe).on('click',function(){
      console.log('被点击了')
      $(popover).show()
        //每次点击button,document只监听一次click事件,关闭浮层后不再监听,这样就做到了节省内存
        $(document).one('click',function(){
          $(popover).hide()
        })
    })
    
    $(wrapper).on('click', function(e){
       e.stopPropagation()  //阻止冒泡向上传播
    })
    

    再次更新版:点击按钮显示浮层和关闭浮层。

    $(clickMe).on('click',function(){
       if($(popover).css('display') === 'none'){
         $(popover).show()
      }else{
         $(popover).hide()
      }
        $(document).one('click',function(){
          $(popover).hide()
        })
    
    })
    $(wrapper).on('click', function(e){
       e.stopPropagation()  //阻止冒泡
    })
    

    面试题:如果没有阻止冒泡,点击button不会出现浮层,为什么呢?
    答:在冒泡阶段,执行clickMe的函数,执行完$(popover).show()之后就为document添加监听事件,导致document拥有函数,这个过程是在冒泡结束之前进行的,所以当clickMe的函数执行完之后,继续执行document的监听时间函数。

    相关文章

      网友评论

          本文标题:DOM事件模型

          本文链接:https://www.haomeiwen.com/subject/lvwdrftx.html