首页
/ freeCodeCamp Bootstrap Grid 实战:用 `row` 与 `col-xs-4` 让按钮并排显示

freeCodeCamp Bootstrap Grid 实战:用 `row` 与 `col-xs-4` 让按钮并排显示

2026-09-07 12:07:58作者:魏侃纯Zoe

本篇基于 freeCodeCamp 开源课程中的 "Use the Bootstrap Grid to Put Elements Side By Side" 课程(对应挑战文件 bad88fee1348ce8acef08815.md),系统拆解 Bootstrap 响应式 12 列网格的运作原理,并结合本仓库中该挑战的真实种子代码、验收断言与参考实现,带你完整掌握 "容器 → 行 → 列" 三层结构与 col-*-* 宽度语法的用法。读完你既能独立完成 Cat Photo App 中 Like / Info / Delete 三个按钮的并排布局,也能把同一套网格思路迁移到任何响应式页面中。

一、课程定位:Cat Photo App 中的第三个操作按钮就位

在本仓库的课程结构中,这一挑战属于 Bootstrap 挑战块,完整顺序记录在 curriculum/structure/blocks/bootstrap.json 中。该块将 Bootstrap 3.3.7 的样式文件作为必选依赖引入:

"required": [
  {
    "link": "https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.7/css/bootstrap.css"
  }
]

同时块的 challengeOrder 标明本课(id: bad88fee1348ce8acef08815)紧跟在 "Warn Your Users of a Dangerous Action with btn-danger"(bad87fee1348ce8acef08814.md)之后,是 Cat Photo App 操作区打造的收尾环节:

  1. 先通过 btn-primarybtn-infobtn-danger 为 Like / Info / Delete 三个按钮分别配上语义化颜色;
  2. 本课再把这三个全宽按钮从竖直堆叠变为水平并排,形成一行动作栏;
  3. 后续课程再依次引入 span、Font Awesome 图标、表单控件等,逐步完成整个页面。

挑战前端的 challengeType: 0 表示这是一道 HTML/CSS 类课程,而其 Markdown 结构(frontmatter + --description-- + --hints-- + --seed-- + --solutions--)正是本仓库课程体系的标准模板——这种结构本身还受到 curriculum/schema/challenge-schema.js 的约束(其中 challengeType 被限定在 0~33 的整数范围),保证了每份挑战文件都能被课程引擎稳定解析与验证。

二、核心概念:响应式 12 列网格系统

课程描述开门见山地给出了本课的地基知识:

Bootstrap uses a responsive 12-column grid system, which makes it easy to put elements into rows and specify each element's relative width.

2.1 "12 列"意味着什么

Bootstrap 把一行(row)的可用宽度平均切分成 12 份逻辑列,然后通过 col-*-n 类告诉元素"你占据其中 n 份"。这样计算出的宽度是相对宽度(百分比),而非固定像素,因此天然能随视口缩放。

占位类 列数 相对宽度
col-xs-4 4 / 12 约 33.3%(1/3)
col-xs-6 6 / 12 50%(1/2)
col-xs-12 12 / 12 100%(整行)

当同一行内各列占位之和恰好等于 12 时,整行被完整铺满、无需换行;若超过 12,超出的列会被换行到下一行(这是网格系统的通用行为,编写多组布局时可利用此特性)。

2.2 mdxs:针对不同屏幕宽度的列类

课程描述强调了一个关键点:Bootstrap 提供了多套列宽类,它们依据用户屏幕宽度被选用。手机屏幕窄、笔记本电脑屏幕宽,所以:

  • col-md-*md 表示 medium(中等)* 是列数。它指定的是元素在**中等宽度屏幕(例如笔记本电脑)**上的列宽;
  • col-xs-*xs 表示 extra small(超小)* 是列数。它指定元素在**超小屏幕(例如老式小屏手机)**上的列宽。

按 Bootstrap 3 网格的分层使用规则,xs 是最基础的档位,col-xs-* 从超小屏幕起即生效;而 smmdlg 是逐级加宽的档位,只有当视口达到对应宽度时才启用、否则列会退回默认的纵向堆叠。Cat Photo App 之所以选用 col-xs-*,正是希望 Like / Info / Delete 三个按钮即使在手机上也能保持并排,而不是在窄屏下退化成竖排堆叠。课程描述中的完整解释对应原文:

Take for example Bootstrap's col-md-* class. Here, md means medium, and * is a number specifying how many columns wide the element should be. We'll use col-xs-*, where xs means extra small (like an extra-small mobile phone screen).

2.3 三层结构:"容器 → 行 → 列"

网格不能脱离上下文单独存在。把页面放入 container-fluid(来自前序课程 bad87fee1348bd9acde08712.md,提供全宽响应容器),内部再套 rowcol-*-*,就构成了标准三层嵌套:

container-fluid      ← 全宽响应容器
└── row              ← 一行(由 12 列逻辑列组成)
    └── col-xs-4     ← 占据 4/12 宽度
    └── col-xs-4
    └── col-xs-4

课程描述特意注明:"Most of Bootstrap's classes can be applied to a div element",并且 "The row class is applied to a div, and the buttons themselves can be nested within it." 也就是说,Bootstrap 网格不要求语义化标签,全部通过 div + class 组合即可完成布局,结构非常轻量。

三、任务拆解:让三个按钮各占 1/3 并排显示

本课的任务表述(原文)是:

Put the Like, Info and Delete buttons side-by-side by nesting all three of them within one <div class="row"> element, then each of them within a <div class="col-xs-4"> element.

拆解为两条明确指令:

  1. 先建一行:新建一个 <div class="row">,把三个按钮整体包进去;
  2. 再分三列:在每个按钮外面各包一层 <div class="col-xs-4">

三个 col-xs-4 相加正好 4 + 4 + 4 = 12,铺满一行。每个按钮既不会互相挤压,也不会溢出换行,视觉上得到三等分的动作栏。

3.1 初始代码(种子文件)

挑战开始时,三个按钮是直接平铺在 container-fluid 下的(未包裹任何网格结构),因此默认按块级元素纵向堆叠。种子代码(保存于本挑战文件的 --seed-- 区块,完整摘录如下)还包含之前课程积累的自定义 CSS(如红色标题、圆形绿边框、缩小的图片):

<link href="https://fonts.googleapis.com/css?family=Lobster" rel="stylesheet" type="text/css">
<style>
  .red-text {
    color: red;
  }

  h2 {
    font-family: Lobster, Monospace;
  }

  p {
    font-size: 16px;
    font-family: Monospace;
  }

  .thick-green-border {
    border-color: green;
    border-width: 10px;
    border-style: solid;
    border-radius: 50%;
  }

  .smaller-image {
    width: 100px;
  }
</style>

<div class="container-fluid">
  <h2 class="red-text text-center">CatPhotoApp</h2>

  <p>Click here for <a href="#">cat photos</a>.</p>

  <a href="#"><img class="smaller-image thick-green-border" src="https://cdn.freecodecamp.org/curriculum/cat-photo-app/relaxing-cat.jpg" alt="A cute orange cat lying on its back."></a>

  <img src="https://cdn.freecodecamp.org/curriculum/cat-photo-app/running-cats.jpg" class="img-responsive" alt="Tuxedo cats running on dirt ground.">
  <button class="btn btn-block btn-primary">Like</button>
  <button class="btn btn-block btn-info">Info</button>
  <button class="btn btn-block btn-danger">Delete</button>
  <p>Things cats love:</p>
  <ul>
    <li>catnip</li>
    <li>laser pointers</li>
    <li>lasagna</li>
  </ul>
  <p>Top 3 things cats hate:</p>
  <ol>
    <li>flea treatment</li>
    <li>thunder</li>
    <li>other cats</li>
  </ol>
  <form action="https://freecatphotoapp.com/submit-cat-photo">
    <label><input type="radio" name="indoor-outdoor"> Indoor</label>
    <label><input type="radio" name="indoor-outdoor"> Outdoor</label>
    <label><input type="checkbox" name="personality"> Loving</label>
    <label><input type="checkbox" name="personality"> Lazy</label>
    <label><input type="checkbox" name="personality"> Crazy</label>
    <input type="text" placeholder="cat photo URL" required>
    <button type="submit">Submit</button>
  </form>
