梦应逍遥 发表于 2025-2-16 05:21:16

【JavaScript】Web API事件流、事件委托

目次

1.事件流
1.1 事件流和两个阶段说明
1.2 事件捕捉
1.3 事件冒泡
1.4 制止冒泡
1.5 解绑事件
L0 事件解绑
L2 事件解绑
鼠标经过事件的区别
两种注册事件的区别
2.事件委托
案例     tab栏切换改造
3.其他事件
3.1 页面加载事件
3.2 页面滚动事件
3.2 页面滚动事件-获取位置
案例   页面滚动显示隐蔽侧边栏​编辑
3.3 页面尺寸事件
4. 元素尺寸于位置
4.1 元素尺寸于位置-尺寸
案例     仿京东固定导航栏案例
获取位置:
案例    电梯导航
总结不易~本章节对我的劳绩很大,希望对你也是~!!!
本节所用的全部资料,图片代码全在我的gitee内里:我的gitee堆栈
1.事件流

1.1 事件流和两个阶段说明

   目标:可以或许说出事件流经过的2个阶段           事件流指的是事件完整执行过程中的流动路径                  https://i-blog.csdnimg.cn/direct/b5246a130755416fbadbf096e3ff3d60.png                 说明:假设页面里有个div,当触发事件时,会经历两个阶段,分别是捕捉阶段、冒泡阶段                 简朴来说:捕捉阶段是 从父到子 冒泡阶段是从子到父                 现实工作都是使用事件冒泡为主      


1.2 事件捕捉

   目标:简朴了解事件捕捉执行过程   事件捕捉概念:                从DOM的根元素开始去执行对应的事件 (从外到里)      事件捕捉需要写对应代码才气看到结果      代码:    DOM.addEventListenter(事件类型,事件处理函数,是否使用捕获机制(true))   https://i-blog.csdnimg.cn/direct/718276171b714537903501573ad3584b.png         当我们给多个事件使用同名事件,而且第三个参数都是true的情况下,比如这里点击 “儿子” 这个盒子,事件流就会从最外层的盒子开始从大方向往下探求,直到找到 “儿子” 这个盒子才停止            说明:                    addEventListener第三个参数传入   true   代表是捕捉阶段触发(很少使用)                 若传入false代表冒泡阶段触发,默认就是false                 如果用 L0 事件监听,则只有冒泡阶段,没有捕捉             1.3 事件冒泡

   目标:可以或许说出事件冒泡的执行过程      事件冒泡概念:                当一个元素的事件被触发时,同样的事件将会在该元素的全部祖先元素中依次被触发。这一过程被称为事件冒泡   https://i-blog.csdnimg.cn/direct/e1f3db53c0864dd6abef7b963323f3e8.png         此时任然点击的是“儿子”盒子,冒泡就往最大开始冒泡,从当前点击盒子的层数开始,一直到最大层竣事~                 简朴明白:当一个元素触发事件后,会依次向上调用全部父级元素的   同名事件          事件冒泡是默认存在的            L2事件监听第三个参数是 false,或者默认都是冒泡            1.4 制止冒泡

   目标:可以或许写出制止冒泡的代码                  问题:    因为默认就有冒泡模式的存在,所以容易导致事件影响到父级元素               需求:    若想把事件就限定在当前元素内,就需要制止事件冒泡               条件:    制止事件冒泡需要拿到事件对象              语法:      事件对象.stopPropagation()https://i-blog.csdnimg.cn/direct/9b7b0589b3e441f7a0016fd8a4d767d4.png
       点击“儿子”盒子就会被制止向上冒泡传播       使用事件对象来制止默认行为:    <!-- form表单 提供提交地址 -->
<form action="http://www.itcast.cn">
    <input type="submit" value="免费注册">

    <a href="http://www.baidu.com">百度一下</a>
</form>

<script>
    const form = document.querySelector('form')
    // e是事件对象
    form.addEventListener('submit', function (e) {
      //阻止默认行为 提交
      e.preventDefault()
    })

    const a = document.querySelector('a')
    a.addEventListener('click', function (e) {
      e.preventDefault()
    })
