Skip to content

2-14swipe插件及综合案例-通讯录(一) ​

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

学习目标 ​

  • 能够独立引入 Swiper 的 CSS/JS 文件,复制并初始化轮播结构,实现可手动滑动、点击按钮切换的轮播效果。
  • 能够根据 Swiper API 修改常用配置项,如 autoplay、loop、pagination、slidesPerView、spaceBetween,并覆盖默认样式调整分页器与箭头外观。
  • 能够引入 AlloyFinger 插件,为指定元素注册 swipe 手势事件,依据 event.direction 判断滑动方向并通过添加/移除类名实现元素滑动与复位。
  • 能够分析通讯录案例的头部、列表、底部三段式布局,说明渲染、滑动、新增、删除四项业务,并使用 forEach 遍历对象数组获取元素与索引。
  • 能够封装 render 函数,使用模板字符串拼接通讯录列表结构,通过 innerHTML 渲染到页面,并替换姓名、手机号等数据后重新调用函数更新视图。
  • 能够说明 window 对象、延迟定时器 setTimeout 和 location 对象的常用用法,完成延迟执行、地址读取或页面跳转的简单示例。

知识点 ​

材料说明 ​

本节笔记主题是 window 对象,但提供的转写稿主要讲 swiper 插件、AlloyFinger 手势插件、通讯录案例和 forEach 遍历,与 window 对象没有直接对应关系。为避免编造,下面不把插件内容硬套进本节,而是严格依据笔记扩写 window 对象;转写稿中与本节无关的同音字和调试过程不纳入。

一、BOM 与 window:先建立“浏览器总入口”的认识 ​

BOM 与 window 对象关系示意图

为什么需要:学习 JavaScript 不能只会写变量和函数,还要知道代码运行在哪个环境里。浏览器提供了 BOM,即 Browser Object Model,浏览器对象模型,而 window 是这套模型的核心入口。

怎么想:不要把 window 想成普通对象,而要把它看成浏览器环境中的全局对象、顶级对象。很多浏览器能力都挂在它下面,页面文档、弹窗、控制台输出都与它有关。

怎么做:在浏览器控制台输入 window,观察它包含的属性和方法;再输入 window.document、window.alert、window.console,理解它们都是 window 下面的成员。

易错点:window 是浏览器环境提供的对象,不是 JavaScript 语言里凭空产生的普通变量;离开浏览器环境讨论 window 时要谨慎。

二、document、alert()、console.log() 都是 window 的属性 ​

为什么需要:初学者常把 document、alert()、console.log() 当成彼此独立的东西,实际上它们都能通过 window 找到,理解这一点才能明白“省略 window”的来源。

怎么想:既然 document 是 window 的属性,那么 window.document 和 document 指向同一个对象;alert() 是 window 的方法,window.alert() 与 alert() 等价;console.log() 中 console 也是 window 下的成员。

怎么做:可以用下面的代码验证:

js
console.log(window.document === document); // true
console.log(window.alert === alert);       // true
console.log(window.console === console);   // true

易错点:console.log() 不是 window 直接的方法,而是 window.console.log();不要写成 window.log()。另外,能省略 window. 不代表 window 不存在,只是访问全局对象属性时的一种简写。

三、var 定义在全局作用域中的变量、函数会成为 window 的属性和方法 ​

为什么需要:这解释了为什么有些全局变量可以直接通过 window 访问,也解释了全局作用域与 window 的联系。

怎么想:在传统全局作用域中,用 var 声明的变量、用函数声明写出的函数,会成为 window 对象的属性和方法。也就是说,它们既是全局变量/函数,也能通过 window 访问。

怎么做:

js
var siteName = 'window 教程';
function sayHi() {
  console.log('hi');
}

console.log(window.siteName);     // 'window 教程'
console.log(typeof window.sayHi); // 'function'
window.sayHi();                   // 等价于 sayHi()

易错点:本节讲的是 var 和全局函数声明这种传统写法,不要反推成“所有变量都一定挂到 window 上”。学习中先牢牢记住笔记结论:通过 var 定义在全局作用域中的变量、函数会变成 window 的属性和方法。

四、调用 window 下的属性和方法时可以省略 window ​

为什么需要:省略写法让代码更简洁,但如果不理解省略规则,就会误以为 document、alert 是独立关键字。

怎么想:因为它们在全局访问时默认能从 window 上找到,所以 window.document 可以写成 document,window.alert() 可以写成 alert()。省略的是访问前缀,不是对象本身。

