首页
/ freeCodeCamp Bootstrap 挑战解析:用 col-xs-* 栅格响应式排布表单复选框

freeCodeCamp Bootstrap 挑战解析:用 col-xs-* 栅格响应式排布表单复选框

2026-09-07 21:58:02作者:邵娇湘

本篇指南基于 freeCodeCamp 开源课程仓库中 Bootstrap 模块的 "Responsively Style Checkboxes" 挑战(源文件),系统讲解如何利用 Bootstrap 3 的 col-xs-* 栅格类把页面中的复选框排成等宽三列,让表单在任何屏幕宽度下都保持均匀分布。读完本文,你将掌握 12 栅格体系在 form 元素上的应用模式、该挑战的完整解题步骤与验收测试语义,并能理解它在 Cat Photo App 系列练习中的前后衔接位置。

一、挑战背景与课程定位

本挑战属于 freeCodeCamp 经典(legacy)前端课程中 Bootstrap 模块,是构建 CatPhotoApp(猫咪照片应用)练习链上的一环。挑战文件头部 frontmatter 给出了它的元信息:

id: bad87fee1348bd9aeda08845
title: Responsively Style Checkboxes
challengeType: 0
forumTopicId: 18269
required:
  - link: >-
      https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.2.0/css/font-awesome.css
    raw: true
dashedName: responsively-style-checkboxes

几个字段的含义:

  • challengeType: 0:在仓库的 challenge-types.ts 中,const html = 0,即该挑战属于“HTML 经典型”挑战,学员直接在浏览器编辑器中书写 HTML/CSS。它的渲染视图为 classic(见 viewTypes),提交评估方式为 tests(见 submitTypes),即通过内嵌测试字符串自动判分。
  • forumTopicId:对应的社区讨论主题编号。
  • required:声明本挑战额外需要引入 Font Awesome 4.2.0 样式表(raw: true 表示把 link 原样注入页面)。
  • dashedName:生成 URL 使用的短横线式 slug。

根据模块结构文件 bootstrap.json,整个 Bootstrap 模块加载 Bootstrap 3.3.7 的 CSS,其 helpCategoryHTML-CSSblockLayoutlegacy-challenge-list(按经典旧式列表展示)。本挑战在该模块 31 道题的 challengeOrder 中排在第 16 位,紧随 "Responsively Style Radio Buttons"(源文件)之后,再往后是 "Style Text Inputs as Form Controls" 与 "Line up Form Elements Responsively with Bootstrap"——整条链路都在演示同一个主题:用栅格让表单元素像内容块一样整齐排布

二、核心原理:为什么 col-xs-* 能作用于表单元素

挑战描述开门见山:Since Bootstrap's col-xs-* classes are applicable to all form elements, you can use them on your checkboxes too!(由于 Bootstrap 的 col-xs-* 类适用于所有表单元素,你也可以把它们用在复选框上。)

这意味着一个关键认知:栅格系统并不关心被布局的是图片、按钮还是 input。Bootstrap 3 采用 12 列浮动栅格:

  • .row 是栅格行容器,内含 15px 负外边距用于抵消列的 gutter;
  • .col-xs-N 表示在超小屏及以上的视口宽度下,元素占据 12 等分中的 N 份,通过 float: left 与百分比宽度实现横向并排;
  • col-xs-44 / 12 ≈ 33.333% 宽度,三个 col-xs-4 正好填满一整行。

.col-xs-* 之所以能“无论屏幕多宽都保持排布”,是因为它是 Bootstrap 3 中唯一不包裹在媒体查询里的断点类——它默认在任何宽度都生效,而 col-sm-*(≥768px)、col-md-*(≥992px)、col-lg-*(≥1200px)才各自有最小宽度门槛。因此当一行中放入三个 col-xs-4 时,手机竖屏、平板、桌面都会保持三列等宽,不会因为分辨率变化而折行失衡。

