Skip to content

一. window定时器


1. window定时器方法

  • 有时我们并不想立即执行一个函数,而是等待特定一段时间之后再执行,我们称之为“计划调用scheduling a call)”
  • 目前有两种方式可以实现:
    • setTimeout允许我们将函数推迟到一段时间间隔之后再执行
    • setInterval允许我们重复运行一个函数,从一段时间间隔之后开始运行,之后以该时间间隔连续重复运行该函数
  • 并且通常情况下有提供对应的取消方法:
    • clearTimeout:取消setTimeout的定时器
    • clearInterval:取消setInterval的定时器
  • 大多数运行环境都有内置的调度程序,并且提供了这些方法:
    • 目前来讲,所有浏览器以及Node.js都支持这两个方法
    • 所以我们后续学习Node的时候,也可以在Node中使用它们

2. setTimeout的使用

  • 语法如下:

    js
    var timerId = setTimeout(func | code, [delay], [arg1], [arg2], ...)
    • func|code:想要执行的函数或代码字符串
      • 一般传入的都是函数,由于某些历史原因,支持传入代码字符串,但是不建议这样做
    • delay:执行前的延时,以毫秒为单位(1000 毫秒 = 1 秒),默认值是 0
    • arg1,arg2…:要传入被执行函数(或代码字符串)的参数列表
  • clearTimeout方法:

    • setTimeout 在调用时会返回一个定时器标识符timer identifier)”,我们可以使用它来取消执行

      js
      var timerID = setTimeout(function(name, age) {
      	console.log('定时器', name, age)
      }, 2000, 'later', 23)
      
      clearTimeout(timerId)
  • 回调函数中的this指向

    js
    'use strict' // 正常情况下, 默认调用函数是指向window的, 严格模式下, 指向的是undefined
    
    function foo() { console.log(this) } // window(默认情况) => undefined(严格模式)
    
    setTimeout(function() {
      console.log(this) // 严格模式下, 定时器中的回调函数中的this仍然指向window,由此可推断出定时器中传入的回调函数可能是显示绑定到window上面的,所以不论默认情况还是严格模式,定时器中回调函数的this都是指向window的
    }, 1000); // 如果回调函数传入的是箭头函数,则this是根据外层作用域来决定的,因为箭头中this是不会绑定的,所以就不会绑定到window对象上

    注意:

    • 默认情况还是严格模式下,定时器中传入的回调函数(非箭头函数)中的this都是指向window对象的
    • 箭头函数的是不绑定this的,所以是根据外层作用域来决定的,往上层作用域中一层一层查找直至window

3. setInterval的使用

  • setInterval 方法和setTimeout的语法相同:

    js
    var timerId = setInterval(func | code, [delay], [arg1], [arg2], ...)
    • func|code:想要执行的函数或代码字符串
      • 一般传入的都是函数,由于某些历史原因,支持传入代码字符串,但是不建议这样做
    • delay:执行前的延时,以毫秒为单位(1000 毫秒 = 1 秒),默认值是 0
    • arg1,arg2…:要传入被执行函数(或代码字符串)的参数列表
    • 不过与setTimeout只执行一次不同,setInterval 是每间隔给定的时间周期性执行
  • clearInterval方法:

    • setInterval也会返回一个“定时器标识符(timer identifier)”,我们可以通过clearInterval来取消这个定时器

      js
      var timerID = setInterval(function(name, age) {
      	console.log('定时器', name, age)
      }, 200, 'later', 23)
      
      clearInterval(timerId)
  • 关于定时器还有一些宏任务相关的概念,我们会在js高级中讲解

二. 轮播消息提示