怎么做:对比两种写法:

js
window.alert('hello');
alert('hello');

window.document.title = '测试';
document.title = '测试';

易错点:省略 window 只适用于全局访问场景;如果局部变量同名,访问结果可能变化。调试时可以在控制台分别输入 window.document 和 document,观察它们是否相等,从而确认省略关系。

从“等一会儿再执行”说起 ​

为什么需要延迟执行?因为页面里经常有“过一会儿再做”的需求:提示框几秒后消失、轮播图隔几秒切换、用户操作后延迟跳转等。若没有内置定时器,就只能写很别扭的等待逻辑。JavaScript 提供的 setTimeout 就是用来让一段代码延迟执行的函数,常写作 window.setTimeout,平时可省略 window。

怎么想?先把它理解成“预约一次任务”:给出一个回调函数,再给出延迟时间,时间到了才执行回调。怎么做?语法是:

JavaScript
setTimeout(回调函数, 延迟时间)

易错点:延迟时间通常按毫秒理解;第一个参数应传函数,不是直接写执行结果。转写稿中讲轮播配置时提到“加一个 autoplay 3000 就行了……3000 是 3 秒的意思”,这里能对应上的就是:定时类参数常用毫秒表示,3000 毫秒即 3 秒。转写稿里的“GS、CS、AT码”是 JS、CSS、HTML 的口误,本节不展开插件内容,只借用这个毫秒概念帮助理解延迟时间。

setTimeout 只执行一次 ​

为什么强调“只执行一次”?因为 setTimeout 不是循环器,它只预约一次。怎么想?可以把它理解为“把一段代码推迟执行”,执行完这次任务就结束。怎么做?例如:

JavaScript
setTimeout(function () {
  console.log('我只执行一次')
}, 3000)

易错点:不要把它和“每隔一段时间执行一次”混为一谈。笔记中明确对比了间歇函数 setInterval:每隔一段时间就执行一次,平时也省略 window。本节重点是延迟函数,间歇函数只作概念区分。

延迟函数与后面的代码谁先执行 ​

为什么后面的代码会先执行?因为 setTimeout 只是登记一个延迟任务,当前同步代码不会停在原地等 3 秒。怎么想?程序先继续往下走,等延迟时间到了,事件循环再把回调拿出来执行。怎么做?观察下面结构:

JavaScript
let timerId = setTimeout(function () {
  console.log('我只执行一次')
}, 3000)

console.log(timerId)

易错点:不要以为写了 setTimeout 就会立刻输出回调内容;回调要等待。笔记的注意点也写明:“延时函数需要等待,所以后面的代码先执行。”

返回值是定时器编号 ​

为什么需要返回值?因为后面可能要取消这个延迟任务。怎么想?setTimeout 返回一个正整数,表示定时器的编号;把它存到变量里,就能交给清除函数。怎么做?

JavaScript
let timerId = setTimeout(function () {
  console.log('我只执行一次')
}, 3000)
console.log(timerId)

易错点:返回值不是回调结果,而是编号;编号通常是正整数,但不要依赖具体数值做业务判断。

清除延时函数 clearTimeout ​

为什么需要清除?因为用户可能在 3 秒内已经完成操作,或页面状态已变化,原来的延迟任务就不该再执行。怎么想?开启时保存 timerId,需要取消时调用 clearTimeout(timerId)。怎么做?

JavaScript
clearTimeout(timerId)

易错点:清除要在延迟回调执行前进行;若回调已经执行,再清除没有意义。还要注意清除的是“延时函数”的编号,不要和间歇函数的概念混淆。

完整示例与调试观察 ​

笔记给出的完整示例是:

html
<body>
  <script>
    // 定时器之延迟函数

    // 1. 开启延迟函数
    let timerId = setTimeout(function () {
      console.log('我只执行一次')
    }, 3000)

    // 1.1 延迟函数返回的还是一个正整数数字,表示延迟函数的编号
    console.log(timerId)

    // 1.2 延迟函数需要等待时间,所以下面的代码优先执行

    // 2. 关闭延迟函数
    clearTimeout(timerId)

  </script>
</body>

这段代码的调试现象要会推导:先执行 setTimeout,登记一个 3 秒后的任务,并把编号赋给 timerId;随后立即执行 console.log(timerId),所以控制台先看到编号;紧接着执行 clearTimeout(timerId),把刚才登记的延迟任务取消。因此 3 秒后不会输出“我只执行一次”。这不是 setTimeout 失效,而是它被清除得太早了。

