Skip to content

2-3定时器和及轮播图定时切换案例 ​

本页对应 第二章WebAPIs,共 4 段视频,约 43 分钟。

学习目标 ​

  • 能够使用 setInterval 开启间隔定时器,正确传入回调函数与毫秒间隔,并说明回调函数名后不加小括号的原因。
  • 能够保存 setInterval 返回的唯一 ID,并使用 clearInterval 按 ID 关闭指定定时器。
  • 能够使用信号量索引依次获取数组中的轮播对象,并据此更新图片 src、文字内容、背景颜色和小圆点样式。
  • 能够实现小圆点 active 类切换,做到先移除旧项的 active,再为当前项添加 active,并保证默认第一项可正常初始化。
  • 能够定位并修复轮播索引越界导致的 undefined 报错,并使用 if、三元表达式或取余运算实现最后一张无缝回到第一张。

知识点 ​

从“自动执行”到“特定时机执行” ​

为什么需要:以前写的代码大多按顺序自动执行,但网页交互不是这样:用户不点击,就不该弹窗;鼠标不经过,就不该显示下拉菜单。代码要等到某个特定时机才执行,才符合真实交互。

怎么想:先承认“事件”是程序运行时发生的特定动作或特定事情,例如点击按钮、鼠标经过菜单。事件发生后,我们才做对应的事情,例如点击按钮弹出警示框、鼠标经过某个盒子显示下拉菜单。

怎么做:把“要做什么”写成事件处理函数,再把它注册到元素对象上。这个注册过程就是事件监听,也叫事件注册、事件绑定。

易错点:不要把事件监听写成页面一打开就执行;它只是先注册,等事件触发时才执行。

事件监听三要素与基本语法 ​

为什么需要:只写一个函数还不够,必须明确“谁被触发、什么情况下触发、触发后做什么”。

怎么想:事件监听三要素是事件源(谁被触发了)、事件类型(点击还是鼠标经过等)、事件处理函数(要做什么事情)。语法是:

JavaScript
元素对象.addEventListener('事件类型', 事件处理函数)

怎么做:以“点击按钮弹出秋香”为例,先获取按钮,再注册 click 事件:

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>事件监听</title>
  </head>
  <body>
    <button class="btn">唐伯虎</button>
    <script>
      // 需求: 点击button按钮,页面会弹出一个警示框,内容显示 '秋香'
      // 事件监听语法:
      // 元素对象.addEventListener('事件类型', 事件处理函数)
      // 1. 获取元素对象 button按钮
      const btn = document.querySelector('.btn')
      // 2. 事件监听 
      btn.addEventListener('click', function () {
        alert('秋香')
      })
    </script>
  </body>
</html>

易错点:事件类型要加引号,并且小写;函数是点击之后再去执行,每次点击都会执行一次,不是注册时执行一次。

回调函数:把函数交给别人“回头调用” ​

为什么需要:事件处理函数不是立刻执行,而是先交给浏览器“记住”,等点击发生再调用。定时器也一样:转写稿里反复强调“每隔一段时间自动回头调用函数”,这个“回头调用”的函数就是回调函数。

怎么想:当一个函数被当作参数传递给另一个函数时,这个函数就是回调函数。它的作用是完成某些特定任务。事件监听里第二个参数是函数,定时器第一个参数也是函数,它们都是回调函数。

怎么做:

html
<script>
  // 1. 定时器间隔函数,里面第一个参数又是函数,这个匿名函数就是回调函数
  setInterval(function () {
    console.log('我是回调函数')
  }, 1000)

  // 2. addEventListener 函数的第二个参数也是函数,这个匿名函数也是回调函数
  btn.addEventListener('click', function () {
    console.log('我是回调函数')
  })
</script>

转写稿在讲 setInterval 时有一个关键调试结论:函数名直接写在参数位置时,不要加小括号,因为定时器会按间隔“自动回头调用”;如果加了小括号,就变成先调用函数、把返回值传进去。事件监听同理,addEventListener('click', function () { ... }) 里的匿名函数不会立即执行,而是点击后由浏览器调用。

易错点:不要把“函数声明/函数名”和“函数调用”混为一谈;回调函数里的代码执行次数由事件触发次数或定时器间隔决定。

DOM0 与 DOM2 事件监听版本 ​

为什么需要:事件监听有历史写法,不同写法在“同名事件是否覆盖”上表现不同,需要知道推荐哪一种。