image-20220621111056708
html
<!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>
      .tip-bar {
        display: inline-flex;
        align-items: center;
        height: 60px;
        background-color: rgba(0,0,0,.4);
        border-radius: 30px;
      }
      img {
        width: 60px;
        height: 60px;
        margin-right: 5px;
        border-radius: 50%;
      }
      span {
        font-size: 13px;
        color: white;
        margin-right: 8px;
      }
    </style>
  </head>
  <body> 
    <div class="tip-bar">
      <img src="https://bfs.biyao.com/group1/M01/A2/67/rBACVGA_iOuAYaTxAAAPbted3yE165.png" alt="">
      <span>183***138对这件商品感兴趣</span>
    </div>

    <script>
      // 1.从服务器拿到数据ajax/fetch请求
      let tipList = [
        {
          icon: 'https://bfs.biyao.com/group1/M01/A6/97/rBACYWBCHqyAFH5tAAANZXX5Eww646.png',
          title: 'coderwhy对这件商品感兴趣'
        },
        {
          icon: 'https://bfs.biyao.com/group1/M01/A2/67/rBACVGA_iOuAYaTxAAAPbted3yE165.png',
          title: '123***814对这件商品感兴趣'
        },
        {
          icon: 'https://bfs.biyao.com/group1/M00/7F/4E/rBACYV16HseAP-PnAAAW9bbVoKE463.png',
          title: '刘军对这件商品感兴趣'
        }
      ]

      var tipBarEl = document.querySelector('.tip-bar')
      var imgEl = tipBarEl.querySelector('img')
      var spanEl = tipBarEl.querySelector('span')

      var currentIndex = 0
      setInterval(function() {
        if(currentIndex == tipList.length) currentIndex = 0
        imgEl.src = tipList[currentIndex].icon
        spanEl.textContent = tipList[currentIndex].title
        currentIndex++
      }, 1000)
    </script>
  </body>
</html>

三. 关闭隐藏消息


image-20220621111524050
html
<!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>
      .top-bar {
        display: flex;
        flex-direction: row;
        align-items: center;
        height: 45px;
        width: 375px;
        background-color: black;
        /* 关键 */
        overflow: hidden;
        transition: all .5s ease-out;
      }
      .delete {
        display: flex;
        flex-direction: row;
        justify-content: center;
        align-items: center;
        height: 100%;
        width: 30px;
        cursor: pointer;
      }
      .delete img {
        height: 10px;
        width: 10px;
      }
      .logo {
        height: 30px;
        width: 30px;
        margin-left:3px;
        margin-right: 30px;
        cursor: pointer;
      }
      span {
        color: white;
        font-size: 14px;
        flex: 1;
        white-space: nowrap;
        overflow: hidden;
        text-overflow: ellipsis;
      }
      .btn {
        width: 94px;
        height: 100%;
        line-height: 45px;
        text-align: center;
        font-size: 14px;
        color: #fff;
        background-color: #F63515;
      }
    </style>
  </head>
  <body>

    <div class="top-bar">
      <div class="delete">
        <img src="./img/delete.png" alt="">
      </div>
      <img class="logo" src="./img/logo.png" alt="">
      <span>打开京东App,购物更轻松</span>
      <div class="btn">立即打开</div>
    </div>

    <script> 
      var topBarEl = document.querySelector('.top-bar')
      var deleteEl = topBarEl.querySelector('.delete')
      deleteEl.onclick = function() {
        topBarEl.style.height = '0px'
      }
      topBarEl.addEventListener('transitionend', function() {
        topBarEl.remove()
      })
    </script>
  </body>
</html>

四. 侧边栏展示