</script>       留意:   此方法可以阻断事件流动传播,不光在冒泡阶段有用,捕捉阶段也有用      

1.5 解绑事件

L0 事件解绑

   on事件方式,直接使用null覆盖偶就可以实现事件的解绑https://i-blog.csdnimg.cn/direct/abb58435de144bc79ff80f5027211265.png    此时该事件已经被解绑后,再次点击就会不起作用
L2 事件解绑

       addEventListener方式,必须使用:      removeEventListener(事件类型, 事件处置惩罚函数, [获取捕捉或者冒泡阶段])    https://i-blog.csdnimg.cn/direct/b5640bbb09394fb0b96429a84f66f016.png
      此时不会再有弹窗,而是btn事件直接被移除         留意:匿名函数无法被解绑   


鼠标经过事件的区别

   鼠标经过事件:           mouseover 和 mouseout 会有冒泡结果   https://i-blog.csdnimg.cn/direct/2e332f9fdfbd4525a550a71bb080c511.png                           mouseenter 和 mouseleave 没有冒泡结果 (保举)   

两种注册事件的区别

   传统on注册(L0)              同一个对象,反面注册的事件会覆盖前面注册(同一个事件)              直接使用null覆盖偶就可以实现事件的解绑              都是冒泡阶段执行的      事件监听注册(L2)              语法: addEventListener(事件类型, 事件处置惩罚函数, 是否使用捕捉)              反面注册的事件不会覆盖前面注册的事件(同一个事件)              可以通过第三个参数去确定是在冒泡或者捕捉阶段执行              必须使用removeEventListener(事件类型, 事件处置惩罚函数, 获取捕捉或者冒泡阶段)              匿名函数无法被解绑   

2.事件委托

   事件委托是使用事件流的特征办理一些开发需求的知识技巧              优点:减少注册次数,可以进步程序性能              原理:事件委托实在是使用事件冒泡的特点。              给   父元素注册事件   ,当我们触发子元素的时候,会冒泡到父元素身上,从而触发父元素的事 件              实现:事件对象.target. tagName 可以获得真正触发事件的元素      https://i-blog.csdnimg.cn/direct/3f0c7590b0894c37be6493340da5dabc.png    ulli    ul.addEventListener('click' , function(){})   执行父级点击事件


案例     tab栏切换改造

从我的gitee堆栈获取资源:我的gitee
   需求:优化程序,将tab切换案例改为事件委托写法    https://i-blog.csdnimg.cn/direct/eacb8f007e5448ab811e1f6430ade125.png    思绪:      ①:给a的父级 注册点击事件,采取事件委托方式      ②: 如果点击的是A , 则举行排他头脑,删除添加类      ③: 留意判定的方式 使用 e.target.tagName      ④: 因为没有索引号了,所以这里我们可以自定义属性,给5个链接添加序号      ⑤: 下面大盒子获取索引号的方式 e.target.dataset.id 号, 然后举行排他头脑       核心代码:https://i-blog.csdnimg.cn/direct/b16ba616fee4415f8bc9c7a339033e82.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>tab栏切换</title>
<style>
    * {
      margin: 0;
      padding: 0;
    }

    .tab {
      width: 590px;
      height: 340px;
      margin: 20px;
      border: 1px solid #e4e4e4;
    }

    .tab-nav {
      width: 100%;
      height: 60px;
      line-height: 60px;
      display: flex;
      justify-content: space-between;
    }

    .tab-nav h3 {
      font-size: 24px;
      font-weight: normal;
      margin-left: 20px;
    }

    .tab-nav ul {
      list-style: none;
      display: flex;
      justify-content: flex-end;
    }

    .tab-nav ul li {
      margin: 0 20px;
      font-size: 14px;
    }

    .tab-nav ul li a {
      text-decoration: none;
      border-bottom: 2px solid transparent;
      color: #333;
    }

    .tab-nav ul li a.active {
      border-color: #e1251b;
      color: #e1251b;
    }

    .tab-content {
      padding: 0 16px;
    }

    .tab-content .item {
      display: none;
    }

    .tab-content .item.active {
      display: block;
    }
</style>
</head>