怎么想:DOM0 写法是“事件源.on事件类型 = 函数”;DOM2 写法是 addEventListener。按笔记结论:on 方式同名事件会被覆盖,addEventListener 则不会,同时拥有事件更多特性,所以推荐使用 DOM2。

怎么做:

javascript
btn.onclick = function () {
  alert('我是弹窗1')
}
javascript
btn.addEventListener('click', function () {
  console.log('我是回调函数')
})

易错点:onclick 这种写法再赋一个同名事件,会把前面的覆盖掉;addEventListener 的事件类型写 'click',不要写成 'onclick'。调试时可以先用 console.log 确认回调是否被触发,再写弹窗或页面修改逻辑。

事件类型总览 ​

事件类型是“用户做了什么、页面发生了什么”的名字。写交互时,先判断动作属于哪一类,再选对应事件名。事件类型字符串大小写敏感,统一用小写,例如 click 不能写成 Click。本节把常见类型分为鼠标事件、键盘事件、表单事件、焦点事件等,下面逐一展开。

事件类型分类示意图

说明:本节转写稿实际围绕定时器、轮播图展开,与“事件类型”不直接对应,不能硬套出事件结论;可迁移的是调试习惯:先 console.log 看是否触发,再看值的变化与执行顺序。转写稿中的“定视器/净视器”应纠正为“定时器”,“缩引号”应纠正为“索引号”。

鼠标事件 ​

为什么需要:页面最常响应用户的点击、移入、移出,例如按钮点击、卡片悬停提示。
怎么想:点击用 click;鼠标经过用 mouseenter;鼠标离开用 mouseleave。先获取元素,再绑定监听。
怎么做:

html
<body>
  <div class="box"></div>
  <script>
    // 鼠标事件类型
    const box = document.querySelector('.box')
    // 1. 鼠标点击
    box.addEventListener('click', function () {
      console.log('我点击了盒子')
    })
    // 2. 鼠标经过
    box.addEventListener('mouseenter', function () {
      console.log('我鼠标经过了盒子')
    })
    // 3. 鼠标离开
    box.addEventListener('mouseleave', function () {
      console.log('我鼠标离开了盒子')
    })
  </script>
</body>

易错点:事件名必须小写;mouseenter 与 mouseleave 要成对理解“进入/离开”,不要写成 mouseEnter。调试时打开控制台,分别点击、移入、移出盒子,看三条日志是否按动作出现。

焦点事件 ​

为什么需要:表单需要知道用户是否正在输入,例如搜索框获得焦点时提示、失去焦点时校验。
怎么想:获得光标是 focus,失去光标是 blur;另外可用 元素.focus() 让元素自动获得焦点。
怎么做:

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>焦点事件</title>
    <style>
      [type=text] {
        width: 245px;
        height: 50px;
        padding-left: 20px;
        border: 1px solid #ccc;
        font-size: 17px;
        outline: none;
      }
    </style>
  </head>

  <body>
    <input type="text" class="search-text">
    <input type="text" class="search">
    <script>
      // 1. 焦点事件(手动触发)
      const search_text = document.querySelector('.search-text')
      // 1.1 获得焦点  focus
      search_text.addEventListener('focus', function () {
        console.log('获得了焦点')
      })
      // 1.2 失去焦点 blur
      search_text.addEventListener('blur', function () {
        console.log('失去了焦点')
      })
      // 2. 拓展 自动获得焦点 focus()    自动失去焦点 blur()
      //  2.1 语法: 元素.focus()  比如百度首页搜索框自动获得焦点
      const search = document.querySelector('.search')
      search.focus()
    </script>
  </body>

</html>

易错点:focus/blur 是事件名,focus()/blur() 是方法,别混写;自动聚焦要放在元素已获取之后。调试时先点第一个输入框看“获得”,再点空白看“失去”,刷新页面观察第二个输入框是否自动聚焦。

键盘事件和 input 事件 ​

为什么需要:评论框、搜索框常要实时获取用户输入,或判断按键时机。
怎么想:按键动作看 keydown/keyup;表单 value 变化看 input。笔记表格给出关键差异:

事件触发时机得到表单值
keydown按下键盘时触发不带最后一次按键值 ab
keyup弹起键盘时触发输入内容 abc
input表单value发生变化时触发输入内容 abc

怎么做:

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>input事件和键盘事件</title>
  <style>
    textarea {
      width: 300px;
      height: 30px;
      padding: 10px;
      border-color: transparent;
      outline: none;
      resize: none;
      background: #f5f5f5;
      border-radius: 4px;
    }
  </style>