若想看到“我只执行一次”,应把 clearTimeout(timerId) 暂时注释掉,再观察 3 秒后输出。易错点:调试时不要只看代码“写了定时器”就以为一定会执行,要检查是否在等待期间被清除。

易错点汇总 ​

第一,setTimeout 只执行一次;需要反复执行应使用 setInterval,但本节只要求理解二者区别。第二,延迟时间通常按毫秒写,3000 表示约 3 秒。第三,后面的同步代码先执行,回调后执行。第四,返回值是定时器编号,要保存后再传给 clearTimeout。第五,清除要发生在延迟回调之前。第六,回调要写成函数形式,避免把“函数调用结果”误当成回调传入。掌握这几点,就能正确使用延迟函数并解释常见调试现象。

说明与本节定位 ​

本节笔记讲的是 location 对象。需要先说明:提供的转写稿主要围绕 swiper 插件、AlloyFinger 手势插件和通讯录案例,并没有出现 location 的讲解;为避免编造,下面不把无关内容硬套进来,只依据本节笔记和代码展开,调试过程也只描述这些代码能直接验证的现象。笔记中“啥希值”应纠正为“哈希值”。

location(地址)会拆分并保存 URL 地址的各个组成部分,它是一个对象。核心内容如下:

属性/方法说明
href属性,获取完整的 URL 地址,赋值时用于地址的跳转
search属性,获取地址中携带的参数,符号 ? 后面部分
hash属性,获取地址中的哈希值,符号 # 后面部分
reload()方法,用来刷新当前页面,传入参数 true 时表示强制刷新

先认识 location:URL 被拆成可读可写的部分 ​

为什么需要:页面中经常要读取当前地址、取参数、做锚点跳转或刷新,location 提供了统一入口。
怎么想:把它当成一个对象,里面按 URL 的组成部分保存信息;用属性读,用方法执行动作。
怎么做:在控制台或脚本中输出 location 及其属性观察。
易错点:location 是对象,不是函数;不要写成 location(),也不要用 new location。

href:读取完整地址,赋值实现跳转 ​

为什么需要:既要拿到当前完整 URL,也要在 JS 中控制页面跳转。
怎么想:读 location.href 得到完整地址;给 location.href 赋值,就是让浏览器跳到新地址。
怎么做:笔记代码中先输出:

js
console.log(location.href)
// location.href = 'http://www.itcast.cn'

取消注释并赋值,就会发生跳转。
易错点:赋值不是普通变量赋值,会触发页面跳转;地址要写完整、写正确,否则可能跳转失败或跳到错误地址。

search:获取问号后面的查询串 ​

为什么需要:搜索、筛选、分页等常把参数放在 URL 的 ? 后面。
怎么想:location.search 取的是 ? 后面部分,注意它通常包含 ? 本身。
怎么做:示例表单中有 <input type="text" name="search">,按表单默认提交行为,地址会带上类似 ?search=笔记本 的查询串,然后:

js
console.log(location.search)  // ?search=笔记本

易错点:不要以为 search 只返回“笔记本”;它返回的是带 ? 的字符串。没有查询参数时通常为空字符串。

hash:获取井号后面的哈希值 ​

为什么需要:锚点定位、前端路由常使用 # 后面的片段。
怎么想:location.hash 取 # 后面部分,通常包含 #。
怎么做:示例中点击:

html
<a href="#/music">音乐</a>
<a href="#/download">下载</a>

再输出:

js
console.log(location.hash)

点击“音乐”后可观察到 #/music。
易错点:hash 包含 #,不要把它当成已经解析好的路由对象;点击锚点会改变 hash,调试时看地址栏和控制台输出对应关系。

reload():刷新页面与强制刷新 ​

为什么需要:数据需要重新加载时,可能要刷新当前页面。
怎么想:reload() 是方法,要加括号调用;笔记说明传入 true 表示强制刷新。
怎么做:代码中给刷新按钮绑定点击事件:

js
const btn = document.querySelector('.reload')
btn.addEventListener('click', function () {
  // location.reload() // 页面刷新
  location.reload(true) // 强制页面刷新 ctrl+f5
})

易错点:不要把 location.reload() 直接写在页面加载时执行,否则可能反复刷新;它应放在用户触发的事件中。按笔记,true 表示强制刷新。

