主题
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:先建立“浏览器总入口”的认识

为什么需要:学习 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 时要留意的现象。
课件图示








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