落到 DOM 结构上,.row 会建立块级格式化上下文并清除浮动,内部的列像“网格单元格”一样各自占位。原本依次纵向排列的三个 <label><input type="checkbox">…</label>,一旦各自被包进 col-xs-4,就变成同处一行、均匀分布的三个单元。这正是挑战标题中 “Responsively” 一词的落点:不写一行自定义 CSS,仅靠栅格类即可完成响应式排布。

三、任务要求逐条拆解

挑战指令只有两句话:

Nest all three of your checkboxes in a <div class="row"> element. Then nest each of them in a <div class="col-xs-4"> element.

即需要构造如下三层嵌套结构:

<form>
  <div class="row">                        ← 行容器,包住全部复选框
    <div class="col-xs-4">                 ← 每个复选框一个 1/3 宽列
      <label><input type="checkbox" name="personality"> Loving</label>
    </div>
    <div class="col-xs-4"> … Lazy … </div>
    <div class="col-xs-4"> … Crazy … </div>
  </div>
</form>

注意一个细节:栅格的“行”与表单提交用的 <form> 并不冲突——row 只是视觉/布局层面的容器,form 依旧负责把内部所有 input 一并提交。种子代码中复选框 name 均为 personality,无论怎么布局,提交语义不变。

在初始种子代码(seed)中,页面主体已经搭好了 CatPhotoApp 的大部分结构,完整代码如下:

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

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

</style>

<div class="container-fluid">
  <div class="row">
    <div class="col-xs-8">
      <h2 class="text-primary text-center">CatPhotoApp</h2>
    </div>
    <div class="col-xs-4">
      <a href="#"><img class="img-responsive 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>
    </div>
  </div>
  <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"><i class="fa fa-thumbs-up"></i> Like</button>
    </div>
    <div class="col-xs-4">
      <button class="btn btn-block btn-info"><i class="fa fa-info-circle"></i> Info</button>
    </div>
    <div class="col-xs-4">
      <button class="btn btn-block btn-danger"><i class="fa fa-trash"></i> Delete</button>
    </div>
  </div>
  <p>Things cats <span class="text-danger">love:</span></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">
    <div class="row">
      <div class="col-xs-6">
        <label><input type="radio" name="indoor-outdoor"> Indoor</label>
      </div>
      <div class="col-xs-6">
        <label><input type="radio" name="indoor-outdoor"> Outdoor</label>
      </div>
    </div>
    <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>

种子代码中最值得注意的参照物是它已经示范了一遍解法:表单内的两个单选按钮(Indoor/Outdoor)就包在 row + 各占一个 col-xs-6 的两列里,实现一行并排。而紧随其后的三个复选框仍是裸的 <label> 逐行纵向堆叠,形态明显不一致——本挑战要做的就是“抄自己作业”,把同样的栅格技巧套到复选框上,区别只是把单选按钮的 6/6 两分法换成 4/4/4 三分法。

四、分步实现与最终解法

动手时可以按以下三步推进:

  1. 圈定目标:找到 <form> 内三个 name="personality" 的复选框,它们分别是 Loving、Lazy、Crazy;
  2. 补行容器:在三个复选框外面包一层 <div class="row"></div>。需要注意 .row 会引入负外边距,因此它与上方的单选按钮栅格行是并列的两行,而不是相互嵌套,否则会破坏父级栅格的列宽度计算;
  3. 逐列包裹:把每个 <label>…复选框…</label> 分别放入一个 <div class="col-xs-4"> 中,使三个列构成一行完整的 12 栅格(4 + 4 + 4 = 12)。

改造后 <form> 部分应呈现如下结构(文件其余部分与种子代码保持一致,无需改动):

<form action="https://freecatphotoapp.com/submit-cat-photo">
  <div class="row">
    <div class="col-xs-6">
      <label><input type="radio" name="indoor-outdoor"> Indoor</label>
    </div>
    <div class="col-xs-6">
      <label><input type="radio" name="indoor-outdoor"> Outdoor</label>
    </div>
  </div>
  <div class="row">
    <div class="col-xs-4">
      <label><input type="checkbox" name="personality"> Loving</label>
    </div>
    <div class="col-xs-4">
       <label><input type="checkbox" name="personality"> Lazy</label>
    </div>
    <div class="col-xs-4">
       <label><input type="checkbox" name="personality"> Crazy</label>
    </div>
  </div>
  <input type="text" placeholder="cat photo URL" required>
  <button type="submit">Submit</button>