image-20220621112407604
html
<!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>
      .tool-bar {
        position: fixed;
        top: 30%;
        right: 0;
        display: flex;
        flex-direction: column;
        align-items: center;
        width: 35px;
      }
      .item {
        position: relative;
        width: 35px;
        height: 35px;
        margin-bottom: 1px;
        background-color: #7a6e6e;
        border-radius: 3px 0 0 3px;
      }
      .icon {
        display: inline-block;
        width: 100%;
        height: 100%;
        cursor: pointer;
        background-image: url(./img/toolbars.png);
      }
      /* .icon01 {
      background-position: -48px 0;
      }
      .icon02 {
      background-position: -48px -50px;
      }
      .icon03 {
      background-position: -48px -100px;
      }
      .icon04 {
      background-position: -48px -150px;
      } */
      .name {
        visibility: hidden;
        position: absolute;
        z-index: -1;
        right: 35px;
        /* left: -62px; */
        top: 0;
        width: 0;
        height: 0;
        line-height: 35px;
        color: #fff;
        text-align: center;
        font-size: 12px;
        background-color: #7a6e6e;
        cursor: pointer;
        border-radius: 3px 0 0 3px;
        transition: width .2s ease;
        /* 元素永远不会成为鼠标事件的target */
        /* pointer-events: none;  */
      }
      .item:hover,
      .item:hover .name {
        background-color: #cd1926;
      }
    </style>
  </head>
  <body>

    <div class="tool-bar">
      <div class="item">
        <i class="icon icon01"></i>
        <div class="name">购物车</div>
      </div>
      <div class="item">
        <i class="icon icon02"></i>
        <div class="name">收藏</div>
      </div>
      <div class="item">
        <i class="icon icon03"></i>
        <div class="name">限时活动</div>
      </div>
      <div class="item">
        <i class="icon icon04"></i>
        <div class="name">大礼包</div>
      </div>
    </div>

    <script>
      // 1.动态给icon设置backgroundPosition
      var iconEls = document.querySelectorAll('.icon')
      for (var i = 0; i < iconEls.length; i++) {
        iconEls[i].style.backgroundPosition = `-48px -${i*50}px`
      }
      // 2.实现鼠标进入动画
      // 方案一: mouseenter(不能使用事件委托)
      // var itemEls = document.querySelectorAll('.item')
      // for (var item of itemEls) {
      //   item.onmouseenter = function() {
      //     // console.log('进入', this.lastElementChild)
      //     var nameEl = this.querySelector('.name')
      //     nameEl.style.cssText = `
      //       visibility: visible;
      //       width: 62px;
      //       height: 35px;
      //     `
      //   }
      //   item.onmouseleave = function() {
      //     // console.log('离开', this.lastElementChild)
      //     var nameEl = this.lastElementChild
      //     nameEl.style.width = ''
      //     nameEl.style.height = ''
      //     nameEl.style.visibility = 'hidden'
      //   }
      // }

      // 方案二: mouseover(使用事件委托)
      var toolBarEl = document.querySelector('.tool-bar')
      toolBarEl.onmouseover = function(event) {
        console.log('进入: ', event.target, event.eventPhase)
        var targetEl = event.target;
        (targetEl.classList.contains('name') ? targetEl : targetEl.nextElementSibling).style.cssText = `
          visibility: visible;
          width: 62px;
          height: 35px;
        `
      }

      toolBarEl.onmouseout = function(event) {
        // console.log('离开:', event.target, event.eventPhase)
        var targetEl = event.target;
        (targetEl.classList.contains('name') ? targetEl : targetEl.nextElementSibling).style.cssText = `
          visibility: hidden;
          width: 0px;
          height: 35px;
        `
      }
    </script>
  </body>
</html>

五. tab切换提示


image-20220621112802278
html
<!DOCTYPE html>
<html lang="zh-CN">
  <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>王者荣耀-main-news</title>
    <link rel="stylesheet" href="./css/reset.css">
    <link rel="stylesheet" href="./css/common.css">
    <style>
      .main .section-content {
        display: flex;
        justify-content: space-between;
      }
      .main .section-content .left-content {
        width: 872px;
        height: 1000px;
      }
      .main .section-content .right-content {
        width: 295px;
        height: 500px;
      }
    </style>
  </head>
  <body>

    <div class="main main_wrapper">
      <div class="section-content">
        <div class="left-content">
          <div class="content-center">
            <div class="section_header">
              <div class="header_left">
                <h3 class="title">内容中心</h3>
              </div>
              <div class="header_right" href="#">
                <a class="more" href="#">更多</a>
              </div>
            </div>
            <div class="tab_control">
              <div class="item active">精品栏目</div>
              <div class="line"></div>
              <div class="item">赛事精品</div>
              <div class="line"></div>
              <div class="item">英雄攻略</div>
            </div>
          </div>
        </div>
      </div>

      <script>
        var tabControlEl = document.querySelector('.tab_control')
        var activeEl = tabControlEl.querySelector('.active')

        tabControlEl.addEventListener('mouseover', mouseoverHandler)

        function mouseoverHandler(event) {
          var targetEl = event.target
          if (!targetEl.classList.contains('item')) return
          activeEl.classList.remove('active')
          targetEl.classList.add('active')
          activeEl = targetEl
        }

        // 1.获取元素
        // var tabControl = document.querySelector(".tab_control")

        // // 2.监听鼠标进入(事件委托)
        // var activeLiEl = tabControl.querySelector(".active")
        // tabControl.onmouseover = function(event) {
        //   // 1.拿到事件发生的对象
        //   var itemEl = event.target
        //   if (itemEl.classList.contains("item")) {
        //     // 其他的取消active
        //     // 1.for循环所有的item
        //     // 2.querySelector(".active")
        //     // 3.记录当前的active对应的item
        //     activeLiEl.classList.remove("active")

        //     // 当前进入的item变成active
        //     itemEl.classList.add("active")

        //     // 将最新的itemEl变成activeLiEl
        //     activeLiEl = itemEl
        //   }
        // }
      </script>
      </body>
    </html>