</head>

<body>
  <textarea id="tx" placeholder="发一条友善的评论" rows="2"></textarea>
  <script>
    // 获取元素
    const tx = document.querySelector('#tx')

    // 1. 键盘事件 
    // 1.1 键盘按下事件  keydown  当我们按下键盘的时候就触发
    tx.addEventListener('keydown', function () {
      console.log('我是keydown事件' + tx.value)
    })

    // 1.2 键盘弹起事件  keyup 当我们键盘弹起的时候就触发
    tx.addEventListener('keyup', function () {
      console.log('我是keyup事件' + tx.value)
    })

    // 2. 用户输入事件 input ,是表单value的值发生变化的时候触发
    tx.addEventListener('input', function () {
      console.log('我是input事件' + tx.value)
    })
    // 3. 注意事项
    // 3.1 执行顺序  keydown →  input   →  keyup
    // 3.2 keydown 获取值的时候得不到最后一次按键的值, keyup和input可以得到用户输入内容
  </script>
</body>

</html>

易错点:执行顺序是 keydown → input → keyup;keydown 里读 value 往往少最后一次按键,keyup 和 input 才能拿到完整输入。调试时在 textarea 输入 abc,看控制台日志顺序和每次打印的值,就能验证表格结论。

课件图示 ​

课件第7页:1.1 事件监听  语法:  事件发生后,想要执行的代码写到事件处理函数里面

课件第8页:1.1 事件监听  语法: 举例说明 注意: 1. 事件类型要加引号,小写 2

课件第9页:总结 总结 1. 我们为什么需要事件? 我们希望一段代码在某个特定的时机才去执

课件第15页:1.3 事件监听版本 区别: on 方式同名事件会被覆盖,addEventLi

课件第16页:1.3 事件监听版本 发展史: DOM L0 :是DOM 的发展的第一个版本

课件第39页:描述 属性/方法 效果 事件监听 元素.addEventListener() 事

课件第10页:案例 点击关闭王者荣耀登录窗口 需求:点击关闭按钮之后,关掉登录提示盒子 分析:

课件第24页: 事件类型的大小写敏感的字符串,统一用小写字母 2. 事件类型 3 4 2 1

代码示例 ​

示例 1:事件监听 + 常见事件类型(鼠标 / 焦点 / 键盘 / input) ​

html
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>事件监听与常见事件类型</title>
  <style>
    .box {
      width: 200px;
      height: 100px;
      line-height: 100px;
      text-align: center;
      background-color: pink;
      cursor: pointer;
    }
    [type=text] {
      width: 245px;
      height: 50px;
      padding-left: 20px;
      border: 1px solid #ccc;
      font-size: 17px;
      outline: none;
    }
    textarea {
      width: 300px;
      height: 30px;
      padding: 10px;
      border-color: transparent;
      outline: none;
      resize: none;
      background: #f5f5f5;
      border-radius: 4px;
    }
  </style>
</head>
<body>
  <button class="btn">唐伯虎</button>
  <div class="box">鼠标经过我</div>
  <input type="text" class="search-text" placeholder="请输入内容">
  <textarea id="tx" placeholder="发一条友善的评论" rows="2"></textarea>

  <script>
    // ========== 1. 事件监听三要素:事件源 + 事件类型 + 事件处理函数 ==========
    // 需求:点击 button 按钮,弹出警示框,内容显示 '秋香'
    const btn = document.querySelector('.btn')
    btn.addEventListener('click', function () {
      alert('秋香')
    })

    // ========== 2. 鼠标事件 ==========
    const box = document.querySelector('.box')
    box.addEventListener('mouseenter', function () {
      console.log('我鼠标经过了盒子')
    })
    box.addEventListener('mouseleave', function () {
      console.log('我鼠标离开了盒子')
    })

    // ========== 3. 焦点事件(针对表单获得 / 失去光标) ==========
    const search_text = document.querySelector('.search-text')
    search_text.addEventListener('focus', function () {
      console.log('获得了焦点')
    })
    search_text.addEventListener('blur', function () {
      console.log('失去了焦点')
    })

    // ========== 4. 键盘事件和 input 事件 ==========
    const tx = document.querySelector('#tx')
    tx.addEventListener('keydown', function () {
      console.log('我是keydown事件' + tx.value)
    })
    tx.addEventListener('input', function () {
      console.log('我是input事件' + tx.value)
    })
    tx.addEventListener('keyup', function () {
      console.log('我是keyup事件' + tx.value)
    })
  </script>