</form>

这正是挑战文档 --solutions-- 段给出的参考解法中,相对于种子代码的唯一实质变更。保存并运行后,Loving / Lazy / Crazy 三个选项会像上方 Like / Info / Delete 三个按钮一样,以三等分列均匀铺满一行。

五、验收测试逐条解析

挑战的 --hints-- 部分定义了自动判分规则,每条 hint 对应一段在浏览器环境执行的 Chai/断言代码。理解它们有助于避免“能过测试但结构跑偏”的写法。

Hints 1:所有复选框必须位于同一个 row 容器内

const checkboxes = document.querySelectorAll(`div.row input[type="checkbox"]`);
assert.lengthOf(checkboxes, 3);

选择器为 div.row input[type="checkbox"],即“凡是 classrowdiv 内部的 checkbox”。它要求能找到 3 个复选框——也就是三个复选框必须全部落在 row 容器内(否则数量不足 3)。注意:该选择器并未限定必须是“同一个” row 的 direct child,若把三个复选框拆进多个 row,此断言仍可能凑足 3 个,但会被下面的断言拦截。

Hints 2:每个复选框各自位于独立的 col-xs-4 列中

const checkboxes = document.querySelectorAll(`div.col-xs-4 input[type="checkbox"]`);
assert.lengthOf(checkboxes, 3);

这是最关键的约束:选择器定位“位于 col-xs-4 列内的复选框”。三个复选框必须分别被包进三个 col-xs-4 容器,才能各自命中一次、凑足数量 3。若把三个复选框塞进同一个 col-xs-4,这里只会匹配到 3 个(同一容器内 3 个 input 也会被选中)…实际上 div.col-xs-4 input 会命中该容器下全部复选框,数量仍为 3,因此本断言真正防的是“复选框没有被 col-xs-4 包裹”的情况。两条断言合起来保证了「row 包裹 + 各自独立成列」的最终形态。

Hints 3:所有 div 标签必须闭合配对

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

这组断言直接对学员提交的源码字符串做正则统计,要求 <div 开标签与 </div> 闭标签数量相等——防止多包一层 row/col-xs-4 后忘记补闭合标签,导致页面结构被意外打散。HTML 中 div 标签数量不匹配时浏览器容错解析可能“看起来正常”,但后续兄弟元素会被错误地吞进容器,这类结构性错误正是 freeCodeCamp 用该断言拦截的原因。

从测试的书写形态(document.querySelectorAllassert.lengthOf、对源码字符串的 match)可以看出,这些测试属于 challenge-schema.js 中所定义的 tests 数组(text + testString)结构,判分时在学员代码的浏览器环境里执行,这也与 submitTypes[html] = 'tests' 的类型定义相互印证。

六、同系列挑战横向对照:同一 12 栅格的不同切分

本挑战放在模块中并非孤立,前后三关正好演示了 12 栅格三种典型切分:

