JavaScript之web APIs 事件流 事件委托

74 阅读3分钟

事件流

事件完整执行过程中的流动路径,事件流的两个阶段:捕获阶段和冒泡阶段

截屏2023-04-26 12.03.05.png

捕获阶段

从大到小 从父到子

    //第三个参数传入true代表捕获阶段触发,很少使用
    //第三个参数默认为false,false代表冒泡阶段触发
    DOM.addEventListener(事件类型,事件处理函数,是否使用捕获机制)
    <div class="father">
        <div class="son"></div>
    </div>
    <script>
    //捕获阶段 从大到小依次展示
        document.addEventListener('click',function(){
            alert('我是爷爷')
        },true)
        const fa = document.querySelector('.father')
        const son = document.querySelector('.son')
        fa.addEventListener('click',function(){
            alert('我是爸爸')
        },true)
        son.addEventListener('click',function(){
            alert('我是儿子')
        },true)
    </script>

冒泡阶段

从小到大 从子到父 (实际工作中使用事件冒泡为主)

当元素触发事件后,同名事件从小到大不断向上调用所有祖先元素

事件冒泡默认存在,第三个参数默认为false,false是事件冒泡

    <div class="father">
        <div class="son"></div>
    </div>
    <script>
    //冒泡阶段,从小到大依次展示
        document.addEventListener('click',function(){
            alert('我是爷爷')
        })
        const fa = document.querySelector('.father')
        const son = document.querySelector('.son')
        fa.addEventListener('click',function(){
            alert('我是爸爸')
        })
        son.addEventListener('click',function(){
            alert('我是儿子')
        })
    </script>

阻止事件冒泡

由于默认就有冒泡模式的存在,容易导致事件影响父级元素,需阻断事件流动传播

事件对象.stopPropagation()

    <div class="father">
        <div class="son"></div>
    </div>
    <script>
        document.addEventListener('click',function(){
            alert('我是爷爷')
        })
        const fa = document.querySelector('.father')
        const son = document.querySelector('.son')
        fa.addEventListener('click',function(){
            alert('我是爸爸')
        })
        son.addEventListener('click',function(e){
            alert('我是儿子')
            //阻断事件冒泡
            e.stopPropagation()
        })
    </script>

阻止默认行为

e.preventDefault()

    <a href="http://www.baidu.com" >百度一下</a>
    <script>
        const a= document.querySelector('a')
        a.addEventListener('click',function(e){
           e.preventDefault()
        })
    </script>

解绑事件

1.on事件方式,直接用null覆盖

        //绑定事件
        btn.onclick = function(){
            alert('点击一次')
        }
        //解绑事件
        btn.onclick = null

2.addEventListener监听事件方式,用removeEventListener

        function fn(){
            alert('点击一次')
        }
        btn.addEventListener('click',fn)
        //匿名函数无法被解绑
        btn.removeEventListener('click',fn)

事件委托

利用事件冒泡特点,给父元素监听事件,当我们触发子元素时,会向上冒泡查找到父元素,能减少事件监听次数,提高程序性能

找到真正触发的元素 事件对象.target.tagName

截屏2023-04-26 13.04.12.png


 <ul>
        <li class="active">第1个孩子</li>
        <li>第2个孩子</li>
        <li>第3个孩子</li>
        <li>第4个孩子</li>
        <li>第5个孩子</li>
        <p>我是不</p>
    </ul>
    <script>
        const ul = document.querySelector('ul')
        ul.addEventListener('click',function(e){
            //事件委托
            if(e.target.tagName === 'LI'){
            document.querySelector('ul .active').classList.remove('active')
            e.target.classList.add('active')
        }
        })
    </script>

案例 -- tab切换栏进阶版

  <div class="tab">
    <div class="tab-nav">
      <h3>每日特价</h3>
      <ul>
        <li><a class="active" href="javascript:;" data-id="0">精选</a></li>
        <li><a href="javascript:;" data-id="1">美食</a></li>
        <li><a href="javascript:;" data-id="2">百货</a></li>
        <li><a href="javascript:;" data-id="3">个护</a></li>
        <li><a href="javascript:;" data-id="4">预告</a></li>
      </ul>
    </div>
    <div class="tab-content">
      <div class="item active">1<img src="./images/tab00.png" alt="" /></div>
      <div class="item">2<img src="./images/tab01.png" alt="" /></div>
      <div class="item">3<img src="./images/tab02.png" alt="" /></div>
      <div class="item">4<img src="./images/tab03.png" alt="" /></div>
      <div class="item">5<img src="./images/tab04.png" alt="" /></div>
    </div>
  </div>
  <script>
    const ul = document.querySelector('.tab-nav ul')
    ul.addEventListener('click',function(e){
        //事件委托 判断是否是a标签
        if(e.target.tagName === 'A'){
          document.querySelector('.tab-nav ul li .active').classList.remove('active')
          //当前对象
          e.target.classList.add('active')
          //自定义标签属性 dataset 
          const i = +e.target.dataset.id
          document.querySelector('.tab-content .active').classList.remove('active')
          const item = document.querySelectorAll('.tab-content .item')
          item[i].classList.add('active')
        }        
    })
  </script>

页面加载事件

load:监听页面中所有资源css,html,js全部加载完毕,给window添加load事件

      //写在html头部head标签内
      window.addEventListener('load',function(){
            //所有资源加载完毕执行该操作
            const btn = document.querySelector('button')
            btn.addEventListener('click',function(){
                alert('111')
            })
       })

