Web APIs - 第5天

学习元素滚动、资源加载、大小、位置、动画等 DOM 知识,能够编写可交互的网页特效。

  • 能够使用延时函数创建定时任务
  • 能够监听外部资源的加载状态
  • 能够监听页面元素的滚动状态
  • 能够动态获取元素的大小及位置
  • 能够结合 JavaScript 编写可交互的网页特效

DOM

知道如何触发焦点事件和输入事件,能够动态获取元素大小及位置,编写可交互的网页特效。

滚动事件

在页面发生滚动时触发scroll事件

  1. 页面滚动,前提时时必须有滚动条

    • 页面滚动
    • 盒子内滚动,文档滚动
  2. 添加事件监听,谁有滚动条
  1. <body>
  2. <div class="box">
  3. <p>网页事件---滚动事件</p>
  4. <p>网页事件---滚动事件</p>
  5. <p>网页事件---滚动事件</p>
  6. </div>
  7. </body>
  8. <script>
  9. //页面滚动,前提时时必须有滚动条;
  10. //盒子内滚动,文档滚动
  11. //盒子滚动,添加事件监听;谁有滚动条
  12. let box = document.querySelector('.box')
  13. box.addEventListener("scroll", function () {
  14. // this:box dom 节点属性
  15. console.log(this.scrollTop)
  16. })
  17. //页面滚动;页面有滚动条,因为body高度太高了
  18. document.addEventListener("scroll", function () {
  19. //页面卷入高度值1
  20. console.log(document.documentElement.scrollTop);
  21. })
  22. </script>

总结:

  1. 监听页面的滚动, window 和 document均可监听该事件
  2. 在滚动监听的过程中能实时获取页面滚动的距离

案例-滚动

  1. body { height: 2000px;}
  2. <body>
  3. <div class="box">澳门</div>
  4. <script>
  5. let box = document.querySelector('.box')
  6. document.addEventListener("scroll", function () {
  7. let he = document.documentElement.scrollTop
  8. if (he > 200) {
  9. box.style.display = 'block'
  10. } else {
  11. box.style.display = 'none'
  12. }
  13. })
  14. //需求:当用户点击返回顶部,真的页面回到顶部
  15. box.addEventListener("click", function () {
  16. document.documentElement.scrollTop = 0
  17. })
  18. </script>

加载事件

加载事件是浏览器加载,如图片、外联JavaScript、外联 CSS等外部资源完成时触发的事件。

  • load:加载

    • 有关这个页面所有的资源加载完成后, 才执行后面函数
    • 所有资源:

      1. 外联资源css Image
      2. DOM节点加载渲染完成后
    • 推荐:写js代码,放在</boyd>里边

      1. <p>这是个段落标签,测试img事件</p>
      2. <img src="./images/卡车.jpg" alt="">
      3. <script>
      4. //给window,可以添加给img
      5. let img = document.querySelector("img")
      6. //<img src="./images/卡车.jpg" alt=""> img标签 给这个位置创建img 节点
      7. // img里面是什么内容 img.src自己结局
      8. // 继续往下执行
      9. // console.log(img.width) //输出宽度的时候,图片没有加载完成0
      10. //load:加载
      11. img.addEventListener("load", function () {
      12. // 当img资源加载完成以后
      13. console.log(img.width)
      14. })
      15. //场景:涉及图片加载,为了严谨性、最好需要等待资源加载完成后,在做js操作
      16. </script>

总结:

  1. 外部资源加载完成时会触发 load 事件
  2. window对象监听 load 事件时等待所有外部资源加载完毕后才会被触发
  3. .将 script 标签置于 head 标签中,不能保证成功获取 DOM 节点,
  4. 推荐将 script 标签置于 </body>之前

元素大小

动态获取 DOM 元素的大小与位置是开发网页特效所必不可少的

  • 在学习 CSS 盒模型时,我们知道影响盒子大小的因素包括:contentpaddingborder ,在使用JavaScript 动态获取 DOM 元素大小时同样需要从这 3 个方面考虑
  1. <div class="box"></div>
  2. <script>
  3. let box = document.querySelector(".box")
  4. // console.log(box.style.width); //获取行内样式宽度
  5. // DOM节点: .offsetWidth / innerHeight
  6. // width/height+padding+border
  7. console.log(box.offsetWidth)
  8. //DOM节点:clientWidth/height
  9. // width/height+padding
  10. console.log(box.clientWidth)
  11. //DOM节点:clientLeft/Top
  12. // border边框
  13. console.log(box.clientTop)
  14. </script>