</body>
</html>

关键行与易错点

  • 元素对象.addEventListener('事件类型', 事件处理函数):三个要素缺一不可,事件源用 querySelector 先拿到。
  • 事件类型必须加引号且全小写,写成 'click',不是 'Click'、更不是 click 变量。
  • 函数体里的代码不是立即执行,是点击 / 经过 / 输入之后才“回头调用”,每触发一次执行一次——这就是回调函数。
  • mouseenter / mouseleave 是“经过 / 离开”;focus / blur 是“获得 / 失去焦点”,别和 click 混用。
  • 键盘三件套执行顺序是 keydown → input → keyup;keydown 里读 tx.value 拿不到最后一次按键的值,keyup 和 input 才能拿到完整内容。
  • 对比 DOM0 写法 btn.onclick = function(){}:同名事件会被覆盖,addEventListener 不会覆盖,推荐后者。

示例 2:定时器间隔函数(开启 / 关闭 / 回调函数) ​

html
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>定时器间隔函数</title>
</head>
<body>
  <button class="stop">关闭定时器</button>

  <script>
    // 1. 回调函数:把函数写到外面,再当作参数传给 setInterval
    //    注意:传函数名 repeat 时,后面不加小括号!
    function repeat() {
      console.log('我是一秒钟自动执行一次的函数')
    }

    // 2. 开启定时器:setInterval(函数, 间隔时间)
    //    间隔时间单位是毫秒,1000 毫秒 = 1 秒
    //    开启后返回一个唯一的 ID 数字,页面中第一个定时器返回 1
    let timer = setInterval(repeat, 1000)
    console.log(timer) // 1

    // 3. 关闭定时器:clearInterval(定时器ID)
    //    想关掉定时器,必须先用一个变量把 ID 存起来
    document.querySelector('.stop').addEventListener('click', function () {
      clearInterval(timer)
      console.log('定时器已关闭')
    })
  </script>
</body>
</html>

关键行与易错点

  • setInterval(function(){}, 1000) 里的匿名函数就是回调函数,定时器每隔指定时间自动“回头调用”它一次,永不停歇。
  • 函数名后面不加小括号:setInterval(repeat, 1000) 是对的;写成 setInterval(repeat(), 1000) 会立刻执行一次并把返回值传进去,定时器就废了。
  • 间隔时间单位是毫秒,1 秒要写 1000,写成 1 就是 1 毫秒。
  • 开启定时器会返回一个唯一 ID(1、2、3…),页面里多个定时器靠它区分,避免关错。
  • 关闭定时器必须拿到变量名:clearInterval(timer);如果写成 let timer = setInterval(...) 后马上 clearInterval(timer),代码执行太快,你几乎看不到输出——实际开发中都是满足某个条件(如鼠标经过、倒计时结束)才关。

示例 3:综合案例 —— 轮播图定时版 ​