六. 王者荣耀轮播图


image-20220621113219054
html
<!DOCTYPE html>
<html lang="zh-CN">
  <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>王者荣耀-main-news</title>
    <link rel="stylesheet" href="./css/reset.css">
    <link rel="stylesheet" href="./css/common.css">
    <style>
      .main {
        height: 100px;
      }
      .news-section {
        display: flex;
        height: 342px;
      }
      .news-section .banner {
        width: 605px;
        background-color: #000;
        overflow: hidden;
      }
      .news-section .banner .image-list {
        position: relative;
        display: flex;
        width: 604px;
        height: 298px;
        transform: translateX(-100%);
      }
      .news-section .banner .image-list .item {
        /* position: absolute;
        left: 100%; */
        flex-shrink: 0;
        width: 100%;
      }
      .news-section .banner .image-list .item:first-child {
        /* left: 0;
        transition: left 300ms ease; */
      }
      .news-section .banner .image-list .item a {
        display: block;
      }
      .news-section .banner .image-list .item a img {
        width: 100%;
      }
      .news-section .banner .title-list {
        display: flex;
        height: 44px;
        line-height: 44px;
      }
      .news-section .banner .title-list .item {
        flex: 1;
        text-align: center;
      }
      .news-section .banner .title-list .item a {
        display: block;
        font-size: 14px;
        color: #b1b2be;
      }
      .news-section .banner .title-list .item.active a,
      .news-section .banner .title-list .item a:hover {
        color: #f3c258;
        background-color: rgba(255,255,255,.15);
      }
      .news-section .news {
        flex: 1;
        background-color: purple;
      }
      .news-section .download {
        width: 236px;
        background-color: skyblue;
      }
      .news-section .download a {
        display: block;
        background: url(./img/main_sprite.png) no-repeat;
      }
      .news-section .download a.download-btn {
        height: 128px;
        background-position: 0 -219px;
      }
      .news-section .download a.guard-btn {
        height: 106px;
        background-position: 0 -350px;
      }
      .news-section .download a.experience-btn {
        height: 108px;
        background-position: 0 -461px;
      }
    </style>
  </head>
  <body>

    <div class="main main_wrapper">
      <div class="news-section">
        <div class="banner">
          <ul class="image-list">
            <li class="item">
              <a href="">
                <img src="./img/banner_01.jpeg" alt="">
              </a>
            </li>
            <li class="item">
              <a href="">
                <img src="./img/banner_01.jpeg" alt="">
              </a>
            </li>
            <li class="item">
              <a href="">
                <img src="./img/banner_01.jpeg" alt="">
              </a>
            </li>
            <!-- <li class="item">
              <a href="">
              <img src="./img/banner_04.jpeg" alt="">
              </a>
              </li>
              <li class="item">
              <a href="">
              <img src="./img/banner_05.jpeg" alt="">
              </a>
            </li> -->
          </ul>
          <ul class="title-list">
            <li class="item active">
              <a href="#">桑启的旅途故事</a>
            </li>
            <li class="item">
              <a href="#">启示之音抢先听</a>
            </li>
            <li class="item">
              <a href="#">谁成为版本之子</a>
            </li>
            <li class="item">
              <a href="#">观赛体验升级</a>
            </li>
            <li class="item">
              <a href="#">季后赛开战</a>
            </li>
          </ul>
        </div>
        <div class="news"></div>
        <div class="download">
          <a class="download-btn" href="#"></a>
          <a class="guard-btn" href="#"></a>
          <a class="experience-btn" href="#"></a>
        </div>
      </div>
    </div>

    <script>
      var bannerEl = document.querySelector('.banner')
      var imgListEl = bannerEl.querySelector('.image-list')
      var titleListEl = bannerEl.querySelector('.title-list')
      var activeEl = titleListEl.querySelector('.active')
      var currentIndex = 0
      var newIndex = 0
      var timerId = null

      titleListEl.addEventListener('mouseover', titleListMoveHandler)
      bannerEl.addEventListener('mouseenter', timerHandler)
      bannerEl.addEventListener('mouseleave', timerHandler)
      startTimer()

      function timerHandler(event) {
        event.type == 'mouseenter' ? clearInterval(timerId) : startTimer()
      }

      function titleListMoveHandler(event) {
        var itemEl = event.target.parentElement
        if (itemEl == activeEl) return
        newIndex = Array.from(titleListEl.children).findIndex(function(item) {
          return item == itemEl
        })
        switchBanner()
      }

      function startTimer() {
        timerId = setInterval(function() {
          newIndex == titleListEl.children.length-1 ? newIndex = 0 : ++newIndex
          switchBanner()  
        }, 1500)
      }

      function switchBanner() {
        activeEl.classList.remove('active')
        titleListEl.children[newIndex].classList.add('active')
        activeEl = titleListEl.children[newIndex]
        var imgEls = imgListEl.querySelectorAll('img')
        if (newIndex > currentIndex) { // 新索引位置大于当前索引位置
          imgEls[2].src = `./img/banner_0${newIndex+1}.jpeg`
          imgListEl.style.transform = `translateX(-200%)`
        } else 
          if (newIndex < currentIndex) { // 新索引位置小于当前索引位置
            imgEls[0].src = `./img/banner_0${newIndex+1}.jpeg`
            imgListEl.style.transform = `translateX(0%)`
          }
        imgListEl.style.transition = `all .3s ease`
        imgListEl.addEventListener('transitionend', function() {
          imgEls[1].src = `./img/banner_0${newIndex+1}.jpeg`
          imgListEl.style.transform = `translateX(-100%)`
          // imgListEl.style.transition = `all 0s ease`
          imgListEl.style.transition = ''
        })
        currentIndex = newIndex // 保存最新的索引位置
      }

      // // 1.获取元素
      // var titleListEl = document.querySelector(".title-list")
      // var imageListEl = document.querySelector(".image-list")
      // var bannerEl = document.querySelector(".banner")

      // // 定义变量保存一些的状态
      // var activeTitleEl = titleListEl.querySelector(".active")
      // var currentIndex = 0
      // var previousIndex = 0
      // var timerID = null

      // // 2.底部titles的切换, 同时进行轮播
      // titleListEl.onmouseover = function(event) {
      //   // 1.1.确定发生鼠标进入的元素
      //   var itemEl = event.target.parentElement
      //   if (!itemEl.classList.contains("item")) return

      //   // 1.2.获取对应的索引index
      //   var index = Array.from(titleListEl.children).findIndex(function(item) {
      //     return item === itemEl
      //   })
      //   previousIndex = currentIndex
      //   currentIndex = index

      //   // 1.3.调用切换的函数
      //   switchBanner()
      // }

      // // 3.定时器: 定时轮播
      // startTimer()


      // // 监听banner的事件
      // bannerEl.onmouseenter = function() {
      //   clearInterval(timerID)
      // }
      // bannerEl.onmouseleave = function() {
      //   startTimer()
      // }


      // // 封装一个切换轮播的函数
      // function switchBanner() {
      //   // 第一件事情: 让imageListEl滚动
      //   // 1.1.让imageListEl修改transform
      //   // 其他内容需要调整
      //   for (var i = 0; i < imageListEl.children.length; i++) {
      //     var itemEl = imageListEl.children[i]

      //     if (i === currentIndex) { // 当前要展示的imageItem
      //       itemEl.style.transition = "left 300ms ease"
      //       itemEl.style.left = "0"
      //     } else if (i < currentIndex) { // 需要放到左侧的imageItem
      //       if (i !== previousIndex) {
      //         itemEl.style.transition = "none"
      //       }
      //       itemEl.style.left = "-100%"
      //     } else { // 需要放到右侧的imageItem
      //       if (i !== previousIndex) {
      //         itemEl.style.transition = "none"
      //       }
      //       itemEl.style.left = "100%"
      //     }
      //   }


      //   // 第二件事情: 改变title选中
      //   // 1.2.移除之前的active
      //   activeTitleEl.classList.remove("active")

      //   // 1.3.将active添加到鼠标进入的元素
      //   var currentItemEl = titleListEl.children[currentIndex]
      //   currentItemEl.classList.add("active")

      //   // 1.4.让activeItemEl指向最新的元素
      //   activeTitleEl = currentItemEl
      // }

      // // 封装一个添加定时器的函数
      // function startTimer() {
      //   timerID = setInterval(function() {
      //     previousIndex = currentIndex
      //     currentIndex++
      //     if (currentIndex === titleListEl.children.length) {
      //       currentIndex = 0
      //     }

      //     // 调用切换的函数
      //     switchBanner()
      //   }, 3000);
      // }
    </script>
  </body>
</html>

Released under the MIT License.