zoukankan      html  css  js  c++  java
  • [JavaScript]继续学习DOM事件模型

    继续学习DOM事件模型

    继续深入一下对于DOM事件模型的学习,理解事件捕获与冒泡。

    假设我们有一个需求,点击之后浮层出现;点击别处浮层消失。

    <!DOCTYPE html>
    <html>
    <head>
    <script src="//code.jquery.com/jquery-1.9.1.min.js"></script>
      <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>
    
    .wrapper {
      position: relative;
      display: inline-block;
    }
    .popover {
      border:1px solid red;
      position: absolute;
      left:100%;
      top: 0;
      white-space: nowrap;
      padding: 10px;
      margin-left: 10px;
      background-color: #fff;
      display: none;
    }
    .popover::before {
      position: absolute;
      border:10px solid transparent;
      border-right-color:red;
      right:100%;
      top:5px;
      content:''
    }
    .popover::after {
      position: absolute;
      border:10px solid transparent;
      border-right-color:white;
      right:100%;
      top:5px;
      content:'';
      margin-right: -1px;
    }
    

    给按钮添加事件:

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

    按钮点击之后,浮层出现。

    如果要设置成点击别处之后浮层消失,该怎么操作?

    思路1:监听body,当body被点击后,浮层消失

    document.body.addEventListener('click',function(){
        console.log(1)
        popover.style.display = 'none'
    })
    

    点击图中的空白区域,完全没有响应:

    点击后无反应

    解决方法:给我们的body添加一个border,看看我们的点击区域是不是在body内:

    body {border:1px solid red}
    

    body所在的位置

    果然,因为body的高度是由其内部文档流元素的总体高度决定的,所以body的高度如图所示。我们只有点击了红色框内的范围,点击事件才会响应。

    所以监听body是有可能会因为body高度不够而达不到需要的效果的。

    思路2:监听document

    修改代码:

    document.addEventListener('click',function(){
        console.log(1)
        popover.style.display = 'none'
    })
    

    监听document

    可以看到无论点击哪里,1都会被打印出来。

    但是问题又来了:我们的点击按钮失效了。

    事件传播过程

    我们知道,事件传播顺序是先执行捕获,再执行冒泡,如果addEventListener()没有指定第三个参数为true或者false,则默认为false,即表示事件的回调函数会在事件冒泡阶段执行。

    在这个过程中,两个事件都没有指定第三个参数,即都为false。当监听队列从document捕获到button,他们并没有做出反应,因为他们的回调函数不是在事件捕获阶段执行的;当从button冒泡到document时,他们的回调函数才会被触发,所以是先执行button的回调函数,运行popover.style.display = 'block',再执行document的回调函数,执行popover.style.display = 'none',他的样式是先被显示,然后马上被隐藏,所以看起来像是点击按钮失效了。

    事件传播

    我们可以验证这一过程

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

    运行顺序

    看到控制台先打印出2,再打印出1。

    阻止传播

    可以用阻止传播来解决这个问题

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

    浮层就出来了:

    阻止冒泡

    没错,看似可以接受的方案,问题又来了,如果点击按钮后出现的这个浮层,它里面有内容,而我们又想选择里面的内容(比如一个CheckBox),但是这个浮层现在一点击就会消失,该怎么办呢?

    让他的父元素阻止冒泡

    wrapper.addEventListener('click',function(e){
        e.stopPropagation()
    })
    

    这样我们就可以点击浮层而不让他消失了。

    优化

    • 阻止冒泡,并且只有当按钮被点击之后document才添加对点击事件的的一次监听,document对于其他点击事件不监听,节省内存。
    $(clickMe).on('click',function(){
      $(popover).show()
      $(document).one('click',function(){
        $(popover).hide()
    })
    })
    $(wrapper).on('click',function(e){
      e.stopPropagation()
    })
    
    
    • 或者添加setTimeout(),等一会(冒泡过程结束后)再把document的click事件添加进去:
    $(clickMe).on('click',function(){
      $(popover).show()
      console.log('show')
      setTimeout(function(){
          console.log('添加one click')
          $(document).one('click',function(){
              console.log('点击按钮后这里不会被执行,因为冒泡通知过程已经结束')
            $(popover).hide()
        })
      },0)
    })
    

    冒泡过程已结束

    document回过头来处理

    总结:

    • W3C制定了统一的标准-先捕获再冒泡

    • addEventListener有三个参数:element.addEventListener(event, function, useCapture)

      • 第一个参数是需要绑定的事件
      • 第二个参数是触发事件后要执行的函数
      • 第三个参数默认值是false,表示在事件冒泡阶段调用事件处理函数;如果参数为true,则表示在事件捕获阶段调用处理函数。
    • 传播过程可以用stopPorpagation()来阻止(但是要注意选择合适的节点使用)

    本人文笔不好,关于事件的更详细的说明可以参考segmentfault上的这篇博文


    更新:

    点击按钮弹出浮层
    点击别处关闭浮层
    点击浮层时,浮层不得关闭
    再次点击按钮,浮层消失

    //其实很简单,只是加个判断
    $(clickMe).on('click',function(){
      if (popover.style.display == 'block') {
        $(popover).hide()
      } else {
        $(popover).show()
        setTimeout(function(){
          console.log('添加one click')
          $(document).one('click',function(){
              console.log('点击按钮后这里不会被执行,因为冒泡通知过程已经结束')
            $(popover).hide()
        })
      },0)
      }
    })
    
  • 相关阅读:
    《团队-爬取豆瓣Top250-团队一阶段互评》
    团队-爬虫豆瓣top250项目-开发文档
    结对总结
    课后作业-阅读任务-阅读提问-2
    2017-10-06-构建之法:现代软件工程-阅读笔记
    结对-结对编项目贪吃蛇-开发过程
    团队-爬取豆瓣电影TOP250-开发环境搭建过程
    个人-GIT使用方法
    课后作业-阅读任务-阅读提问-1
    课后作业-阅读任务-阅读笔记-1
  • 原文地址:https://www.cnblogs.com/No-harm/p/9588411.html
Copyright © 2011-2022 走看看