# day03 - Web APIs

学习目标:

学习事件流,事件委托,其他事件等知识

优化多个事件绑定和实现常见网页交互

# 热身案例

# 1.1 案例:全选文本框 1

需求:用户点击全选,则下面复选框全部选择,取消全选则全部取消

分析:

  • 全选复选框点击,可以得到当前按钮的 checked
  • 把下面所有的小复选框状态 checked,改为和全选复选框一致

image-20220716162426411

# 案例:全选文本框 2

需求:用户点击全选,则下面复选框全部选择,取消全选则全部取消,文字对应变化

分析:

  • 遍历下面的所有的 checkbox, 添加点击事件
  • 检查小复选框选中的个数,是不是等于 小复选框总的个数
  • 把结果给全选按钮
  • 利用 css 复选框选择器 input:checked

image-20220716162659422

# 1. 事件流

# 1.1 事件流和两个阶段说明

目标:能够说出事件流经过的 2 个阶段

  • 事件流指的是事件完整执行过程中的流动路径

    image-20220716162949039

  • 说明:假设页面里有个 div,当触发事件时,会经历两个阶段,分别是捕获阶段、冒泡阶段

  • 简单来说:捕获阶段是 从父到子 冒泡阶段是从子到父

  • 实际工作都是使用事件冒泡为主

# 1.2 事件捕获

目标:简单了解事件捕获执行过程

  • 事件捕获概念:从 DOM 的根元素开始去执行对应的事件 (从外到里)

  • 事件捕获需要写对应代码才能看到效果

  • 代码:

    image-20220716163212562

  • 说明:

    • addEventListener 第三个参数传入 true 代表是捕获阶段触发(很少使用)
    • 若传入 false 代表冒泡阶段触发,默认就是 false
    • 若是用 元素.onclick ,则只有冒泡阶段,没有捕获

# 1.3 事件冒泡

目标:能够说出事件冒泡的执行过程

  • 事件冒泡概念:当一个元素的事件被触发时,同样的事件将会在该元素的所有祖先元素中依次被触发。这一过程被称为事件冒泡
  • 简单理解:当一个元素触发事件后,会依次向上调用所有父级元素的 同名事件
  • 事件冒泡是默认存在的
  • 事件监听 addEventListener 第三个参数是 false,或者默认都是冒泡

# 1.4 阻止冒泡

目标:能够写出阻止冒泡的代码

  • 问题:因为默认就有冒泡模式的存在,所以容易导致事件影响到父级元素
  • 需求:若想把事件就限制在当前元素内,就需要阻止事件冒泡
  • 前提:阻止事件冒泡需要拿到事件对象
  • 语法: 事件对象.stopPropagation()

# 1.5 阻止默认行为

  • 问题:有时我们需要阻止一些默认行为,比如说表单提交,a 链接跳转

  • 语法: 事件对象.preventDefault()

    示例代码:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    const form = document.querySelector('form')
    form.addEventListener('submit', function (e) {
    // 阻止默认行为 提交
    e.preventDefault()
    })

    const a = document.querySelector('a')
    a.addEventListener('click', function (e) {
    e.preventDefault()
    })

# 1.6 解绑事件

  • addEventListener 方式监听的事件,解绑:removeEventListener (事件类型,事件处理函数,[获取捕获或者冒泡阶段])
  • on 事件方式,直接使用 null 覆盖偶就可以实现事件的解绑

​ 示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<button>点击</button>
<script>
const btn = document.querySelector('button')
function fn() {
alert('点击了')
}
btn.addEventListener('click', fn)
// 事件监听移除解绑
btn.removeEventListener('click', fn)

// btn.onclick = function () {
// alert('点击了')
// // on事件移除解绑
// btn.onclick = null
// }
</script>

注意:<span style="color: red"> 匿名函数无法被解绑 </span>

# 1.7 鼠标经过事件的区别

鼠标经过事件:

  • mouseover 和 mouseout 会有冒泡效果
  • mouseenter 和 mouseleave 没有冒泡效果 (推荐)

# 1.8 两种注册事件的区别

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

# 2. 事件委托

目标:能够说出事件委托的好处

  • 如果同时给多个元素注册事件,我们怎么做的?

    • for 循环注册事件

    • const lis = document.querySelectorAll('ul li')
      for (let i = 0; i < lis.length; i++) {
          lis[i].addEventListener('click', function() {
              console.log('我被点击了')
          })
      }
      <!--code2-->

总结:

  • 事件委托的好处是什么?
  • 事件委托是委托给了谁?父元素还是子元素?
  • 如何找到真正触发的元素?
    • 事件对象.target.tagName

# 2.2 案例 tab 栏切换改造

需求:优化程序,将 tab 切换案例改为事件委托写法

分析:

  • 给 a 的父级 注册点击事件,采取事件委托方式
  • 如果点击的是 a , 则进行排他思想,删除添加类
  • 注意判断的方式 利用 e.target.tagName
  • 因为没有索引号了,所以这里我们可以自定义属性,给 5 个链接添加序号
  • 下面大盒子获取索引号的方式 e.target.dataset.id 号, 然后进行排他思想

# 3. 其它事件

# 3.1 页面加载事件

