Skip to content

JavaScript 插件与BootStrap 定制和主题(Bootstrap 2/2) ​

本页对应 课堂视频,共 7 段视频,约 1 小时 35 分。

学习目标 ​

  • 能够使用 Bootstrap 轮播图插件完成三张图片轮播,配置指示器与 data 属性控制,并使用 <picture> 结合媒体查询实现不同视口下图片切换。
  • 能够使用路径导航、缩略图、分页组件和栅格系统搭建课程列表页,并按 xs/sm/md/lg 断点实现每行 1/2/3/4 列布局及响应式页脚。
  • 能够下载并替换 Bootstrap 主题 CSS 文件,验证页面按钮、导航等颜色与风格变化。
  • 能够不依赖 Bootstrap,使用 Flex 布局和百分比宽度实现可嵌套、可自动换行的 12 列自定义栅格系统。
  • 能够使用媒体查询和移动优先策略实现响应式栅格类,说明 xs/sm/md/lg 类名的生效范围与覆盖规则。
  • 能够使用 Less 变量、Mixin 递归和模块化导入结构生成栅格类,并通过修改列数、断点或容器宽度变量后重新编译验证结果。

知识点 ​

先看本节清单:插件不是“组件样式”,而是带交互的 JS 行为 ​

本节笔记保留如下:

1. 下拉菜单
2. collapse
3. 模态框 modal.js
4. 轮播图 carousel.js
...

为什么需要: 下拉菜单、折叠面板、模态框、轮播图这类东西,光靠 CSS 能做出“样子”,但点击切换、自动轮播、指示点联动等交互,需要 JavaScript 插件。Bootstrap 把它们封装好,我们主要是按文档复制结构、补数据、加 data-* 属性。

怎么想: 先查文档对应插件,再复制官方示例,最后把示例里的省略号、占位图、占位文字补全。不要一上来自己写交互。

怎么做: 以所用 Bootstrap 版本文档为准引入对应 JS 文件,不能只引 CSS。转写稿重点演示的是轮播图 carousel.js,所以下面以轮播图为主线;下拉菜单、collapse、modal 先按清单知道“去文档找现成结构”。

易错点: 插件名和组件名容易混;只复制 HTML 不引入 JS,点击没反应;官方示例常省略重复项,不能直接上线。

轮播图:先复制结构,再补全三张图和三个指示点 ​

为什么需要: 手写轮播要处理定时器、左右按钮、小圆点索引、当前项 active,还要考虑响应式图片,成本高。Bootstrap 的 carousel 插件已经把这些行为封装好。

怎么想: 转写稿里说“轮播图属于插件,从文档里复制代码”,复制后先看缺什么:图片没有、指示点数量不够、item 不够。官方示例常用省略号省掉重复项,我们要补齐。注意“轮波图”是口误,正确是“轮播图”。

怎么做: 结构上外层给一个 id,每个指示点用 data-slide-to 关联索引,每个轮播项补 carousel-item,第一项加 active。核心示意:

html
<div id="carouselExampleIndicators" class="carousel slide" data-ride="carousel">
  <ol class="carousel-indicators">
    <li data-target="#carouselExampleIndicators" data-slide-to="0" class="active"></li>
    <li data-target="#carouselExampleIndicators" data-slide-to="1"></li>
    <li data-target="#carouselExampleIndicators" data-slide-to="2"></li>
  </ol>
  <div class="carousel-inner">
    <div class="carousel-item active">
      <img src="./images/play01.jpg" alt="轮播图一">
      <div class="carousel-caption d-none d-md-block">
        <h3>高考乐</h3>
        <p>这里放轮播图说明文字。</p>
      </div>
    </div>
    <div class="carousel-item">
      <img src="./images/play02.jpg" alt="轮播图二">
    </div>
    <div class="carousel-item">
      <img src="./images/play03.jpg" alt="轮播图三">
    </div>
  </div>
</div>

易错点: data-slide-to 不要写成转写稿口述里的“date-slide-to”;索引从 0 开始;指示点数量要和轮播项数量一致;外层 id 与 data-target 必须对应;第一项别忘了 active。

响应式轮播图:用 picture 换源,但别忘给 img 设宽度 ​