<body>
<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"><img src="./images/tab00.png" alt="" /></div>
      <div class="item"><img src="./images/tab01.png" alt="" /></div>
      <div class="item"><img src="./images/tab02.png" alt="" /></div>
      <div class="item"><img src="./images/tab03.png" alt="" /></div>
      <div class="item"><img src="./images/tab04.png" alt="" /></div>
    </div>
</div>

<script>
    // 1.采取ul父元素 因为ul只有一个
    const ul = document.querySelector('.tab-nav ul')
    const as = document.querySelectorAll('.tab-nav a')
    // 2.添加事件
    ul.addEventListener('click', function (e) {
      // console.dir(e.target.tagName === 'DIV')
      // e.target 是我们点击的对象

      // 我们只有点击a链接才会进行添加和删除操作
      // console.log(e.target.tagName)
      if (e.target.tagName === 'A') {
      document.querySelector('.tab-nav .active').classList.remove('active')
      // this指向ul 不能用this
      // 当前元素要添加active 是e.active
      e.target.classList.add('active')

      //下面大盒子模块
      // console.log(e.target.dataset.id)
      // 这里注意隐式转换,将字符型转换为数字型
      const id = +e.target.dataset.id
      // 移除原来的active
      document.querySelector('.tab-content .active').classList.remove('active')
      // 对应大盒子 添加active
      document.querySelector(`.tab-content .item:nth-child(${id + 1})`).classList.add('active')
      }
    })

</script>

</body>

</html>




3.其他事件

3.1 页面加载事件

    加载外部资源(如图片、外联CSS和JavaScript等)加载完毕时触发的事件    为什么要学?           有些时候需要等页面资源全部处置惩罚完了做一些事变           老代码喜欢把 script 写在 head 中,这时候直接找 dom 元素找不到(script代码再html上面)     事件名:load    监听页面全部资源加载完毕:           给 window 添加 load 事件https://i-blog.csdnimg.cn/direct/fc1c3af9e2b34b1a92545ab3fb1c2743.png
   留意:不光可以监听整个页面资源加载完毕,也可以针对某个资源绑定load事件
   当初始的 HTML 文档被完全加载和剖析完成之后,DOMContentLoaded 事件被触发,而无需等候样式表、图像等完全加载     事件名:DOMContentLoaded     监听页面DOM加载完毕:           给 document 添加 DOMContentLoaded 事件https://i-blog.csdnimg.cn/direct/e26f90188e7244d58015f23330f0ef80.png
总结:
      1. 页面加载事件有哪两个?如何添加?          load 事件              监听整个页面资源给    window    加          DOMContentLoaded              给    document    加              无需等候样式表、图像等完全加载   
3.2 页面滚动事件

   滚动条在滚动的时候持续触发的事件   为什么要学?           许多网页需要检测用户把页面滚动到某个区域后做一些处置惩罚, 比如固定导航栏,比如返回顶部      事件名:scroll       监听整个页面滚动:https://i-blog.csdnimg.cn/direct/042bee8d75bf45129288a2c2f1fe096e.png
           给 window 或 document 添加 scroll 事件   监听某个元素的内部滚动直接给某个元素加即可

3.2 页面滚动事件-获取位置

   scrollLeft和scrollTop (属性)https://i-blog.csdnimg.cn/direct/82d73d85309e40fcb00b446cee5255d7.png               获取被卷去的大小              获取元素内容往左、往上滚出去看不到的间隔              这两个值是可   读写   的       只管在scroll事件内里获取被卷去的间隔

   3.2 页面滚动事件-获取位置   开发中,我们经常检测页面滚动的间隔,比如页面滚动100像素,就可以显示一个元素,或者固定一个元素
留意事项
document.documentElement HTML 文档返回对象为HTML元素
总结:
   1. 被卷去的头部或者左侧用谁人属性?是否可以读取和修改?           scrollTop / scrollLeft           可以读取,也可以修改(赋值)   2. 检测页面滚动的头部间隔(被卷去的头部)用谁人属性?           document.documentElement.scrollTop

案例   页面滚动显示隐蔽侧边栏

 从我的gitee堆栈获取资源:我的gitee