# 3.1.1 页面加载时间 load
  • 加载外部资源(如图片、外联 CSS 和 JavaScript 等)加载完毕时触发的事件

  • 为什么要学?

    • 有些时候需要等页面资源全部处理完了做一些事情
    • 老代码喜欢把 script 写在 head 中,这时候直接找 dom 元素找不到
  • 事件名:load

  • 监听页面所有资源加载完毕:

    • 给 window 添加 load 事件

    • window.addEventListener('load', function () {
      	// 执行的操作
      })
      <!--code3-->

总结:

  • 页面加载事件有哪两个?如何添加?
    • load 事件: 监听整个页面资源给 window 加
    • DOMContentLoaded
      • 给 document 加
      • 无需等待样式表、图像等完全加载

# 3.2 页面滚动事件

  • 滚动条在滚动的时候持续触发的事件

  • 为什么要学?

    • 很多网页需要检测用户把页面滚动到某个区域后做一些处理, 比如固定导航栏,比如返回顶部
  • 事件名:scroll

  • 监听整个页面滚动:

    • window.addEventListener('scroll', function () {
          // 执行的操作
      })
      <!--code4-->
  • 开发中,我们经常检测页面滚动的距离,比如页面滚动 100 像素,就可以显示一个元素,或者固定一个元素

  • document.documentElement HTML 文档返回对象为 HTML 元素

总结:

  • 被卷去的头部或者左侧用那个属性?是否可以读取和修改?
  • 检测页面滚动的头部距离(被卷去的头部)用那个属性?

# 3.4 案例 小兔鲜页面大于 300 就显示电梯导航

需求:当页面滚动大于 300 像素的距离时候,就显示侧边栏,否则隐藏侧边栏

  • 需要用到页面滚动事件
  • 检测页面被卷去的头部,如果大于 300,就让侧边栏显示
  • 显示和隐藏配合 css 过渡,利用 opacity 加渐变效果

# 3.5 页面滚动事件 - 滚动到指定的坐标

  • scrollTo () 方法可把内容滚动到指定的坐标
  • 语法: 元素.scrollTo(x, y)
  • 例如: window.scrollTo(0, 1000)

# 3.6 案例 返回顶部

需求:点击返回按钮,页面会返回顶部

分析:

  • 绑定点击事件
  • 利用 scrollTop 让页面返回顶部

# 3.7 页面尺寸事件

  • 会在窗口尺寸改变的时候触发事件:resize

    • window.addEventListener('resize', function () {
          console.log(1)
      })
      <!--code5-->

# 3.8 页面尺寸事件 - 获取元素宽高

  • 获取宽高
    • 获取元素的可见部分宽高(不包含边框,margin,滚动条等)
    • clientWidth 和 clientHeight

# 3.9 案例 Rem 基准值

需求:分析 flexible.js 源码

image-20220716174828665

# 4. 元素尺寸与位置

  • 使用场景:

    • 前面案例滚动多少距离,都是我们自己算的,最好是页面滚动到某个元素,就可以做某些事
    • 简单说,就是通过 js 的方式,得到元素在页面中的位置
    • 这样我们可以做,页面滚动到这个位置,就可以做某些操作,省去计算了

    image-20220716175105498

# 4.1 元素尺寸与位置 - 尺寸

  • 获取宽高:

    • 获取元素的自身宽高、包含元素自身设置的宽高、padding、border
    • offsetWidth 和 offsetHeight
    • 获取出来的是数值,方便计算
    • 注意:获取的是可视宽高,如果盒子是隐藏的,获取的结果是 0
  • 获取位置:

    • 获取元素距离自己定位父级元素的左、上距离
    • offsetLeft 和 offsetTop 注意是只读属性

    image-20220716175319154

总结:

  • offsetWidth 和 offsetHeight 是得到元素什么的宽高?
    • 内容 + padding + border
  • offsetTop 和 offsetLeft 得到位置以谁为准?
    • 带有定位的父级
    • 如果都没有则以 文档左上角 为准

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

需求:当页面滚动到秒杀模块,导航栏自动滑入,否则滑出

分析:

  • 用到页面滚动事件
  • 检测页面滚动大于等于 秒杀模块的位置 则滑入,否则滑出
  • 主要移动的是秒杀模块的顶部位置

# 4.3 案例 实现 bilibili 点击小滑块移动效果

需求:当点击链接,下面红色滑块跟着移动

分析:

  • 用到事件委托
  • 点击链接得到当前元素的 offsetLeft 值
  • 修改 line 颜色块的 left 值 = 点击链接的 offsetLeft
  • 添加过渡效果

# 4.4 getBoundingClientRect

  • element.getBoundingClientRect() 方法返回元素的大小及其相对于视口的位置

image-20220716193501337

总结:

属性作用说明
scrollLeft 和 scrollTop被卷去的头部和左侧配合页面滚动来用,可读写
clientWidth 和 clientHeight获得元素宽度和高度不包含 border,margin, 滚动条 用于获取元素大小,只读
offsetWidth 和 offsetHeight获得元素宽度和高度包含 border、padding,滚动条等,只读
offsetLeft 和 offsetTop获取元素距离自己定位父级元素的左、上距离获取元素位置的时候使用,只读属性

# 5. 案例 电梯导航

需求:点击不同的模块,页面可以自动跳转不同的位置

分析:

  • 页面滚动到对应位置,导航显示,否则隐藏模块
  • 点击导航对应小模块,页面 会跳到对应大模块位置
  • 页面滚动到对应位置,电梯导航对应模块自动发生变化