为什么需要: 转写稿指出,小视口下如果只把大图按 100% 缩放,文字区域和图片比例可能不好看。更稳妥的是不同屏幕加载不同尺寸图片:大屏用宽图,小屏用更扁或更合适的图。

怎么想: 用 <picture> 做换源。<source> 按顺序匹配,哪个条件先满足就用哪个;最后的 <img> 是兜底。转写稿里调试很久,一度以为 source 没生效,最后发现是图片原始尺寸太大,又少给 img 设置 width: 100%,导致判断被图片撑开效果误导。

怎么做:

html
<picture>
  <source media="(min-width: 992px)" srcset="./images/play01.jpg">
  <source media="(min-width: 768px)" srcset="./images/play01-sm.jpg">
  <img src="./images/play01-xs.jpg" alt="响应式轮播图一">
</picture>
css
.carousel-item img {
  width: 100%;
  display: block;
}

易错点: srcset 不要拼成“SRC set”;media 不要写成“me位置”;条件顺序要清楚;picture 只负责换源,不负责尺寸,样式要落到里面的 img 上;只给 picture 设宽度往往不解决问题。转写稿最后确认:给图片加 width: 100% 后,source 的切换效果才正常显示。

边界提醒:课程列表、分页、页角不是本节插件主线 ​

转写稿后半段还讲了面包屑、缩略图、分页、页角以及自定义栅格系统,这些主要是组件组合、栅格和 LESS 源码结构,不是“JavaScript 插件”这一节的核心。学习时不要把“复制组件结构”和“插件交互”混为一谈:本节先掌握下拉菜单、collapse、modal、carousel 的文档入口和轮播图完整调试过程,后续再展开页面组合与栅格实现。

定制 Bootstrap 与主题替换 ​

为什么需要:项目风格不能永远长得像默认 Bootstrap,颜色、字号、间距往往要统一调整。
怎么想:先分清“定制”和“主题”。定制是改变量或源码;主题是别人已经写好的一套 CSS,直接替换即可。
怎么做:原笔记给了两个入口:

在 Bootswatch 选一套主题,下载后得到一个 CSS 文件,放到项目 css 目录;把原来引入的 bootstrap.css 注释掉,换成主题 CSS。
易错点:不要同时引入默认 Bootstrap 和主题 CSS,否则后引入的会覆盖前面的;主题本质仍是 Bootstrap 样式,不是另一套框架。

轮播图:补齐结构并用 picture 做响应式图片 ​

为什么需要:轮播图复制官方插件代码后通常不完整,图片、指示点、文字都要补。
怎么想:轮播图靠外层 id 和每个小圆点的 data-slide-to 关联;每个 item 对应一张图。响应式图片不能只靠 img 缩放,屏幕很窄时图片会被压得很扁,最好按视口换图。
怎么做:补齐 3 个 item 和 3 个指示点,每个 item 内写 h3 与 p。图片用 picture:

html
<picture>
  <source media="(min-width: 992px)" srcset="./images/play01.jpg">
  <source media="(min-width: 768px)" srcset="./images/play01-sm.jpg">
  <img src="./images/play01-xs.jpg" alt="轮播图一" class="img-responsive">
</picture>

调试时 source 一度不生效,检查地址、media、srcset 都没拼错,最后发现图片原始尺寸太大,缺少宽度限制,导致误判。补上图片宽度 100% 后正常:

css
.carousel-inner img { width: 100%; }

易错点:picture 的 source 是“先满足先加载”,和 CSS 媒体查询“后覆盖前”相反;source 要有 media 和 srcset;最后必须保留 img 兜底;图片要限制宽度。

课程列表:面包屑、缩略图、分页、页脚 ​

为什么需要:课程列表是典型“组件拼装”页面,手写会慢,组件能快速搭结构。
怎么想:先看响应式关系:超小屏一行 1 个,小屏 2 个,中屏 3 个,大屏 4 个,对应栅格 col-sm-6 col-md-4 col-lg-3。
怎么做:面包屑用“路径导航”组件;课程卡片用“缩略图”组件,外层套 container 和 row;图片加 width:100%;按钮分别用 btn-success 和 btn-primary 一类颜色类;分页复制分页组件,可把箭头改成“上一页/下一页”;页脚没有现成组件,用栅格自己写:

html
<footer class="site-footer">
  <div class="container">
    <div class="row">
      <div class="col-sm-6 col-md-3"><h4>标题</h4><ul><li><a href="#">链接</a></li></ul></div>
      <!-- 复制多列 -->
    </div>
    <p class="copyright">Copyright</p>
  </div>
</footer>

页脚样式:背景 #333,链接和文字白色,ul 去圆点、去默认内边距,版权文字居中并加上间距。
易错点:缩略图复制后不要连它的 row/col 一起照搬,行列要由页面栅格控制;图片不设宽会撑破卡片;页脚类名写错会导致样式完全不生效。

自定义非响应式栅格:先理解“行 + 列 + 百分比” ​

为什么需要:会用 Bootstrap 不等于会做布局系统,理解栅格才能改列数、改断点。
怎么想:一行是 flex 容器,允许换行;列不伸不缩,宽度按 n/12 计算。
怎么做:

css
.row { display: flex; flex-wrap: wrap; }
[class*="col-"] { flex: 0 0 auto; box-sizing: border-box; padding: 0 10px; }
.col-1 { width: 8.33%; }
.col-2 { width: 16.66%; }
/* ...手写到 col-12 */

易错点:百分比要算准;列宽和 padding 必须配 box-sizing;非响应式只是固定比例,不会随屏幕自动变列数。

响应式栅格:移动优先与媒体查询 ​

为什么需要:同一张页面在手机、平板、桌面要显示不同列数。
怎么想:移动优先:xs 的类写在外层,所有屏幕默认生效;sm/md/lg 写进对应 min-width 媒体查询,大屏继承小屏,除非被更大断点覆盖。
怎么做:

less
.grid(@type: xs) {
  .make-col(@n) when (@n <= 12) {
    .col-@{type}-@{n} { width: percentage(@n / 12); }
    .make-col(@n + 1);
  }
  .make-col(1);
}
.grid(xs);
@media (min-width: 768px) { .grid(sm); }
@media (min-width: 992px) { .grid(md); }
@media (min-width: 1200px) { .grid(lg); }

易错点:@{type} 是变量拼选择器;除法要加括号;递归必须有 when (@n <= 12) 终止条件,否则无限生成;媒体查询里的类只在该断点以上生效。

Less 目录结构与递归混合 ​

为什么需要:Bootstrap 源码把变量、混合、重置、布局、组件拆开,入口只负责 @import,方便维护和二次定制。
怎么想:仿照它建 bootstrap.less 入口,拆 variables.less、mixins.less、normalize.less、layout.less、grid.less;阈值、版心宽度、列数都做成变量。
怎么做:变量里定义 @sm: 768px; @md: 992px; @lg: 1200px; @columns: 12;,生成列宽时用 percentage(@n / @columns);编译只编译入口文件。
易错点:子文件单独打开会报“变量找不到”,因为变量是在入口中统一导入的;改列数只需改变量,但递归上限也要跟着改;不要编译 mixins.less 这类只供引入的文件。

为什么需要自定义栅格系统 ​

Bootstrap 的栅格很好用,但作为前端不能只会复制类名。转写稿里说得很直接:要理解它“怎么设计出来”,才能自己搞一套页面结构框架。自定义栅格的目标不是重写 Bootstrap,而是抓住核心:一行如何分栏、列宽如何按比例计算、不同屏幕下如何切换比例。视频4先做非响应式版本,视频5再加响应式,视频6—7用 Less 组织变量、混合和入口文件,最后自动生成类名。

先做非响应式:Flex + 百分比 ​

为什么需要:如果每列都手写宽度,页面一改就乱;用“行/列”结构才能复用。怎么想:一行是容器,列是项目,列宽用百分比表示 12 份中的几份。怎么做:转写稿中自定义栅格用 Flex,不用浮动。关键代码如下(按转写稿整理):

less
.row {
  display: flex;
  flex-wrap: wrap;
}
[class*="col-"] {
  flex: 0 0 auto;
  box-sizing: border-box;
  padding: 0 10px;
}
.col-1  { width: 8.33%; }
.col-2  { width: 16.66%; }
.col-3  { width: 25%; }
.col-4  { width: 33.33%; }
/* ……一直到 .col-12 { width: 100%; } */

易错点:flex-wrap: wrap 不能少,否则列多了不换行;box-sizing: border-box 要加,否则 padding 会撑宽;百分比要保留两位左右,转写稿里也是用计算器逐个算,说明手写很累,这正是后面用 Less 的原因。