完整示例与调试观察 ​

保留本节代码:

html
<body>
  <form>
    <input type="text" name="search"> <button>搜索</button>
  </form>
  <a href="#/music">音乐</a>
  <a href="#/download">下载</a>

  <button class="reload">刷新页面</button>
  <script>
    // location 对象  
    // 1. href属性 (重点) 得到完整地址,赋值则是跳转到新地址
    console.log(location.href)
    // location.href = 'http://www.itcast.cn'

    // 2. search属性  得到 ? 后面的地址 
    console.log(location.search)  // ?search=笔记本

    // 3. hash属性  得到 # 后面的地址
    console.log(location.hash)

    // 4. reload 方法  刷新页面
    const btn = document.querySelector('.reload')
    btn.addEventListener('click', function () {
      // location.reload() // 页面刷新
      location.reload(true) // 强制页面刷新 ctrl+f5
    })
  </script>
</body>

调试时可按顺序做:打开页面看 href;在输入框输入“笔记本”并提交,观察地址栏和 search;点击“音乐”“下载”,观察 hash 变化;最后点“刷新页面”验证 reload()。注意表单提交会触发页面跳转/刷新,控制台输出可能被清掉,这是调试 search 时要留意的现象。

课件图示 ​

课件第9页:1.2 定时器-延迟函数 l JavaScript 内置的一个用来让代码延迟执行

课件第6页:1.1 BOM l BOM (Browser Object Model ) 是浏

课件第12页:1.3 location对象 l location (地址) 它拆分并保存了 U

课件第7页:总结 总结 1. JavaScript有几部分组成的? Ø ECMAScript

课件第14页:案例 5秒钟之后跳转页面 需求:用户点击可以跳转,如果不点击,则5秒之后自动跳转

课件第10页:案例 5秒钟之后消失的广告 需求:5秒钟之后,广告自动消失 分析: ①:点击关闭

课件第21页:1.6 本地存储分类- localStorage(重点) l 作用: 数据可以长

课件第13页:总结 总结 属性/方法 说明 href 属性,获取完整的 URL 地址,赋值时用

代码示例 ​

示例一:window 全局对象 · 延迟函数 setTimeout · location 对象 ​

html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
  <title>BOM 定时器与 location</title>
</head>
<body>
  <form>
    <input type="text" name="search"> <button>搜索</button>
  </form>
  <a href="#/music">音乐</a>
  <a href="#/download">下载</a>

  <button class="reload">刷新页面</button>

  <script>
    // ===== 1. 定时器之延迟函数 =====

    // 1.1 开启延迟函数(只执行一次)
    let timerId = setTimeout(function () {
      console.log('我只执行一次')
    }, 3000)

    // 1.2 返回值是一个正整数,表示延迟函数的编号
    console.log(timerId)

    // 1.3 延迟函数需要等待时间,所以下面的代码优先执行
    console.log('我先执行,因为定时器还在等 3 秒')

    // 1.4 关闭延迟函数(上面那句 log 就永远不会输出了)
    clearTimeout(timerId)

    // ===== 2. location 对象 =====
    // 2.1 href 得到完整地址,赋值则是跳转到新地址
    console.log(location.href)
    // location.href = 'http://www.itcast.cn'

    // 2.2 search 得到 ? 后面的地址(含 ?)
    console.log(location.search)   // 例如 ?search=笔记本

    // 2.3 hash 得到 # 后面的地址(含 #)
    console.log(location.hash)     // 例如 #/music

    // 2.4 reload 方法 刷新页面
    const btn = document.querySelector('.reload')
    btn.addEventListener('click', function () {
      // location.reload()        // 普通刷新
      location.reload(true)       // 强制刷新(相当于 ctrl + f5)
    })
  </script>
</body>
</html>

要点解释

  • setTimeout(回调函数, 延迟时间) 只执行一次,单位是毫秒;setInterval 才是每隔一段时间执行一次。
  • 定时器的返回值是一个正整数编号,不是对象,把它存到变量里才能交给 clearTimeout(timerId) 清除。
  • 延迟函数会“等”,所以 clearTimeout 之前的同步代码(console.log('我先执行…'))先跑完,回调根本没机会执行。
  • location.href 既是读也是写:读是完整 URL,赋值就是跳转;location.search 带 ?,location.hash 带 #,取值时别把符号弄丢。
  • 易错点:<form> 里的 <button> 默认 type="submit",点击会提交并刷新页面,把 location 打印的结果冲掉——演示 location 时建议改成 <button type="button"> 或阻止默认行为。