https://i-blog.csdnimg.cn/direct/76d10c24d2e94821b09eca9643aaa1ad.png

   需求:当页面滚动大于300像素的间隔时候,就显示侧边栏,否则隐蔽侧边栏   分析:   ①:需要用到页面滚动事件   ②:检测页面被卷去的头部,如果大于300,就让侧边栏显示   ③:显示和隐蔽共同css过渡,使用opacity加渐变结果   https://i-blog.csdnimg.cn/direct/6f996880122545eeb27253cd393b532a.png
elevator.style.opacity:


[*]这是用来设置 HTML 元素透明度的属性,opacity 的值范围是从 0(完全透明)到 1(完全不透明)。
https://i-blog.csdnimg.cn/direct/7ee68dafd0b24ffab36b63b029c5dce7.png
点击顶部后,设置页面滚动的间隔是0
document.documentElement.scrollTop = 0

3.3 页面尺寸事件

   会在窗口尺寸改变的时候触发事件:           resizehttps://i-blog.csdnimg.cn/direct/f9d8f458b4e34c1cae599fb55825f453.png

   检测屏幕宽度:https://i-blog.csdnimg.cn/direct/5913136caaf641b287a206821c3403a1.png
当一个盒子不晓得高度和宽度的时候可以直接用来测得盒子的宽度和高度:
const div = document.querySelector('div')
    console.log(div.clientWidth)
    console.log(div.clientHeight)   获取宽高:           获取元素的可见部门宽高(不包含边框(border),margin,滚动条等)           clientWidth和clientHeight



4. 元素尺寸于位置

   使用场景:           前面案例滚动多少间隔,都是我们本身算的,最好是页面滚动到某个元素,就可以做某些事。           简朴说,就是通过js的方式,得到元素在页面中的位置           这样我们可以做,页面滚动到这个位置,就可以做某些操作,省去计算了
4.1 元素尺寸于位置-尺寸

   获取宽高:           获取元素的自身宽高、包含元素自身设置的宽高、padding、border           offsetWidth和offsetHeight           获取出来的是数值,方便计算           留意: 获取的是可视宽高, 如果盒子是隐蔽的,获取的结果是0   获取位置:           获取元素间隔本身定位父级元素的左、上间隔           offsetLeft和offsetTop 留意是只读属性https://i-blog.csdnimg.cn/direct/bd6bee60df1d4194acf4946b7ac7d7c5.png
https://i-blog.csdnimg.cn/direct/485cce9e3bbe4465ba1e630774a9f8ae.png
总结:
   1. offsetWidth和offsetHeight是得到元素什么的宽高?           内容 + padding + border   2. offsetTop和offsetLeft 得到位置以谁为准?           带有定位的父级           如果都没有则以 文档左上角 为准

   案例     仿京东固定导航栏案例

 从我的gitee堆栈获取资源:我的gitee
   需求:当页面滚动到秒杀模块,导航栏自动滑入,否则滑出      分析:      ①:用到页面滚动事件      ②:检测页面滚动大于等于 秒杀模块的位置 则滑入,否则滑出      ③:重要移动的是秒杀模块的顶部位置    https://i-blog.csdnimg.cn/direct/b4f8c3b91643412f93af76ccb3b81730.png

获取位置:

   2.   element.getBoundingClientRect()   方法返回元素的大小及其相对于视口的位置https://i-blog.csdnimg.cn/direct/89900ed99557421baf232474a51bb7f0.png
   总结:         属性      作用说明         scrollLeft和scrollTop               被卷去的头部和左侧               共同页面滚动来用,      可读写               clientWidth 和 clientHeight               获得元素宽度和高度               不包含border,margin,滚动条 用于js               获取元素大小,只读属性               offsetWidth和offsetHeight                获得元素宽度和高度               包含border、padding,滚动条等,只读               offsetLeft和offsetTop               获取元素间隔本身定位父               级元素的左、上间隔               获取元素位置的时候使用,只读属性      