响应式:移动优先与媒体查询 ​

为什么需要:同一张页面在手机、平板、桌面要显示不同列数。怎么想:转写稿反复强调“移动优先,大的跟着小的走”。只写 col-xs-6,所有屏幕都按 6 份;写了 col-md-3,只有中等屏幕及以上才按 3 份,小屏幕仍按默认或 xs。怎么做:把 xs 类写在外层,把 sm/md/lg 放进媒体查询:

less
.col-xs-1 { width: 8.33%; }
/* …… */

@media (min-width: 768px) {
  .col-sm-1 { width: 8.33%; }
  /* …… */
}
@media (min-width: 992px) {
  .col-md-1 { width: 8.33%; }
  /* …… */
}
@media (min-width: 1200px) {
  .col-lg-1 { width: 8.33%; }
  /* …… */
}

易错点:媒体查询里的类名必须带对应前缀,如 .col-sm-*;xs 不写媒体查询,所以任何屏幕都生效;sm 的条件是 min-width: 768px,768 以上都生效,除非 md/lg 再覆盖。转写稿中“这个弯要转过来”说的就是覆盖关系。

用 Less 变量和目录组织 ​

为什么需要:阈值、版心宽度、列数会改,散落在 CSS 里难维护。怎么想:仿 Bootstrap 的 Less 源码结构,入口文件只负责引入。怎么做:转写稿建立 eight-strip.less 作为入口,引入变量、混合、normalize、layout、grid。变量可写成:

less
@sm-width: 768px;
@md-width: 992px;
@lg-width: 1200px;
@container-xs: 100%;
@container-sm: 750px;
@container-md: 970px;
@container-lg: 1170px;
@columns: 12;

layout.less 负责全局盒模型和 .container 的媒体查询;grid.less 负责行列。易错点:变量文件不需要单独编译,入口文件引入后统一编译;转写稿里 layout 单独看会报“找不到变量”,因为变量是在入口里一起引入的。

递归混合生成类名 ​

为什么需要:12 份 × 4 种屏幕 = 48 个类名,手写又累又容易错。怎么想:Less 没有普通循环,但混合可以自己调用自己,形成递归;加结束条件 @number <= @columns。怎么做:先写内层生成器,再套外层传类型:

less
.grid(@type: xs) {
  .make-col(@number) when (@number <= @columns) {
    .col-@{type}-@{number} {
      width: percentage(@number / @columns);
    }
    .make-col(@number + 1);
  }
  .make-col(1);
}

.grid(xs);
@media (min-width: @sm-width) { .grid(sm); }
@media (min-width: @md-width) { .grid(md); }
@media (min-width: @lg-width) { .grid(lg); }

易错点:选择器插值要写 .col-@{type}-@{number};除法要加括号 (@number / @columns);percentage() 是 Less 自带函数;递归没有结束条件会无限生成;外层 .grid(@type) 内再定义 .make-col,才能让内层使用 @type。转写稿先在单独文件里用 make-col(1) 验证能生成 1—12,再放回项目编译,编译后得到 48 个类名,改 @columns: 16 就能整体变 16 份。

调试与内容约束 ​

转写稿里还出现一个典型误判:响应式类名写对了,但列里的图片原始尺寸太大,显示溢出,看起来像“媒体查询没生效”。后来给图片补 width: 100% 才正常。栅格只负责列宽,不负责内容尺寸;课程列表里也加了类似 .courses img { width: 100%; }。所以自定义栅格完成后,还要约束图片等替换元素,否则大图会撑破列,导致调试方向跑偏。

作业目标与使用方式 ​

本节作业不是“抄一个页面”,而是用 Bootstrap 案例练熟文档检索、组件拼装和响应式调试,最后反过来用 Less 实现一套自己的栅格系统。先保留本节任务清单:

text
1. bootstrap页面案例
2. 读bootstrap文档
3. 尝试 4、5 的使用
4. 自己使用 less 实现栅格系统

下面按这四项展开。每个知识点都按“为什么需要 → 怎么想 → 怎么做 → 易错点”来写。

一、Bootstrap 页面案例:先搭轮播图,再补响应式图片 ​

为什么需要:轮播图、课程列表、分页、页脚是后台/课程页的高频结构。用现成组件能快速搭出页面骨架,但真正要学的是“组件如何与栅格、响应式图片配合”。