示例二:Swiper 轮播图插件(引入 → 结构 → 样式覆盖 → 初始化) ​

html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Swiper 轮播图</title>

  <!-- 1. 先引入插件自带的样式 -->
  <link rel="stylesheet" href="./css/swiper.min.css">

  <style>
    .banner {
      width: 400px;
      margin: 50px auto;
    }
    .swiper-container {
      width: 100%;
      height: 250px;
    }
    .swiper-slide img {
      width: 100%;
      height: 100%;
      object-fit: cover;
    }

    /* 2. 插件默认样式不是我们要的,自己写类名覆盖它 */
    .swiper-pagination-bullet {
      background-color: #fff;
      opacity: 1;
    }
    /* 当前高亮的小圆点权重不够,必须单独写 active 这个类 */
    .swiper-pagination-bullet-active {
      background-color: pink;
    }
  </style>
</head>
<body>
  <div class="banner">
    <!-- 3. 结构照抄插件的,不要随意改类名 -->
    <div class="swiper-container">
      <div class="swiper-wrapper">
        <div class="swiper-slide"><img src="./images/slide01.jpg" alt=""></div>
        <div class="swiper-slide"><img src="./images/slide02.jpg" alt=""></div>
        <div class="swiper-slide"><img src="./images/slide03.jpg" alt=""></div>
        <div class="swiper-slide"><img src="./images/slide04.jpg" alt=""></div>
        <div class="swiper-slide"><img src="./images/slide05.jpg" alt=""></div>
      </div>

      <!-- 分页器(小圆点) -->
      <div class="swiper-pagination"></div>
      <!-- 左右按钮 -->
      <div class="swiper-button-next"></div>
      <div class="swiper-button-prev"></div>
    </div>
  </div>

  <!-- 4. 再引入插件 js -->
  <script src="./js/swiper.min.js"></script>
  <script>
    // 5. 初始化(配置成我们想要的效果)
    new Swiper('.swiper-container', {
      pagination: '.swiper-pagination',      // 显示小圆点
      paginationClickable: true,             // 点击小圆点可以切换
      nextButton: '.swiper-button-next',     // 下一个按钮
      prevButton: '.swiper-button-prev',     // 上一个按钮
      loop: true,                            // 无缝循环滚动
      spaceBetween: 30,                      // 每张图之间 30px 缝隙
      slidesPerView: 1,                      // 一次显示 1 张
      autoplay: 3000                         // 3 秒自动切换一张
    })
  </script>
</body>
</html>

要点解释

  • 使用插件的固定流程:引入 CSS/JS → 复制 HTML 结构 → 覆盖自定义样式 → 初始化配置,四步一步都不能少。
  • 引入顺序:swiper.min.css 放 <head>,swiper.min.js 放 </body> 前,初始化代码必须写在 js 引入之后,否则 Swiper 未定义。
  • 结构不能自己乱写:必须是 .swiper-container > .swiper-wrapper > .swiper-slide,每张图片各自包在一个 swiper-slide 里,复制时整个 div 连 img 一起复制。
  • 想改插件默认样式(如小圆点颜色),不要改插件源码,自己写同名类覆盖;active 高亮项要写 .swiper-pagination-bullet-active,否则被插件样式盖掉。
  • 初始化参数是对象,autoplay: 3000 表示 3 秒(毫秒);slidesPerView: 2 就能一次显示两张;参数之间用逗号隔开,最后一项不要加逗号。
  • 不知道某个参数是什么意思,就去官网 API 文档里搜参数名,别猜。

示例三:AlloyFinger 手势滑动插件 ​

html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>AlloyFinger 手势滑动</title>
  <style>
    /* 外层盒子:只露出 200px,多出来的部分被裁掉 */
    .nav {
      width: 200px;
      height: 100px;
      margin: 100px auto;
      border: 1px solid pink;
      overflow: hidden;
    }
    /* 内层盒子比外层宽 50px,才有滑动的空间 */
    .item {
      width: 250px;
      height: 100px;
      background-color: skyblue;
      transition: all .3s;      /* 过渡,让滑动是“慢慢划过去”的 */
    }
    /* 添加 active 就让盒子整体左移 50px */
    .item.active {
      transform: translateX(-50px);
    }
  </style>