案例    电梯导航

 从我的gitee堆栈获取资源:我的gitee
   需求:点击不同的模块,页面可以自动跳转不同的位置   模块分析:   ①:页面滚动到对应位置,导航显示,否则隐蔽模块   ②:点击导航对应小模块,页面 会跳到对应大模块位置   ③:页面滚动到对应位置,电梯导航对应模块自动发生厘革
<script>
    // 第一大模块 页面滑动可以显示和隐藏
    (function () {
      // 设置标志位 控制滚动事件的执行


      // 获取元素
      const entry = document.querySelector('.xtx_entry')
      const elevator = document.querySelector('.xtx-elevator')
      // 1. 当页面滚动大于300像素就显示电梯导航
      // 2. 给页面添加滚动事件
      window.addEventListener('scroll', function () {
      const n = document.documentElement.scrollTop
      // if (n >= 300) {
      //   elevator.style.opacity = 1
      // }
      // else {
      //   elevator.style.opacity = 0
      // }

      elevator.style.opacity = n >= entry.offsetTop ? 1 : 0
      })

      const backTop = document.querySelector('#backTop')
      backTop.addEventListener('click', function () {
      document.documentElement.scrollTop = 0
      // 方法二:
      // window.scrollTo(x, y)
      window.scrollTo(0, 0)
      })
    })();

    let isClicking = false

    function fn(e) {
      // console.log(11)
      if (e.target.tagName === 'A' && e.target.dataset.name) {
      isClicking = true

      // 不能直接获取这个类进行移除 因为找不到就会报错
      // document.querySelector('.xtx-elevator-list .active').classList.remove('active')

      // 判断 如果有这个类就移除 如果没有就返回null 不执行移除
      const old = document.querySelector('.xtx-elevator-list .active')
      // console.log(old)
      // 判断
      if (old) old.classList.remove('active')
      e.target.classList.add('active')
      // console.log(e)
      // console.log(e.target)

      // 拿到当前小盒子的自定义属性
      // console.log(e.target.dataset.name)
      // 根据小盒子的自定义属性值 去选择 对应的大盒子
      // console.log(document.querySelector(`.xtx_goods_${e.target.dataset.name}`))
      // 获得对应大盒子的offsetTop的值
      const top = document.querySelector(`.xtx_goods_${e.target.dataset.name}`).offsetTop
      // console.log(top)
      // 让页面滚动就只有这一句话
      document.documentElement.scrollTop = top
      }
    }

    // 第二第三都放到另一个执行函数里面
    (function () {

      // 2.点击页面可以滑动
      const list = document.querySelector('.xtx-elevator-list')

      list.addEventListener('click', fn)

      // 3. 页面滚动,可以根据大盒子 选 小盒子 添加active
      window.addEventListener('scroll', function (e) {
      if (isClicking) {
          isClicking = false
          return;
      }
      // 3.1 先移除类(判断是否存在) 然后再添加
      const old = document.querySelector('.xtx-elevator-list .active')
      // console.log(old)
      if (old) old.classList.remove('active')
      // 这里利用索引的话中间是不能出现多余的盒子的
      // const boxs = document.querySelectorAll('.xtx_panel')
      const n = document.documentElement.scrollTop
      // console.log(n)
      // console.log(boxs.offsetTop)
      // console.log(boxs.offsetTop)
      // console.log(boxs.offsetTop)
      // console.log(boxs.offsetTop)

      const news = document.querySelector('.xtx_goods_new')
      const popular = document.querySelector('.xtx_goods_popular')
      const brand = document.querySelector('.xtx_goods_brand')
      const topic = document.querySelector('.xtx_goods_topic')


      if (n >= news.offsetTop && n < popular.offsetTop) {
          // 选择第一个盒子
          document.querySelector('').classList.add('active')
      }
      else if (n >= popular.offsetTop && n < brand.offsetTop) {
          document.querySelector('').classList.add('active')
      }
      else if (n >= brand.offsetTop && n < topic.offsetTop) {
          document.querySelector('').classList.add('active')
      }
      else if (n >= topic.offsetTop) {
          document.querySelector('').classList.add('active')
      }
      })
    })();

</script>


总结不易~本章节对我的劳绩很大,希望对你也是~!!!









免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
页: [1]
查看完整版本: 【JavaScript】Web API事件流、事件委托