怎么想:先复制轮播图插件代码,再补齐三张图片、三组文字和三个小圆点。轮播图小圆点的控制依赖 data-slide-to 与最外层 id 关联;小屏下文字显示不全时,不要只让图片 width:100% 硬缩,而要考虑换图。

怎么做:轮播项按三份复制,图片路径分别换成 01/02/03,每个 item 内补 h3 和段落。响应式图片用 picture:

html
<picture>
  <source media="(min-width: 992px)" srcset="./img/play01.jpg">
  <source media="(min-width: 768px)" srcset="./img/play01-sm.jpg">
  <img src="./img/play01-xs.jpg" alt="轮播图小屏图片">
</picture>

转写中调试很久,最后发现不是 source 拼写错,而是图片没有限制宽度,导致“看起来一直加载小图”的误判。给图片补:

css
.course-list img {
  width: 100%;
}

易错点:picture 的 source 是“先有效先加载”,与 CSS 媒体查询“后覆盖前”不同;source 用 srcset,不是 src;轮播图外层 id 与 data-target 要对上;图片必须补 width:100%,否则大图会把容器撑开。

二、读 Bootstrap 文档:按组件找现成结构 ​

为什么需要:Bootstrap 的组件名和日常叫法不完全一致。比如“面包屑导航”在文档里叫“路径导航”,课程卡片可看“缩略图”,分页就是“分页”组件。不熟文档就找不到。

怎么想:先确定页面区块:路径导航、课程列表、分页、页脚。前三个优先找组件,页脚没有现成组件就手写栅格。

怎么做:路径导航复制后改“首页 / 课程 / 推荐课程”,最后一个可不做链接;课程列表先确定响应式份数:超小屏 12 份、小屏 6 份、中屏 4 份、大屏 3 份,即 col-sm-6 col-md-4 col-lg-3。缩略图复制后只保留内部结构,外层行列自己写。分页复制后把箭头改成“上一页/下一页”。页脚用 footer + container + 行 + 列,列按 col-sm-6 col-md-3 组织。

易错点:复制组件后常把示例外层行列一起复制,导致嵌套混乱;课程图片同样要 width:100%;页脚没有组件时不要硬找,直接用栅格手写。

三、尝试 4、5 的使用:只迁移结构,不硬套类名 ​

为什么需要:Bootstrap 3、4、5 的类名和布局方式有差异。作业要求“尝试 4、5 的使用”,目的是让你养成按版本文档查写法的习惯。

怎么想:把已有案例当作结构参考,不把 3 的类名无条件套到 4/5。重点验证栅格、容器、轮播图、分页、卡片/缩略图这些区块。

怎么做:打开对应版本文档,逐项替换类名与结构;先保证页面骨架能显示,再补样式。遇到组件结构变化,以文档示例为准。

易错点:不要凭记忆写 4/5 类名;不要同时混用多个版本的类名;迁移后要重新检查响应式断点和图片宽度。

四、自己用 Less 实现栅格系统:变量、混合、递归循环 ​

为什么需要:会用 Bootstrap 只是“会复制”,理解栅格系统怎么生成,才算真正掌握布局框架。转写中强调:栅格系统是核心,其他组件技术含量相对低。

怎么想:先做非响应式,再做响应式。用变量保存断点、容器宽度、列数;用 Flex 做行容器;用混合生成 .col-xs-1 到 .col-xs-12。Less 没有直接循环,但混合可以递归调用自己,用条件终止。

怎么做:目录上设入口文件,只编译入口,变量、混合、重置、布局、栅格分别引入。关键混合可写成:

less
.grid(@type: xs) {
  .make-col(@number) when (@number <= 12) {
    .col-@{type}-@{number} {
      width: percentage(@number / 12);
    }
    .make-col(@number + 1);
  }
  .make-col(1);
}

外层再按媒体查询调用 .grid(sm); .grid(md); .grid(lg);。变量可把 12 换成 @columns,把断点换成 @sm-width、@md-width 等。

易错点:递归必须有终止条件,否则无限生成;选择器插值要写 @{number};只编译入口文件,变量和混合文件不单独编译;移动优先下,xs 写在外层,sm/md/lg 放进对应 min-width 媒体查询;改列数时所有百分比要跟着变量走。