</div>

请留意:btn btn-block btn-primary / btn-info / btn-danger 这些类都必须原样保留——它们决定按钮是块级全宽、并带有 Like(蓝)/ Info(浅蓝)/ Delete(红)的语义配色;网格课程只负责摆放位置,不替换按钮自身样式。

四、验收标准解读:hints 究竟在测什么

挑战文件的 --hints-- 区块包含四条自动化断言,是理解题目边界的最佳材料。它们勾勒出"行、列、标签闭合"三个检查维度。

4.1 行内恰好有三个按钮

const row = document.querySelector('div.row');
const rowChildren = row?.querySelectorAll(`:scope ${'button'}`);
assert.lengthOf(rowChildren, 3);

从页面中找出第一个 div.row,断言其直接后代按钮恰好为 3 个——保证三个按钮确实被收进同一行,而不是被拆进多个行里。

4.2 每个 col-xs-4 恰好容纳一个按钮

const columns = document.querySelectorAll('div.col-xs-4');
assert.lengthOf(columns?.[0]?.querySelectorAll(`:scope ${'button'}`), 1);
assert.lengthOf(columns?.[1]?.querySelectorAll(`:scope ${'button'}`), 1);
assert.lengthOf(columns?.[2]?.querySelectorAll(`:scope ${'button'}`), 1);

按文档顺序取出所有 div.col-xs-4,逐一断言前三个列里各有且仅有一个按钮。这既要求存在三个独立列,也要求按钮被正确地一对一包裹(不允许一个列里塞两个按钮、另一个空着)。

4.3 标签成对闭合

assert.equal(code.match(/<\/button>/g).length, code.match(/<button/g).length);
assert.equal(code.match(/<\/div>/g).length, code.match(/<div/g).length);

最后两段通过对代码字符串统计 <button</button><div</div> 出现次数并要求相等,来强制标签闭合成对——漏写任何一个闭合标签都会导致计数不匹配而判错。这也是 freeCodeCamp 验证 HTML 结构完整性的常用手法:先断言"存在闭合标签",再断言"开闭数量相等"。

五、参考实现:完整的并排按钮方案

本挑战文件 --solutions-- 区块给出的标准答案是在三个按钮外加一层 row、再给每个按钮包一层 col-xs-4。相比种子代码,唯一的改动集中在图片后的按钮区域:

<div class="row">
  <div class="col-xs-4">
    <button class="btn btn-block btn-primary">Like</button>
  </div>
  <div class="col-xs-4">
    <button class="btn btn-block btn-info">Info</button>
  </div>
  <div class="col-xs-4">
    <button class="btn btn-block btn-danger">Delete</button>
  </div>
</div>

将上面这段替换掉种子文件中三个"裸奔"的 <button>,即可通过全部 hints。完整可运行代码如下:

<link href="https://fonts.googleapis.com/css?family=Lobster" rel="stylesheet" type="text/css">
<style>
  .red-text {
    color: red;
  }

  h2 {
    font-family: Lobster, Monospace;
  }

  p {
    font-size: 16px;
    font-family: Monospace;
  }

  .thick-green-border {
    border-color: green;
    border-width: 10px;
    border-style: solid;
    border-radius: 50%;
  }

  .smaller-image {
    width: 100px;
  }
</style>