结论:

  1. clientWidth / Height动态获取元素的大小,包含内边距。注:如有滚动条需要减掉滚动条的宽
    度,chrome 滚动条宽 15px

  2. offsetWidth / Height 动态获取元素的大小,包含内边距和边框

  • 关于元素盒子的大小还有一种形式,那就是内容溢出产生滚动时的情形

  1. scrollWidth / Height 动态获取元素滚动区域的大小

  2. clientLeft/Top动态获取border的边框大小

案例-滚动到底部

  1. <style>
  2. .box {
  3. width: 300px;
  4. height: 200px;
  5. border: 20px solid #999999;
  6. background-color: pink;
  7. padding: 10px 30px;
  8. overflow: auto;
  9. }
  10. p {
  11. height: 600px;
  12. padding: 10px;
  13. margin: 0;
  14. border: 1px solid #000;
  15. background-color: green;
  16. }
  17. </style>
  18. <div class="box">
  19. <p>文字内容~~~</p>
  20. </div>
  21. <script>
  22. let box = document.querySelector('.box')
  23. let cha = box.scrollHeight - box.clientHeight
  24. box.addEventListener("scroll", function () {
  25. if (box.scrollHeight - cha) {
  26. alert('请续费过后才能继续阅读!!!')
  27. }
  28. })
  29. </script>

元素位置

JavaScritp 有能力动态获取元素相对于父(祖先)元素的位置

  • 结论:
    1. <div class="father">
    2. <div class="son"></div>
    3. </div>
    4. <button class="btn">获取father的位置</button>
    5. <script>
    6. //获取元素
    7. let father = document.querySelector('.father')
    8. let btn = document.querySelector('.btn')
    9. //点击按钮动态获取元素位置
    10. btn.addEventListener('click', function () {
    11. father.innerHTML = `顶部:${father.offsetTop}
    12. 左侧:${father.offsetLeft}`
    13. })
    14. </script>
  1. offsetLeft/Top 相对最近的已定位的祖先元素的位置,当所有祖先都未定位则参照 body
    素, fixed 定位的元素始终参照浏览器窗口。

  2. offsetParent获取元素位置时所参照的祖先元素

    • JavaScript 也能动态获取元素滚动的位置。
  3. scrollLeft/Top动态获取元素滚动的位置

  4. scrollLeft/Top允许被赋值动态改变元素的滚动位置

  5. 获取整个页面的滚动位置通过 documentElement元素

  • 新的方法 getBoundingClientRect获取相对于浏览器窗口的位置及元素的大小
    1. <body>
    2. <h3>同时获取元素大小及位置</h3>
    3. <button>获取大小及位置</button>
    4. <div class="box"></div>
    5. <script>
    6. // 获取 .box 元素
    7. let box = document.querySelector('.box');
    8. let btn = document.querySelector('button');
    9. // 点击获取元素的位置及大小
    10. btn.addEventListener('click', function () {
    11. let rects = box.getBoundingClientRect();
    12. console.log(rects);
    13. })
    14. </script>
    15. </body
  • 扩展:还有一对属性 clientLeftclientHeight 实际指的是边框的宽度。

延时函数

  1. 知道 JavaScript 中定时器函数的构成,能够基于定时器创建定的定时任务。

setTimeout

延时函数是实现程序暂缓执行的技术,如下代码所示:

  1. <script>
  2. // 普通函数
  3. function foo() {
  4. console.log('我是反应迟钝的树獭...');
  5. }
  6. // 等待 3 秒后才会执行函数 foo
  7. setTimeout(foo, 3000);
  8. // 也可以是匿名函数
  9. setTimeout(function () {
  10. console.log('休息1秒后再执行...');
  11. }, 1000);
  12. </script>
  • 延时函数和间歇函数统称为定时器或定时任务,间歇函数的特征是不间断的重复执行,而延时函数只会
    执行 1 次。

  • 结合递归函数可以使用setTimeout 实现 setInterval 一样的功能!!!

    1. <script>
    2. function myInterval() {
    3. let d = new Date();
    4. document.querySelector('.clock').innerText = d.toLocaleString();
    5. // 延时任务,自调用
    6. setTimeout(myInterval, 1000);
    7. }
    8. // 启动定时任务
    9. myInterval();
    10. </script>
  • 一个值得关注的细节是 setInterval 间歇函数也会延时执行,如下所示:
    1. <script>
    2. // 会延时 1秒 才会正式启动定时任务
    3. setInterval(function () {
    4. let d = new Date();
    5. document.querySelector('.clock').innerText = d.toLocaleString();
    6. }, 1000);
    7. </scrip>
  • 上述执行代码的结果是等待 1 秒后,才会在网页中显示时间。

  • setTimeout 结合递归函数创建定时任务时,是立即执行定的,因此实践中经常使用 setTimeout 配
    合递归来替代 setInterval !

  • 总结:

    1. setInterval 的特征是重复执行,首次执行会延时
    2. setTimeout 的特征是延时执行,只执行 1 次
    3. setTimeout结合递归函数,能模拟 setInterval 重复执行,且不会有延时
    4. clearTimeout 清除由 setTimeout 创建的定时任务