挑战 栅格切分 布局效果
Responsively Style Radio Buttons(源文件 row + 两个 col-xs-6 两个单选按钮一行对半排布
Responsively Style Checkboxes(本文) row + 三个 col-xs-4 三个复选框一行三等分排布
Line up Form Elements Responsively(源文件 row + col-xs-7(文本输入框)+ col-xs-5(提交按钮) 输入框与按钮同一行,按 7:5 分配宽度

三者共同遵守的铁律是每行列宽之和等于 12:6+6、4+4+4、7+5 都恰好凑满。若相加超过 12,超出的列会自动换行到下一行起始位置,这也是为什么“任意组合但必须整行可控”是使用栅格时的第一心法。看到这里你也能顺带理解,种子中标题(col-xs-8)与猫咪头像(col-xs-4)组合、三个操作按钮(三个 col-xs-4)组合,运用的都是同一套等宽/非等宽切分逻辑——表单控件并不特殊。

七、如何本地体验与验证

由于这是纯 HTML/CSS 浏览器挑战,体验成本极低:

  1. 在 freeCodeCamp 平台上打开该挑战,把上文 --solutions-- 的最终形态写进编辑器(或直接对照第四节的 <form> 片段替换种子代码中的对应区域);
  2. 点击运行,观察三个复选框是否与按钮区一样整齐地横排为一列(Loving、Lazy、Crazy 从左到右各占约 1/3 宽度);
  3. 拖动浏览器窗口或切换设备模拟器改变宽度,验证其在窄屏、宽屏下始终保持三列——这是 col-xs-* 无断点限制的直接体现;
  4. 触发“运行测试”,三条 hint 全部通过即完成挑战,可对比官方 solution 自查结构。

若想研究该挑战在仓库中的存放与校验机制,可以顺带查阅:模块结构配置(block 级依赖与题目顺序)、挑战 schema 校验器(frontmatter 与 tests 字段的合法性规则)、以及 挑战类型定义challengeType: 0 的语义映射)。

八、小结

"Responsively Style Checkboxes" 表面上是极简的一道改 HTML 结构题,背后却浓缩了三个可迁移的知识点:

  1. 栅格类是通用的布局原语,可以平等地作用于 div、按钮、图片乃至 input 等一切元素,无需写一行自定义 CSS;
  2. col-xs-* 是 Bootstrap 3 中无条件生效的断点类,用它排布的表单在任何屏幕宽度下都保持等宽分布,这正是它适合做“默认响应式”布局的原因;
  3. 结构的规范性由测试约束:自动判分既检查“内容是否正确落入容器”(DOM 选择器断言),也检查“标签是否闭合配对”(源码字符串断言),保证了视觉结果与结构质量同时达标。

把这一手法与前后几道题的 6/6、4/4/4、7/5 切分放在一起理解,你就掌握了用 12 栅格体系排布整个表单的最小完整范式,可以直接迁移到任何基于 Bootstrap 栅格的前端布局中。

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

项目优选

收起
kernelkernel
deepin linux kernel
C
33
18
ops-transformerops-transformer
本项目是CANN提供的transformer类大模型算子库,实现网络在NPU上加速计算。
C++
1.13 K
2.75 K
pytorchpytorch
作为 Ascend for PyTorch 社区的核心组件,TorchNPU 是昇腾专为 PyTorch 打造的深度学习适配插件,使 PyTorch 框架能够直接调用昇腾 NPU,为开发者提供昇腾 AI 处理器的超强算力。
Python
857
1.35 K
docsdocs
暂无描述
Markdown
897
5.8 K
kernelkernel
openEuler内核是openEuler操作系统的核心,既是系统性能与稳定性的基石,也是连接处理器、设备与服务的桥梁。
C
529
593
ops-nnops-nn
本项目是CANN提供的神经网络类计算算子库,实现网络在NPU上加速计算。
C++
916
1.83 K
jiuwenswarmjiuwenswarm
JiuwenSwarm 是一款基于openJiuwen开发的智能AI Agent,它能够将大语言模型的强大能力,通过你日常使用的各类通讯应用,直接延伸至你的指尖。
Python
3.58 K
1.01 K
ops-mathops-math
本项目是CANN提供的数学类基础计算算子库,实现网络在NPU上加速计算。
C++
1.35 K
1.46 K
cann-learning-hubcann-learning-hub
CANN 学习中心仓,支持在线互动运行、边学边练,提供教程、示例与优化方案,一站式助力昇腾开发者快速上手。
Jupyter Notebook
1.01 K
515
AscendNPU-IRAscendNPU-IR
AscendNPU-IR是基于MLIR(Multi-Level Intermediate Representation)构建的,面向昇腾亲和算子编译时使用的中间表示,提供昇腾完备表达能力,通过编译优化提升昇腾AI处理器计算效率,支持通过生态框架使能昇腾AI处理器与深度调优
C++
547
388