</head>
<body>
  <div class="nav">
    <div class="item">往左滑 / 往右滑试试</div>
  </div>

  <!-- 1. 引入手势插件(下载到本地,或换成官网给的在线地址) -->
  <script src="./js/alloy_finger.js"></script>
  <script>
    // 2. 先拿到要滑动的元素
    const item = document.querySelector('.item')

    // 3. 给这个元素注册手势事件
    new AlloyFinger(item, {
      swipe: function (evt) {
        // evt.direction 就是滑动的方向,首字母大写
        if (evt.direction === 'Left') {
          item.classList.add('active')      // 往左滑:左移
        } else {
          item.classList.remove('active')   // 其它方向:滑回去
        }
      }
    })
  </script>
</body>
</html>

要点解释

  • 核心思路:new AlloyFinger(元素, 配置对象),第一个参数决定“给谁绑手势”,第二个参数对象里 swipe 是滑动时触发的回调。
  • 方向判断靠事件对象的 evt.direction,值是 'Left' / 'Right' / 'Up' / 'Down'——首字母必须大写,写成 'left' 永远不成立。
  • 真正产生动画的是 CSS:.item.active { transform: translateX(-50px) } 配合 transition: all .3s;JS 只负责加/删类名,这样才有滑动的过渡效果。
  • 外层 .nav 一定要 overflow: hidden,否则宽出来的那 50px 会露在页面上,看不出“滑出/滑回”的效果。
  • 除了 swipe,插件还支持 touchStart、touchMove、touchEnd 等事件,写法相同,都是“事件名 + 回调函数”。
  • 易错点:只想左右滑动时,用 else 把其它方向统一处理成“移除 active”,否则手指上下滑一下盒子就卡在左边回不来了。

常见错误 ​

1. clearTimeout 传入的是回调函数而不是定时器编号

  • 现象:调用 clearTimeout(fn) 后,延迟代码仍然执行,控制台也没有报错。
  • 原因:setTimeout 返回的是一个正整数编号,只有把这个编号传给 clearTimeout,才能取消对应定时器;传函数引用时,找不到匹配的定时器,因此清除无效。
  • 修法:先用变量接收返回值(如 const timerId = setTimeout(...)),清除时写 clearTimeout(timerId);同一变量被覆盖前要先清除旧定时器。

2. 依赖 setTimeout 之后的同步代码顺序

  • 现象:明明定时器写在前面,console.log 却先打印后面的内容,误以为定时器没生效。
  • 原因:延迟函数要等时间到才进入回调,主线程会先把后面的同步代码执行完,因此顺序与书写顺序不一致。
  • 修法:需要按顺序执行的逻辑要放进回调里,或使用 Promise/async;调试时用时间戳确认,而不是靠书写位置判断。

3. 复制 swiper 结构时破坏了层级,或把图片直接塞进 slide 外层

  • 现象:轮播图不显示、只显示一张、左右按钮点了没反应,或小圆点数量不对。
  • 原因:swiper 依赖固定的 DOM 层级(容器 → wrapper → slide),并且每个 swiper-slide 里要包一张图片;随手删掉 swiper-wrapper,或在 slide 外面套 <img>,都会让插件初始化失效。
  • 修法:整体复制官方 demo 的结构,只替换 slide 内部的图片;需要几个图片,就复制几份完整的 swiper-slide(含内部 img);改完后对照官方 demo 的层级逐层核对。

4. 覆盖 swiper 默认样式时不加权重,导致高亮态丢失

  • 现象:把分页圆点改成白色后,当前选中那个圆点的颜色也变白/看不见了。
  • 原因:默认高亮样式由 .swiper-pagination-bullet-active 控制,你写的 .swiper-pagination-bullet 权重不够或书写顺序靠后,把 active 态一并覆盖。
  • 修法:基础态与激活态分开写,激活态用完整类名(如 .swiper-pagination-bullet-active)并放在基础样式之后;调试时用开发者工具确认最终生效的规则。

5. 给 swiper 加配置项漏写逗号,或把 autoplay 写成字符串

  • 现象:整段初始化代码报语法错误,页面白屏;或 autoplay: '3000' 不自动播放。
  • 原因:初始化参数是一个对象,多个键值对之间必须用逗号分隔;autoplay 需要数值毫秒,写成字符串时部分版本不识别。
  • 修法:每加一个配置项就补一个逗号,最后一个可省略;数值参数不加引号;配置项名称到官方 API 文档核对,不要凭记忆拼写。