五、调试记录与检查清单 ​

转写中的调试很典型:轮播图小屏文字挤压 → 换 picture;picture 看似不生效 → 检查 source 顺序、srcset、媒体条件;最终发现图片未设 width:100%,造成“加载错图”的误判。课程列表图片撑破列,也是同一类问题。Less 递归则要检查终止条件、变量作用域和入口编译。

交作业前检查:轮播图 id/data-slide-to 是否对应;picture 是否三档图片齐全;课程列表是否按 sm-6/md-4/lg-3 响应;分页和页脚是否在版心内;Less 是否只编译入口;把列数从 12 改成 16 时是否仍能生成正确百分比。

课堂画面 ​

课堂画面 1

课堂画面 2

课堂画面 3

课堂画面 4

课堂画面 5

课堂画面 6

代码示例 ​

html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Bootstrap 轮播图 + picture 响应式图片</title>
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@3.4.1/dist/css/bootstrap.min.css">
  <style>
    /* 关键:必须限制图片宽度,否则大图会撑开容器,导致误判响应式图片没生效 */
    .carousel-inner .item img {
      width: 100%;
    }
    .carousel-inner .item picture {
      display: block;
    }
    .carousel-caption {
      background: rgba(0, 0, 0, .35);
    }
  </style>
</head>
<body>
  <div class="container">
    <div id="carousel-example-generic" class="carousel slide" data-ride="carousel">
      <!-- 小圆点:data-target 指向最外层轮播图 id,data-slide-to 对应索引 -->
      <ol class="carousel-indicators">
        <li data-target="#carousel-example-generic" data-slide-to="0" class="active"></li>
        <li data-target="#carousel-example-generic" data-slide-to="1"></li>
        <li data-target="#carousel-example-generic" data-slide-to="2"></li>
      </ol>

      <div class="carousel-inner" role="listbox">
        <div class="item active">
          <picture>
            <!-- picture 的 source 是“先匹配先生效”,与 CSS 媒体查询覆盖顺序相反 -->
            <source media="(min-width: 992px)"
                    srcset="https://via.placeholder.com/1170x400/337ab7/ffffff?text=play01+LG">
            <source media="(min-width: 768px)"
                    srcset="https://via.placeholder.com/768x400/5cb85c/ffffff?text=play01+SM">
            <img src="https://via.placeholder.com/480x300/f0ad4e/ffffff?text=play01+XS" alt="play01">
          </picture>
          <div class="carousel-caption">
            <h3>高考乐</h3>
            <p>第一张轮播图文字,屏幕变小时图片会自动切换。</p>
          </div>
        </div>

        <div class="item">
          <picture>
            <source media="(min-width: 992px)"
                    srcset="https://via.placeholder.com/1170x400/d9534f/ffffff?text=play02+LG">
            <source media="(min-width: 768px)"
                    srcset="https://via.placeholder.com/768x400/5bc0de/ffffff?text=play02+SM">
            <img src="https://via.placeholder.com/480x300/8e44ad/ffffff?text=play02+XS" alt="play02">
          </picture>
          <div class="carousel-caption">
            <h3>高考乐高二</h3>
            <p>第二张轮播图文字,使用 picture 切换不同尺寸图片。</p>
          </div>
        </div>

        <div class="item">
          <picture>
            <source media="(min-width: 992px)"
                    srcset="https://via.placeholder.com/1170x400/2c3e50/ffffff?text=play03+LG">
            <source media="(min-width: 768px)"
                    srcset="https://via.placeholder.com/768x400/18bc9c/ffffff?text=play03+SM">
            <img src="https://via.placeholder.com/480x300/e67e22/ffffff?text=play03+XS" alt="play03">
          </picture>
          <div class="carousel-caption">
            <h3>高考乐高三</h3>
            <p>第三张轮播图文字,轮播图整体也是响应式的。</p>
          </div>
        </div>
      </div>

      <a class="left carousel-control" href="#carousel-example-generic" role="button" data-slide="prev">
        <span class="glyphicon glyphicon-chevron-left" aria-hidden="true"></span>
        <span class="sr-only">上一张</span>
      </a>
      <a class="right carousel-control" href="#carousel-example-generic" role="button" data-slide="next">
        <span class="glyphicon glyphicon-chevron-right" aria-hidden="true"></span>
        <span class="sr-only">下一张</span>
      </a>
    </div>
  </div>

  <script src="https://cdn.jsdelivr.net/npm/jquery@1.12.4/dist/jquery.min.js"></script>
  <script src="https://cdn.jsdelivr.net/npm/bootstrap@3.4.1/dist/js/bootstrap.min.js"></script>
