美文网首页
阻止事件传播

阻止事件传播

作者: astak3 | 来源:发表于2018-08-21 16:33 被阅读0次

    上一次学习了DOM 的事件,理解了冒泡事件和捕获事件,触发的机制,今天学习一下具体的应用场景,或者说在哪个地方容易踩坑。

    做一个小demo,点击按钮出现浮层,点击其它地方关闭浮层,写一个简单css

    <style>
    .wrapper{
        position:relative;
        display:inline-block;
    }
    .popover{
        position:absolute;
        border:1px solid red;
        left:100%;
        top:0;
        padding:10px;
        margin-left:10px;
        background:white;
        display: none;  /*默认隐藏*/
    }
    .popover::before{
        position:absolute;
        content:'';
        top:5px;
        right:100%;
        border:10px solid transparent;
        border-right-color:red;
    }
    .popover::after{
        position:absolute;
        content:'';
        top:5px;
        right:100%;
        border:10px solid transparent;
        border-right-color:white;
        margin-right:-1px;
    }
    
    </style>
    <div id="wrapper" class='wrapper'>
        <button id="clickMe">点我</button>
        <div id="popover" class="popover">
            <input type="checkbox">浮层
        </div>
    </div>
    <script>
        clickMe.addEventListener('click',function(){
            popover.style.display = 'block';
        });
    </script>
    

    那现在我要点击页面空白地方关闭呢?该用什么方法,很容易想到监听文档,如下代码

    document.addEventListener('click',function(){
        popover.stely.display = 'none';
    });
    

    但是实际上这样写了之后,按钮都失效了,怎么点都没有反应。这是为什么呢?
    理解上一篇讲的捕获和冒泡事件后就很好理解这点了,可以DOM事件之捕获、冒泡
    我们没有指定监听是在捕获还是冒泡阶段,浏览器默认是冒泡阶段,当我们点击按钮时,捕获阶段没有发生什么时候,但是冒泡阶段就不一样了,首先button上函数先触发,然后document上函数也触发了,导致准备出现的浮层又被隐藏了。
    那你可能要问,button上的事件执行了没?其实这两个事件都执行了,只是时间太短,浏览器默认一起执行了,可以在里面加一个debugger,就可以看到了。

    clickMe.addEventListener('click',function(){
        popover.style.display = 'block';
    });
    

    那该怎么解决呢?最简单的方法是,除了要执行popover.style.display = 'block',还要阻止事件传播

    clickMe.addEventlistener('click',function(){
        popover.style.display = 'block';
    });
    
    popover.addEventListener('click',function(e){
        e.stopPropagation();
    });
    

    这里为什么添加在按钮的父元素上面呢?如果不添加在父元素上面,点击浮层的时候,浮层也会被关闭。

    如果页面上有很多监听器的话,这个方法是比较浪费内存的,比较省内存的方法用JQuery 做

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

    一开始不监听,只在popover``show的时候监听一次,马上关掉,这叫做清理战场。
    $(wrapper).on('click',false) 和下面的代码完全等价

    $(wrapper).on('click',function(e){
        e.preventDefault();     //阻止默认事件
        e.stopPropagation();    //阻止传播
    })
    

    但是如果页面中有checkbox,你在它的父元素任何一层,包括checkbox自己,添加了组织默认事件那么这个checkbox就没办法被check

    这里有个问题,如果没有阻止事件传播,向下面这样,会发生什么事情呢?

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

    当然了,和之前一样,什么事情也不会发生,那当我点击按钮之后里面都发生了那些事情呢?
    当我点击了按钮之后,它会做两件事情,首先把popover``show出来,然后把hide函数添加到document上面,当事件传播到document,就会又把它给隐藏了。

    可以给它添加一个setTimeout()函数来解决这个问题

    $(clickMe).on('click',function(){
        $(popover).show();
        setTimeout(function(){
            $(document).one('click',function(){
                $(popover).hide();
            })
        },0)
    });
    

    setTimeout(fn,0)这个0不是马上执行,而是尽快执行,具体是在冒泡结束在执行这里的函数,也就是说,当冒泡结束后,在把监听事件添加到document上面,等待用户下次点击在执行。

    用 jQuery 做事件冒泡

    <style>
    .red.active{
      background:red;
    }
    .blue.active{
      background:blue;
    }
    .green.active{
      background:green;
    }
    .yellow.active{
      background:yellow;
    }
    .orange.active{
      background:orange;
    }
    .purple.active{
      background:purple;
    }
    div{
      border:1px solid black;
      display:flex;
      flex:1;
      padding:20px;
      transition:1s;
      border-radius:100%;
    }
    *{
      padding:0;
      margin:0;
      box-sizing:border-box;
    }
    .red{
      height:300px;
      width:300px;
    }
    </style>
    <div class="red">
      <div class="blue">
        <div class="green">
          <div class="yellow">
            <div class="orange">
              <div class="purple"></div>
            </div>
          </div>
        </div>
      </div>
    </div>
    <script>
    var n = 1
    $('div').on('click',function(e){
      setTimeout(function(){
        $(e.currentTarget).addClass('active')  
      },n*500)
      n+=1
    })
    </script>
    

    总结:

    1. 同时监听buttondocument,点啥都没反应,因为两个函数都执行了,用阻止事件传播解决了,比较浪费内存
    2. 好一定的方法是用jQuery 做,点击button后在监听document,关闭了就不再监听,不阻止事件传播,点啥也没反应,两种解决方法:一种是阻止事件传播,另一种是添加一个setTimeout()函数。

    相关文章

      网友评论

          本文标题:阻止事件传播

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