6. AlloyFinger 的方向判断写成小写 'left'

  • 现象:滑动事件确实触发了,但 if 分支永远不成立,盒子不动。
  • 原因:event.direction 返回的是首字母大写的 'Left'/'Right'/'Up'/'Down',与 'left' 严格比较不相等。
  • 修法:比较时写 'Left',或统一用 event.direction.toLowerCase();先在回调里 console.log 打印真实返回值再写判断。

7. 渲染函数里忘记重新调用,或误用 innerHTML +=

  • 现象:新增/删除数据后页面不变;或反复操作后事件失效、滑动插件对新元素不生效。
  • 原因:渲染逻辑封装成 render() 后,数据变了但没有再次调用;用 innerHTML += 追加会重建已有节点,绑定在旧节点上的事件与插件实例全部丢失。
  • 修法:数据变更后立即调用 render();渲染时先拼完整字符串再一次性赋给 innerHTML,并对新生成的节点重新初始化插件。

练习 ​

1. 定时器编号与清除 写一段代码:3 秒后打印“到点了”,同时提供按钮 A 在 3 秒内取消该定时器。要求打印出定时器编号,并用时间戳证明“后面的代码先执行”。完成后回答:为什么 clearTimeout 传函数无效?

2. location 参数读取 页面上放一个搜索表单(name="search")与两个锚点链接(#/music、#/download)。提交后读取 location.search 并显示在页面上;点击锚点时读取 location.hash 显示当前栏目。要求不使用任何框架,并说明 href、search、hash 三者取到的内容差别。

3. swiper 改造 用官方 demo 搭一个轮播图,替换为自己准备的 4 张图片,然后完成三件事:① 把分页圆点改成白色、当前项改成粉色;② 设置 3 秒自动播放、图间 30px 间距、无限循环;③ 关闭分页器观察效果。每改一项就说明改的是结构、样式还是初始化配置。

4. AlloyFinger 抽屉 做一个 200px 宽的容器,内部 250px 宽的蓝色块,用手指左右滑动实现“滑出/收回”。要求:先打印 event.direction 的真实值,再写判断;并解释为什么用 else 统一移除类就能实现回弹。

5. 通讯录渲染函数 给定一个联系人对象数组(含 name、tel),用 forEach + 模板字符串拼出列表并插入页面,整体封装为 render()。要求:① 说明 forEach 回调两个参数各是什么;② 首次渲染后手动修改数组再调用 render() 验证;③ 说明为什么必须封装成函数而不是写一次性代码。

原视频对照 ​

#视频时长
1swiper插件基本使用14:05
2swiper插件配置07:18
3AlloyFinger手势滑动插件10:10
4综合案例-通讯录案例布局和业务分析05:15
5forEach遍历数组03:07
6综合案例-通讯录渲染业务06:36

小结 ​

  1. window 是 BOM 的顶级对象,document、alert、console 都是它的属性,全局 var 声明的变量和函数会自动挂到 window 上,调用时可省略 window.。
  2. setTimeout 只执行一次且需要等待,因此它后面的同步代码先执行;返回值是正整数编号,clearTimeout 必须接收这个编号才能取消。
  3. location 把 URL 拆成可读写的部件:href 读完整地址、赋值即跳转;search 取 ? 之后;hash 取 # 之后;reload(true) 是强制刷新。
  4. 插件的本质是别人写好的代码,学习路径固定为四步:看官网 → 读使用文档 → 写 demo → 应用到开发。
  5. 使用 swiper 的顺序是:引入 CSS/JS → 复制官方结构 → 按需调整尺寸 → 初始化配置;结构和层级不能自行改动,图片必须放在每个 swiper-slide 内部。
  6. 插件默认样式不满足需求时,把对应类名复制到自己的样式表中覆盖;基础态与 active 态要分开写,并注意权重与书写顺序。
  7. swiper 的初始化参数是对象,autoplay、spaceBetween、loop、slidesPerView、分页器开关等含义都应到官方 API 文档查证,而不是猜测。
  8. AlloyFinger 的核心是“给哪个元素绑定 + 在 swiper 回调里读 event.direction”;方向值首字母大写,据此增删类名即可完成滑动交互。
  9. forEach 的回调第一个参数是元素、第二个是索引,比传统 for 更简洁;渲染业务必须封装成 render(),因为新增和删除后都需要重新调用它。