</body>
</html>

关键行与易错点:

  • 轮播图最外层必须有 id="carousel-example-generic",小圆点的 data-target 要指向这个 id,data-slide-to 对应 0、1、2。
  • 第一个 .item 必须加 active,第一个小圆点也必须加 active。
  • picture 中 source 是“先匹配先生效”,所以先写 min-width: 992px,再写 min-width: 768px,最后用 img src 兜底超小屏。
  • 最容易错的是忘记给图片设置 width: 100%。大图会把容器撑开,看起来像 source 没生效,其实是被大图尺寸误判了。
  • 实际项目把占位图地址换成 play01.jpg、play01-sm.jpg、play01-xs.jpg 即可。
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Bootstrap 课程列表页面</title>
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@3.4.1/dist/css/bootstrap.min.css">
  <style>
    .my-breadcrumb {
      margin: 20px 0;
    }
    .course-list img {
      width: 100%;
    }
    .my-footer {
      background: #333;
      color: #fff;
      padding: 40px 0;
      margin-top: 20px;
    }
    .my-footer a {
      color: #fff;
    }
    .my-footer ul {
      list-style: none;
      padding-left: 0;
    }
    .copyright {
      text-align: center;
      margin-top: 20px;
    }
  </style>
</head>
<body>
  <div class="container">
    <ol class="breadcrumb my-breadcrumb">
      <li><a href="#">首页</a></li>
      <li><a href="#">课程</a></li>
      <li class="active">推荐课程</li>
    </ol>

    <div class="row course-list">
      <div class="col-xs-12 col-sm-6 col-md-4 col-lg-3">
        <div class="thumbnail">
          <img src="https://via.placeholder.com/400x250/337ab7/ffffff?text=HTML5" alt="HTML5">
          <div class="caption">
            <h3>HTML5</h3>
            <p>HTML5 课程介绍文字,共二十个字左右。</p>
            <p>
              <a href="#" class="btn btn-success">立即购买</a>
              <a href="#" class="btn btn-primary">在线试看</a>
            </p>
          </div>
        </div>
      </div>

      <div class="col-xs-12 col-sm-6 col-md-4 col-lg-3">
        <div class="thumbnail">
          <img src="https://via.placeholder.com/400x250/5cb85c/ffffff?text=CSS3" alt="CSS3">
          <div class="caption">
            <h3>CSS3</h3>
            <p>CSS3 课程介绍文字,共二十个字左右。</p>
            <p>
              <a href="#" class="btn btn-success">立即购买</a>
              <a href="#" class="btn btn-primary">在线试看</a>
            </p>
          </div>
        </div>
      </div>

      <div class="col-xs-12 col-sm-6 col-md-4 col-lg-3">
        <div class="thumbnail">
          <img src="https://via.placeholder.com/400x250/f0ad4e/ffffff?text=JavaScript" alt="JavaScript">
          <div class="caption">
            <h3>JavaScript</h3>
            <p>JavaScript 课程介绍文字,共二十个字左右。</p>
            <p>
              <a href="#" class="btn btn-success">立即购买</a>
              <a href="#" class="btn btn-primary">在线试看</a>
            </p>
          </div>
        </div>
      </div>

      <div class="col-xs-12 col-sm-6 col-md-4 col-lg-3">
        <div class="thumbnail">
          <img src="https://via.placeholder.com/400x250/d9534f/ffffff?text=Bootstrap" alt="Bootstrap">
          <div class="caption">
            <h3>Bootstrap</h3>
            <p>Bootstrap 课程介绍文字,共二十个字左右。</p>
            <p>
              <a href="#" class="btn btn-success">立即购买</a>
              <a href="#" class="btn btn-primary">在线试看</a>
            </p>
          </div>
        </div>
      </div>
    </div>

    <div class="text-center">
      <ul class="pagination">
        <li><a href="#">上一页</a></li>
        <li class="active"><a href="#">1</a></li>
        <li><a href="#">2</a></li>
        <li><a href="#">3</a></li>
        <li><a href="#">下一页</a></li>
      </ul>
    </div>
  </div>

  <footer class="my-footer">
    <div class="container">
      <div class="row">
        <div class="col-xs-12 col-sm-6 col-md-3">
          <h4>关于我们</h4>
          <ul>
            <li><a href="#">公司介绍</a></li>
            <li><a href="#">联系方式</a></li>
          </ul>
        </div>
        <div class="col-xs-12 col-sm-6 col-md-3">
          <h4>课程中心</h4>
          <ul>
            <li><a href="#">前端课程</a></li>
            <li><a href="#">后端课程</a></li>
            <li><a href="#">移动开发</a></li>