DOMContentLoaded,给document添加,无需等待样式表和图标完全加载

     //写在html头部head标签内
     document.addEventListener('DOMContentLoaded',function(){
        //const btn = document.querySelector('button')
            //btn.addEventListener('click',function(){
                //alert('111')
            })
      })

页面滚动事件

scroll:监听整个页面的滚动,如固定导航栏,返回顶底部

截屏2023-04-26 16.44.09.png

scrollLeftscrollTop属性

        //检测页面的往上滚动距离
         window.addEventListener('scroll',function(){
            // 获取html元素的写法  document.documentElement
            const n = document.documentElement.scrollTop
            //返回为数字型
            console.log(n);
            
        })
         const elevator = document.querySelector('.elevator')
         const entry = document.querySelector('.nav-bar')
        window.addEventListener('scroll',function(){
            // 获取html元素的写法  document.documentElement
            const n = document.documentElement.scrollTop
            //三元判断 向上滚动距离 元素显示或隐藏 
            elevator.style.opacity = n >= entry.offsetTop ? 0 : 1
            //点击按钮返回页面顶部
            const backTop = document.querySelector('.backTop')
            backTop.addEventListener('click',function(){
             document.documentElement.scrollTop = 0
             })
        })
        })

页面尺寸事件

窗口尺寸改变时触发事件

      //resize浏览器窗口大小发生变化时触发
       window.addEventListener('resize',function(){
        console.log('11');
       })

获取元素的可见部分宽高(不包括border,margin,滚动条等,除了包括padding)

clientWidthclientHeight

       //浏览器窗口变化时获取页面的宽度和高度
       window.addEventListener('resize',function(){
          //console.log(document.documentElement.clientWidth)
          console.log(document.documentElement.clientHeight)
       })

元素尺寸与位置

截屏2023-04-26 21.46.28.png

尺寸:获取元素自身的宽高,包含元素自身设置的宽高,padding,border

offsetWidthoffsetHeight

位置:获取元素位于自己定位父级元素的左,上距离

offsetTopoffsetLeft

       const div = document.querySelector('div')
         console.log(div.offsetTop);

2.位置:返回元素的大小,相对于可视区域的位置 getBoundingClientRect()

截屏2023-04-26 21.50.36.png

案例 -- 仿顶部导航固定栏显示隐藏

<!DOCTYPE html>
<html lang="en">
 
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <style>
        * {
            margin: 0;
            padding: 0;
            box-sizing: border-box;
        }
 
        .content {
            overflow: hidden;
            width: 1000px;
            height: 3000px;
            background-color: pink;
            margin: 0 auto;
        }
 
        .backtop {
            display: none;
            width: 50px;
            left: 50%;
            margin: 0 0 0 505px;
            position: fixed;
            bottom: 60px;
            z-index: 100;
        }
 
        .backtop a {
            height: 50px;
            width: 50px;
            background: url(./images/bg2.png) 0 -600px no-repeat;
            opacity: 0.35;
            overflow: hidden;
            display: block;
            text-indent: -999em;
            cursor: pointer;
        }
 
        .header {
            position: fixed;
            top: -80px;
            left: 0;
            width: 100%;
            height: 80px;
            background-color: purple;
            text-align: center;
            color: #fff;
            line-height: 80px;
            font-size: 30px;
            transition: all .3s;
        }
 
        .sk {
            width: 300px;
            height: 300px;
            background-color: skyblue;
            margin-top: 500px;
        }
    </style>
</head>
 
<body>
    <div class="header">我是顶部导航栏</div>
    <div class="content">
        <div class="sk">秒杀模块</div>
    </div>
    <div class="backtop">
        <img src="./images/close2.png" alt="">
        <a href="javascript:;"></a>
    </div>
    <script>
       const sk = document.querySelector('.sk')
       const head = document.querySelector('.header')
       window.addEventListener('scroll',function(){
           const n = document.documentElement.scrollTop
            // if (n >= sk.offsetTop) {
            //     header.style.top = 0
            // } else {
            //     header.style.top = '-80px'
            // }
           //css一定要跟单位
           head.style.top = n >= sk.offsetTop ? 0 : '-80px'
            
       })

    </script>
</body>
 
</html>

案例 -- 仿b站导航tab栏下方滑块切换

        <style>
        .tabs-list {
          display: flex;
        }
        .tabs-list a {
          padding: 0 4.26666667vw;
          white-space: nowrap;
          font-size: 3.73333333vw;
          height: 10.66666667vw;
          line-height: 10.66666667vw;
        }
        .tabs-list .line {
          position: absolute;
          left: 4.26666667vw;
          bottom: 0;
          width: 7.46666667vw;
          height: 0.53333333vw;
          background-color: #fb7299;
          transition: all 0.3s;
        }
        </style>
        
          <div class="tabs-list">
          <a href="#">首页</a>
          <a href="#">动画</a>
          <a href="#">番剧</a>
          <a href="#">果蔬</a>
          <a href="#">音乐</a>
          <a href="#">舞蹈</a>
          <a href="#">鬼畜</a>
          <a href="#">吹鬼</a>
          <!-- 红色线 -->
          <div class="line"></div>
          
   <script>
      const list = document.querySelector('.tabs-list')
      const line = document.querySelector('.line')
      //事件委托
      list.addEventListener('click',function(e){
      //判断点击是否是a标签
         if(e.target.tagName === 'A'){
           //滑块移动距离是当前a标签到最左边的距离
            line.style.transform = `translateX(${e.target.offsetLeft}px)`
         }
      })
  </script>