<div class="container-fluid">
  <h2 class="red-text text-center">CatPhotoApp</h2>

  <p>Click here for <a href="#">cat photos</a>.</p>

  <a href="#"><img class="smaller-image thick-green-border" src="https://cdn.freecodecamp.org/curriculum/cat-photo-app/relaxing-cat.jpg" alt="A cute orange cat lying on its back."></a>

  <img src="https://cdn.freecodecamp.org/curriculum/cat-photo-app/running-cats.jpg" class="img-responsive" alt="Tuxedo cats running on dirt ground.">
  <div class="row">
    <div class="col-xs-4">
      <button class="btn btn-block btn-primary">Like</button>
    </div>
    <div class="col-xs-4">
      <button class="btn btn-block btn-info">Info</button>
    </div>
    <div class="col-xs-4">
      <button class="btn btn-block btn-danger">Delete</button>
    </div>
  </div>

  <p>Things cats love:</p>
  <ul>
    <li>catnip</li>
    <li>laser pointers</li>
    <li>lasagna</li>
  </ul>
  <p>Top 3 things cats hate:</p>
  <ol>
    <li>flea treatment</li>
    <li>thunder</li>
    <li>other cats</li>
  </ol>
  <form action="https://freecatphotoapp.com/submit-cat-photo">
    <label><input type="radio" name="indoor-outdoor"> Indoor</label>
    <label><input type="radio" name="indoor-outdoor"> Outdoor</label>
    <label><input type="checkbox" name="personality"> Loving</label>
    <label><input type="checkbox" name="personality"> Lazy</label>
    <label><input type="checkbox" name="personality"> Crazy</label>
    <input type="text" placeholder="cat photo URL" required>
    <button type="submit">Submit</button>
  </form>
</div>

六、运行与自测:这条网格在仓库中如何被验证

这份挑战属于本仓库 legacy 课程体系中的 HTML/CSS 类任务,其验证逻辑就以 --hints-- 中的 JavaScript 断言为准。做题时你会经历两类检查:

  1. DOM 结构检查:在页面(iframe 沙箱)内用 document.querySelector 等选择器核对 .row.col-xs-4 与按钮的包含关系——这决定了"答案必须长成指定结构",单纯视觉上并排并不能得分;
  2. 源码文本检查:对提交的 HTML 源码做正则计数,保证标签闭合配对。

这种"结构断言 + 文本断言"的双轨验证,也正是整个 curriculum/challenges 目录下大量 HTML/CSS 类挑战通用的验收模式。若想本地观察 Bootstrap 3.3.7 网格的真实渲染效果,可直接把 curriculum/structure/blocks/bootstrap.json 中声明的 Bootstrap 3.3.7 样式链接放入任意 HTML 页面,然后按上文结构书写 row / col-xs-4,拖动浏览器窗口即可看到三种按钮始终以三等分并排。

七、触类旁通:网格宽度是"相对宽度"的乘法表

本课不是网格在仓库课程中的孤例。往前看,"Split Your Bootstrap Row" 课程(bad87fee1348bd9aec908847.md)用两个 col-xs-6 把一行对半拆开;本课则用三个 col-xs-4 做三等分。两者叠加起来,就能组成一张实用的"12 列乘法表":

想要的分割 方案 列数合计
两等分(各 50%) 两个 col-xs-6 6 + 6 = 12
三等分(各 ≈33.3%) 三个 col-xs-4 4 × 3 = 12
四等分(各 25%) 四个 col-xs-3 3 × 4 = 12
2:1 侧栏布局 col-xs-8 + col-xs-4 8 + 4 = 12

理解"列宽 = 12 份中的若干份、且永远基于百分比"这一点后,你就能脱离记忆、直接推演出任意非对称布局的写法。若想深入这套布局在更大视口下的进阶用法,可继续阅读课程块 bootstrap.jsonchallengeOrder 所列的后续挑战(例如用 Font Awesome 图标点缀按钮、用 Bootstrap 对齐表单元素等),它们共同构成了一个从"响应式容器"到"栅格布局"再到"组件美化"的完整闭环。

小结

本课的核心收益可以浓缩为三句话:

  • 三层结构container-fluid(容器)→ row(行)→ col-*-n(列);
  • 相对宽度col-*-n 表示占 12 列中的 n 份,三个 col-xs-4 恰好铺满一行、按钮并排显示;
  • 档位语义xs(超小屏,手机)是最基础档位,md(中等屏,笔记本)等更大档位按屏幕宽度逐级启用。

把这三个概念连同本课的验收断言(行内恰有三个按钮、每列恰有一个按钮、标签成对闭合)落实到 Cat Photo App 的按钮区,你就完成了从"会写 HTML"到"能用栅格系统做响应式排版"的关键一跃。

登录后查看全文
热门项目推荐
相关项目推荐