html
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>轮播图定时版</title>
  <style>
    * { margin: 0; padding: 0; box-sizing: border-box; }
    ul { list-style: none; }
    .slider {
      position: relative;
      width: 600px;
      height: 400px;
      margin: 50px auto;
      overflow: hidden;
    }
    .slide-wrapper img {
      display: block;
      width: 100%;
      height: 400px;
      object-fit: cover;
    }
    .slide-photo {
      position: absolute;
      left: 0;
      bottom: 60px;
      width: 100%;
      text-align: center;
      color: #fff;
      font-size: 22px;
    }
    .footer {
      position: absolute;
      left: 0;
      bottom: 0;
      width: 100%;
      height: 60px;
      display: flex;
      justify-content: center;
      align-items: center;
      gap: 10px;
      background-color: #ffc0cb;
      transition: background-color .3s;
    }
    .footer li {
      width: 12px;
      height: 12px;
      border-radius: 50%;
      background-color: #fff;
    }
    .footer li.active { background-color: #e93b3d; }
  </style>
</head>
<body>
  <div class="slider">
    <div class="slide-wrapper">
      <img src="https://picsum.photos/seed/1/600/400" alt="">
      <p class="slide-photo">第一张图片</p>
    </div>
    <ul class="footer">
      <!-- 第一个小 li 的 active 类不能删,页面刷新时默认选中它 -->
      <li class="active"></li>
      <li></li>
      <li></li>
      <li></li>
      <li></li>
      <li></li>
      <li></li>
      <li></li>
    </ul>
  </div>

  <script>
    // ========== 0. 数据 ==========
    const slideData = [
      { url: 'https://picsum.photos/seed/1/600/400', title: '第一张图片', color: '#ffc0cb' },
      { url: 'https://picsum.photos/seed/2/600/400', title: '第二张图片', color: '#a0d8ef' },
      { url: 'https://picsum.photos/seed/3/600/400', title: '第三张图片', color: '#b5e6b5' },
      { url: 'https://picsum.photos/seed/4/600/400', title: '第四张图片', color: '#ffe4a0' },
      { url: 'https://picsum.photos/seed/5/600/400', title: '第五张图片', color: '#d8c0ff' },
      { url: 'https://picsum.photos/seed/6/600/400', title: '第六张图片', color: '#ffc8a0' },
      { url: 'https://picsum.photos/seed/7/600/400', title: '第七张图片', color: '#a0e0d8' },
      { url: 'https://picsum.photos/seed/8/600/400', title: '第八张图片', color: '#f0b0c0' }
    ]

    // ========== 1. 获取元素(只获取一次,放在定时器外面) ==========
    const img = document.querySelector('.slide-wrapper img')
    const p = document.querySelector('.slide-photo')
    const footer = document.querySelector('.footer')

    // ========== 2. 信号量:控制播放第几张,必须用 let,初始值为 0 ==========
    let i = 0

    // ========== 3. 开启定时器,按序自动播放 ==========
    setInterval(function () {
      // 3.1 信号量加加
      i++

      // 3.2 播放到最后一张后自动回到第一张(无缝衔接)
      //     if 写法:if (i >= slideData.length) i = 0
      //     三元写法:i = i >= slideData.length ? 0 : i
      //     取余写法(最简洁):
      i = i % slideData.length

      // 3.3 依次拿到数组中的下一个对象
      const obj = slideData[i]

      // 3.4 利用对象更换图片、文字、背景颜色
      img.src = obj.url
      p.innerText = obj.title
      footer.style.backgroundColor = obj.color

      // 3.5 更换小圆点:必须在定时器里面获取,因为每次操作的是不同的小 li
      //     先移除以前的 active(注意 querySelector 选类名要加点)
      document.querySelector('.footer .active').classList.remove('active')
      //     再给当前的 li 添加 active(classList 里不加点)
      footer.children[i].classList.add('active')
    }, 1000)
  </script>
</body>
</html>

关键行与易错点

  • 信号量 let i = 0:只能用 let(后面要 i++ 重新赋值),const 会报错;从 0 开始是因为页面刷新已经显示第一张,1 秒后要切到第二张(索引 1)。
  • 获取元素放定时器外面:定时器每秒执行一次,把 img、p、footer 写进去等于每秒重新获取一次,完全没必要。
  • 小圆点必须在定时器里面获取:前面是“改属性”,只需要一个对象;小圆点每次要操作的是不同的小 li,所以 footer.children[i] 要写在定时器内。
  • 顺序不能颠倒:先 document.querySelector('.footer .active').classList.remove('active'),再 footer.children[i].classList.add('active')。反了就会出现两个红点。
  • 加点与不加点的区别:querySelector('.footer .active') 是按类名选择,要加点;classList.remove('active') / add('active') 是操作类名,不加点。
  • 第一个 li 的 active 类不能删:页面刷新时默认选中第一个,若删了,第一次 querySelector('.footer .active') 就选不到元素会报错。
  • 归零位置在 i++ 之后、使用 i 之前:写成 if (i >= 8) i = 0 会漏掉最后一张(等于 7 时第八章还没播就被重置);应该是 i >= slideData.length,或者直接 i = i % slideData.length 取余,且用 slideData.length 而不是写死 8,数组增减不用改代码。

常见错误 ​

1. 定时器第一个参数写成 repeat()

  • 现象:回调只跑了一次就再也不执行,或控制台报 undefined is not a function。
  • 原因:加括号是立刻调用并把返回值(undefined)交给定时器,传进去的不是函数本身。
  • 修法:只写函数名 setInterval(repeat, 1000);必须传参时用 () => repeat(x) 包一层。

2. 事件类型写成 'onclick' / 'Click'

  • 现象:点击毫无反应,控制台也不报错,最难查。
  • 原因:addEventListener 的事件类型是不带 on、全小写的字符串;onclick 是 DOM0 的属性名,不是事件类型。
  • 修法:统一写 'click'、'mouseenter'、'keyup';写完顺手检查引号和大小写。

3. 用 keydown 取输入框的值,总是少最后一个字符

  • 现象:输入 abc,日志依次是 ''、'a'、'ab'。
  • 原因:keydown 触发时本次按键的值还没写进 value,这是时序问题,不是代码写错。
  • 修法:要完整内容改用 input 或 keyup;只有确实需要"按键按下"这一时机才用 keydown。

4. 索引一直自增,播到最后一张就报错

  • 现象:轮播到第 8 张后控制台报 Cannot read properties of undefined (reading 'url'),画面卡死。
  • 原因:i++ 无限增长,超出数组长度后 arr[i] 是 undefined,再取属性必然崩。
  • 修法:在自增语句紧后面做复位,如 i = i % arr.length;复位必须先于"用 i 取数据"执行,否则会漏播最后一张。

5. 小圆点越切越多,或切到后面就全灭

  • 现象:几个圆点同时高亮;或从第二个开始,一个都不亮。
  • 原因:只 add('active') 没 remove 旧的;或把"获取当前小圆点"的代码提到了定时器外面,永远操作同一个元素。
  • 修法:先 querySelector('.active') 移除,再给当前项添加;获取小圆点必须写在定时器内部(依赖当次索引),并且 HTML 里要保留一个初始 active。

6. 想停定时器却停不下来

  • 现象:clearInterval() 传了空或传错,定时器照跑。
  • 原因:开启时没接返回值,拿不到那个唯一 ID。
  • 修法:const timer = setInterval(...),关闭时 clearInterval(timer);多个定时器各用各的变量名,别混用。

7. 索引用 const 声明后自增报错

  • 现象:Assignment to constant variable。
  • 原因:const 声明的变量不可重新赋值,而 i++ 本质是重新赋值。
  • 修法:会变的索引用 let;只有确实不变的元素引用才用 const。

练习 ​

练习 1|不覆盖的多次绑定 给同一个按钮绑定两个 click 回调,分别打印 A 和 B,要求点击一次两条都输出;再用 onclick 写一遍,观察差异并说明原因。

练习 2|实时字数统计 文本框输入时实时显示"已输入 n / 20",超过 20 字把计数变红。要求:分别用 keydown 和 input 各实现一次,对比同一串输入下计数是否一致,并说出你最终选哪个。

练习 3|搜索框交互 页面加载后搜索框自动获得焦点;获得焦点时边框变蓝,失去焦点时若内容为空则边框变灰并提示"请输入关键词"。要求三个行为分别绑定,互不影响。

练习 4|可停止的倒计时 用 setInterval 实现从 10 倒数到 0,到 0 时停止并在页面显示"时间到"。要求:定时器 ID 必须存进变量;另加一个"暂停"按钮也能停。

练习 5|索引复位三种写法 给定 i++ 后紧跟取 arr[i] 的代码,分别用 if、三元、取余三种方式补上边界复位,跑通 8 张图的循环;再用一句话说明为什么复位语句不能写在取数据之后。

原视频对照 ​

#视频时长
1定时器之间隔函数11:17
2综合案例-轮播图定时版-上07:25
3综合案例-轮播图定时版-中11:53
4综合案例-轮播图定时版-下13:01

小结 ​

  1. 事件监听统一用 addEventListener,事件类型是不带 on 的小写字符串,同名事件不会互相覆盖。
  2. 回调函数的核心是"传引用不传结果"——加不加括号,决定了它是被交出去等待调用,还是当场执行。
  3. 定时器第一个参数必须传函数本身,这是本节最高频的坑;传调用结果等于把 undefined 交给定时器。
  4. 开定时器就必须接返回值,没有 ID 就没有关闭的资格;clearInterval 只能靠这个 ID 工作。
  5. 定时器内的代码会反复执行,元素查询能提到外面的就提,只有依赖当次索引的(如当前小圆点)才留在里面。
  6. 循环播放的本质是索引取模,复位语句必须紧跟在自增之后、使用之前,顺序错一步就漏一张或直接崩。
  7. 类名切换遵循"先清后加",HTML 里保留一个初始 active,否则第一次操作就可能对 null 调方法。
  8. 键盘相关事件按需求选:要完整输入内容用 input 或 keyup,keydown 只适合"按键按下"这个时机本身。