常见错误 ​

  1. 现象:轮播图小圆点有 3 个,但图片只轮播 2 张或点击圆点错位。原因:复制官方示例时保留了省略号,未补齐 .item,且 data-slide-to 与条目索引未一一对应。修法:补齐轮播项数量,统一外层 id,逐项核对 data-slide-to 的 0/1/2 索引。
  2. 现象:picture 响应式图片不按断点切换,总显示某一张图。原因:只怀疑 source 写错,实际常是 img 未限制宽度,大图撑开后造成误判。修法:先给 picture 内图片设置 width:100%,再检查 source 的 media 条件和顺序。
  3. 现象:自定义响应式栅格类名在部分屏幕不生效。原因:把 XS 也写进媒体查询,或只写 MD 却期望小屏生效。修法:按移动优先处理,XS 放全局,SM/MD/LG 分别放入对应 min-width 媒体查询,并核对阈值变量。
  4. 现象:修改 Less 栅格列数后,生成结果仍是 12 份。原因:递归混合中写死 12,或编译的不是入口文件。修法:统一改用 @columns 变量,只编译入口 atstrip.less,检查编译产物是否按新列数生成。
  5. 现象:替换 Bootswatch 主题后样式不生效或旧样式残留。原因:新主题 CSS 未替换原 Bootstrap 引用,或引入顺序、缓存有问题。修法:替换原 bootstrap.css 引用,确认主题 CSS 顺序正确,清理缓存后刷新。
  6. 现象:单独打开 layout.less 报变量找不到。原因:变量文件只由入口文件统一导入,单独编译该分文件时作用域中没有变量。修法:不要单独编译分文件,只编译统一入口文件,由入口先导入变量、混合和分模块。

练习 ​

  1. 用 Bootstrap 完成一个页面:轮播图、面包屑、课程列表、分页、页脚,并检查各断点下列数是否符合预期。
  2. 用 picture 改造轮播图图片,准备大、中、小三种尺寸,验证 992px、768px 附近图片切换是否正确,并记录一次排错过程。
  3. 用 Less 自定义栅格系统,分别生成 12 列和 16 列版本,验证一行分列、嵌套分列和自动换行。
  4. 从 Bootswatch 选择一套主题替换现有页面样式,记录替换了哪个 CSS、是否清理缓存、页面哪些颜色发生变化。
  5. 给定一个“picture 不切换图片”的页面,按“DOM 数量→图片宽度→source 条件→浏览器渲染结果”的顺序写出排查步骤。

原视频对照 ​

#视频时长
1Bootstrap页面案例02-轮播图23:57
2Bootstrap页面案例03-课程列表13:17
3Bootstrap主题02:30
4自定义栅格系统09:57
5自定义响应式栅格系统11:36
6less实现栅格系统01-目录结构08:40
7less实现栅格系统0225:40

小结 ​

  1. 复制插件示例后必须补齐 DOM 数量,并核对 data-* 与目标索引,不能保留示例省略号。
  2. 响应式图片排错应先看图片实际渲染尺寸和 CSS 约束,再检查 source 条件。
  3. 响应式栅格的关键不是类名多少,而是移动优先下媒体查询的生效范围与覆盖关系。
  4. 自定义栅格应把阈值、容器宽度、列数变量化,避免在循环和类名中写死。
  5. Less 递归混合必须有明确终止条件,且只编译统一入口文件。
  6. 主题定制本质上是变量和 CSS 覆盖,替换后要检查引入顺序与缓存。
  7. 页面拼装效率来自对组件文档的熟练度,但真正排错仍要回到 DOM、CSS 和编译产物。