zoukankan      html  css  js  c++  java
  • JS 事件冒泡和事件捕获

    写在前面

    W3C规定DOM事件流(event flow )存在三个阶段:事件捕获阶段、处于目标阶段、事件冒泡阶段。dom标准事件流的触发的先后顺序为:先捕获再冒泡,即当触发dom事件时,会先进行事件捕获,捕获到事件源之后通过事件传播进行事件冒泡。

    event flow

    对事件冒泡和捕捉的解释

    事件冒泡

    在本示例中,当我们点击孙子div的时候浏览器就会去检查这个div身上没有绑定事件,如果有就执行它;然后再检查它的父元素(儿子div)身上有没有绑定事件,如果有就执行;然后再检查儿子div的父元素身上有没有绑定事件,如果有就执行,依此类推,直到html根元素,这样由内到外的向外触发事件就叫做事件冒泡。

    let parent = document.querySelector('.parent')
    let child = document.querySelector('.child')
    let grandson = document.querySelector('.grandson')
    
    parent.addEventListener('click', function(e){
      console.log('爸爸')
    })
    
    child.addEventListener('click', function(e){
      console.log('儿子')
    })
    
    grandson.addEventListener('click', function(e){
      console.log('孙子')
    })
    
    

    事件冒泡

    事件冒泡demo在线预览

    事件捕获

    事件捕获刚好和事件冒泡相反,事件触发时是先检查最外层祖先html元素没有绑定事件,如果有则执行它,然后在检查,然后,它移动到<html>中单击元素的下一个祖先元素,并执行相同的操作,然后是单击元素再下一个祖先元素,依此类推,直到到达实际点击的元素,就和剥洋葱一样。

    let parent = document.querySelector('.parent')
    let child = document.querySelector('.child')
    let grandson = document.querySelector('.grandson')
    
    parent.addEventListener('click', function(e){
      console.log('爸爸')
    }, true) // 注意:这个地方多了一个参数
    
    child.addEventListener('click', function(e){
      console.log('儿子')
    }, true) // 注意:这个地方多了一个参数
    
    grandson.addEventListener('click', function(e){
      console.log('孙子')
    }, true) // 注意:这个地方多了一个参数
    

    事件捕获

    事件捕获demo在线预览

    理解DOM事件的例子

    addEventListener

    在开发中,我们都是通过addEventListener给元素添加事件处理代码,可以同时给同一个元素添加多个事件处理程序代码

    grandson.addEventListener('click', function(e){
      console.log('孙子')
    })
    grandson.addEventListener('click', function(e){
      console.log('孙子')
    })
    // 控制台会打印2次孙子
    

    默认情况下,所有事件处理程序都是在冒泡阶段注册的,如果想在捕获阶段注册一个事件,那么你可以通过使用addEventListener()注册您的处理程序,并将可选的第三个参数useCapture设置为true,capture 就是捕获的意思

    grandson.addEventListener('click', function(e){
      console.log('孙子')
    }, true) // 第三个参数 useCapture 设置为 true 
    

    阻止默认行为

    e.preventDefault()

    参考

    阻止事件传播

    e.stopPropagation()

    实战

    点击别处关闭浮层Demo

    <!DOCTYPE html>
    <html>
    <head>
      <meta charset="utf-8">
      <meta name="viewport" content="width=device-width">
      <title>JS Bin</title>
    </head>
    <body>
      <div class="container">
        <button id="btnOpen">打开浮动层</button>    
        <div class="popover" id="popoverlayer">
          这里是浮动层中显示的内容
          <br>
          <label ><input type="checkbox" />一个checkbox</label> 
        </div>
      </div>
    </body>
    </html>
    
    body{
      border: 5px solid red
    }
    .container{
      border: 1px solid blue;
      position: relative;
      display: inline-block
    }
    .popover{
      display: none;
      position: absolute;
      top: 0;
      left: 100%;
       200px;
      height: 200px;  
      margin-left: 10px;
      background: #fff;  
      border: 1px solid #000;
    }
    .popover::before{
      content: '';
      position: absolute;
      top: 5px;
      left: -20px;
      border: 10px solid transparent;
      border-right: 10px solid #000;
    }
    .popover::after{
      content: '';
      position: absolute;
      top: 5px;
      left: -19px;
      border: 10px solid transparent;
      border-right: 10px solid #fff;
    }
    .popover.active{
      display: block;
    }
    
    let container = document.querySelector('.container');
    let btnOpen = document.querySelector('#btnOpen');
    let popoverlayer = document.querySelector('#popoverlayer');
    
    btnOpen.addEventListener('click', function(e){
      console.log('click');
      popoverlayer.classList.add('active');
      e.stopPropagation();
      
      // 方法2:注册 document click 只执行一次
    //   document.addEventListener('click', function(){
    //     console.log('document once click');
    //     popoverlayer.classList.remove('active');
    //   }, {once: true})
      
    })
    
    popoverlayer.addEventListener('click', function(e){
      console.log('layer click');
      // 阻止传播,为了避免点击浮层时不关闭
      e.stopPropagation();
      
    })
    
    // 注意这里监听的是 document,而不是body
    // 因为当body的高度不够的时候,点击浮层的外部是不会关闭的
    // 方法1
    document.addEventListener('click', function(){
      console.log('document click');
      popoverlayer.classList.remove('active');
    })
    

    关闭浮层demo在线预览

    事件委托

    使用事件委托实现点击点击不同的 li 元素 来获取歌曲的 id

    <!DOCTYPE html>
    <html>
    <head>
      <meta charset="utf-8">
      <meta name="viewport" content="width=device-width">
      <title>JS Bin</title>
    </head>
    <body>
    <ul class="musiclist">
      <li data-id="1"><span>歌曲1</span><span>作者1</span></li>
      <li data-id="2"><span>歌曲2</span><span>作者2</span></li>
      <li data-id="3"><span>歌曲3</span><span>作者3</span></li>
      <li data-id="4"><span>歌曲4</span><span>作者4</span></li>
      <li data-id="5"><span>歌曲5</span><span>作者5</span></li>
    </ul>
    </body>
    </html>
    
    *{
      padding: 0;
      margin:0;  
      box-sizing: border-box;
    }
    body{
      border: 1px solid #000;
      padding: 10px;
    }
    ul,ol{
      list-style: none;
    }
    .musiclist{
      
    }
    .musiclist li{
      display: block;
      padding: 15px 10px;
      background: #fff;
      border-bottom: 1px solid #ddd;
    }
    .musiclist li:hover{
      background: #eee;
    }
    .musiclist li>span:first-child{
      display: inline-block;
      font-size: 16px;
      color: #000;
    }
    .musiclist li>span:nth-child(2){
      font-size: 10px;
      color: #000;
      margin-left: 10px;
    }
    
    
    let musiclist = document.querySelector('.musiclist');
    // 事件处理程序绑定在歌曲列表上面
    musiclist.addEventListener('click', function(e){
      console.log('target', e.target);
      console.log('currentTarget', e.currentTarget);
      
      let target = e.target;
      
      // 当点击的元素是 li 时,才能获取到 dataset 属性
      if(target.nodeName === 'LI'){
        console.log('当前歌曲ID是' + target.dataset.id)
      }
    })
    

    事件委托demo

    e.target 和 e.currentTarget 的区别

    e.target 是当前触发事件的元素

    e.currentTarget 是事件绑定的元素

    e.target 和 e.currentTarget 的区别

    参考

    常见的事件列表

    事件介绍

    EventListener

    Event Flow

    编写一个EventUtil工具类实现事件管理兼容

  • 相关阅读:
    通过索引优化sql
    索引概述
    Spring整合Mybatis
    Mybatis逆向工程
    Mybatis级联
    Mybatis动态语句
    Mybatis—curd
    (转)最大似然估计&贝叶斯估计
    筛法求质——poj2262&2909
    (转)poj1182食物链
  • 原文地址:https://www.cnblogs.com/wubh/p/javascript-event-flow.html
Copyright © 2011-2022 走看看