动画

JavaScript动画

setIntervalsetTimeout 常被用于创建 JavaScript 动画。

  1. <button>开始动画</button>
  2. <div></div>
  3. <script>
  4. let btn = document.querySelector("button");
  5. let div = document.querySelector("div");
  6. // 初始位置坐标
  7. btn.addEventListener("click", function () {
  8. setInterval(function () {
  9. div.style.left = div.offsetLeft + 10 + "px"
  10. }, 2);
  11. /* function fn() {
  12. div.style.left = div.offsetLeft + 1 + "px"
  13. setTimeout(fn, 1000 / 60)
  14. requestAnimationFrame(fn);
  15. }
  16. fn() */
  17. })
  18. </script>
  • 总结;

    1. 创建定时任务不间断的更改元素的样式,实现动画效果
    2. 借助定时器创建动画效时常使用 setTimeout
  • requestAnimationFrame 是一个类似 setTimeout 的函数,在频繁操作 DOM 时的性能更好,但是
    浏览器的兼容性较 setTimeout 稍差。

    1. <body>
    2. <p class="time"></p>
    3. <script>
    4. // 网页时钟
    5. function clock() {
    6. begin = Date.now();
    7. let time = document.querySelector('.time');
    8. let d = new Date;
    9. // 更新 DOM
    10. time.innerHTML = d.toLocaleString();
    11. // 重复调用
    12. requestAnimationFrame(clock);
    13. }
    14. // 开始执行
    15. clock();
    16. </script>
    17. </body>
  • 总结:

    1. 频繁操作 DOM 时, requestAnimationFramesetTimeoutsetInterval 性能更好
    2. 基于 JavaScript DOM 操作实现的动画多采用 requestAnimationFrame
    3. cancelAnimationFrame 停止执行 requestAnimationFrame

综合案例-轮播图

  1. <body>
  2. <div class="box">
  3. <div class="imgs">
  4. <img src="../代码/images/01.jpg" alt="">
  5. <img src="../代码/images/02.jpg" alt="">
  6. <img src="../代码/images/03.jpg" alt="">
  7. <img src="../代码/images/04.jpg" alt="">
  8. <img src="../代码/images/05.jpg" alt="">
  9. </div>
  10. <button class="pre">前一张</button>
  11. <button class="next">下一张</button>
  12. </div>
  13. <script>
  14. // 需求:点击下一张,向左移动一格图片(w)距离 位移负数
  15. //
  16. // 推导
  17. // 点击第1次:移动1格;
  18. // 点击第2次:移动2格;
  19. // 点击第3次:移动3格;
  20. // 点击第4次:移动4格;
  21. // 点击第5次:移动5格;
  22. // 再次点击, 不让移动
  23. // 问题: 如何知道用户现在点击是第几次呢?
  24. let img = document.querySelector("img");
  25. let next = document.querySelector(".next");
  26. let imgFather = document.querySelector(".imgs");
  27. let pre = document.querySelector('.pre')
  28. let num = 0;
  29. // 左侧移动最大格子数
  30. let max = imgFather.children.length - 1;
  31. //右侧移动最小的格子数
  32. let min = imgFather.children.length + 1
  33. img.addEventListener("load", function () {
  34. // 0.获取图片宽度 如果不是img,是div背景图片,div宽度:div.clinetWidth
  35. let w = img.width;
  36. //下一张
  37. next.addEventListener("click", function () {
  38. // 移动格子数 和 最大左侧移动数 一样,不能移!
  39. num++
  40. if (num == imgFather.children.length) {
  41. num = 0
  42. }
  43. // if (num == max) {
  44. // return; // 终止函数!
  45. // } //老样式
  46. // num++;
  47. // 2.计算应该移动多少位移 num * 图片宽度(获取需要谨慎,等待加载完成后才能获取)
  48. let res = num * w;
  49. // 3.设置移动
  50. imgFather.style.transform = `translateX(-${res}px)`;
  51. });
  52. //上一张
  53. pre.addEventListener('click', function () {
  54. // if (num == 0) {
  55. // return;
  56. // } //老样式
  57. num--
  58. if (num == -1) {
  59. num = 4
  60. }
  61. // 1.点击次数 --->挪动格子数
  62. let ret = num * w;
  63. imgFather.style.transform = `translateX(-${ret}px)`
  64. })
  65. });
  66. </script>
  67. </body>