freeCodeCamp 栅格实战:用 Bootstrap 的 col-xs-6 让 Radio Buttons 自适应布局
2026/9/8 18:23:09 网站建设 项目流程

freeCodeCamp 栅格实战:用 Bootstrap 的 col-xs-6 让 Radio Buttons 自适应布局

【免费下载链接】freeCodeCampfreeCodeCamp.org's open-source codebase and curriculum. Learn math, programming, and computer science for free.项目地址: https://gitcode.com/GitHub_Trending/fr/freeCodeCamp

本指南围绕 freeCodeCamp 开源仓库中 Bootstrap 模块的经典关卡Responsively Style Radio Buttons展开,讲解如何把「12 列栅格系统」应用到表单元素上:将一对单选框分别放进col-xs-6列,使它们在任意屏幕宽度下都平分一整行、均匀分布。读完本文,你将掌握row+col-*-*栅格对表单控件的套用方式、freeCodeCamp 关卡文档(frontmatter + 描述 + 测试 + 种子代码 + 参考答案)的结构,以及通过仓库中的关卡源码、结构 JSON 与相邻关卡串起的前后知识链。

一、这一关要解决什么问题

关卡原文描述(对应 curriculum/challenges/english/blocks/bootstrap/bad87fee1348bd9aedb08845.md 第 13-19 行)写得很直白:

You can use Bootstrap'scol-xs-*classes onformelements, too! This way, our radio buttons will be evenly spread out across the page, regardless of how wide the screen resolution is.

也就是说,Bootstrap 栅格类不仅能用在图片、按钮上,同样适用于form表单里的元素。如果把一对单选按钮各自放进一列栅格,它们就能在任意屏幕宽度下都平分一行、保持均匀间距,而不是挤在表单左侧。

关卡任务拆解

  1. 把两个radio单选按钮一起嵌套进一个<div class="row">
  2. 再把每个单选按钮分别嵌套进一个<div class="col-xs-6">

关卡特意提示:单选按钮本质上是type="radio"input元素。这是后面理解测试断言、理解「栅格加在包裹层而非 input 本身」的关键。

二、为什么 col-xs-6 能做到「无论分辨率多宽都均匀分布」

freeCodeCamp 这一 Bootstrap 模块固定在 Bootstrap 3 之上。仓库的模块定义文件 curriculum/structure/blocks/bootstrap.json 第 5-8 行的required字段明确声明了版本:

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

即该模块所有关卡运行在 Bootstrap3.3.7的 CSS 之上。理解下面三点,就理解了本关的「为什么」:

  • 栅格一共 12 列col-xs-6表示占据 6 / 12 = 50% 的宽度。两个col-xs-6恰好填满一行 12 列。
  • col-xs-是"永远生效"的最小屏类*:Bootstrap 3 是移动优先设计,col-xs-*不依赖任何媒体查询断点,从最小屏到最大屏都按百分比宽度生效;而col-sm-*col-md-*col-lg-*分别要到更宽的视口才生效。因此这里选col-xs-6,才能做到原文档强调的 "regardless of how wide the screen resolution is"。
  • 栅格是"布局容器"而非"控件样式":栅格类改变的是元素的盒模型与浮动布局,不影响单选框本身的圆形小按钮外观。所以正确做法是给单选框外面的<div>加栅格类,而不是直接给<input>加——直接对input使用栅格类既不符合 Bootstrap 惯例,也无法被本关测试通过。

同模块更早的关卡 Use the Bootstrap Grid to Put Elements Side By Side 已经演示过 8 + 4、4 + 4 + 4 的切分;本关则是把同样的 12 列数学搬到form内部。

三、栅格类可以作用在任意元素上的通用结论

关卡文档用一个非常清晰的表达给出了可推广的结论:col-xs-* classes are applicable to all form elements(适用于所有表单元素)。这意味着同一种写法可以迁移到:

表单控件12 列切分说明
单选按钮(2 个)每个col-xs-66+6=12,两个选项各占半行
复选框(3 个)每个col-xs-44+4+4=12,三个选项横排一列
文本输入框任意 1-12 的列宽与后续 "Line up Form Elements" 关卡一致

紧跟在后的兄弟关卡Responsively Style Checkboxes(bad87fee1348bd9aeda08845.md)正是这一通用结论的下一站:把三个复选框各自放进col-xs-4,4+4+4=12 又一次完美凑满一行。两关连起来学,能彻底理解「栅格类 = 表单布局工具」。

四、仓库视角:关卡在课程结构中的位置

本关是 Bootstrap 模块中的一环。观察两处仓库结构文件可以还原它的"坐标":

  • 模块清单 curriculum/structure/blocks/bootstrap.json 第 10-135 行的challengeOrder数组按顺序列出整个 Bootstrap 模块的挑战,本关(id: bad87fee1348bd9aedb08845,标题 "Responsively Style Radio Buttons")排在第 15 位,紧接着的前后关卡分别是 "Add Font Awesome Icons to all of our Buttons" 和 "Responsively Style Checkboxes"。该文件还注明helpCategory: "HTML-CSS"blockLayout: "legacy-challenge-list",说明这是一个经典(legacy)HTML/CSS 动手练习模块。
  • 上层课程文件 curriculum/structure/superblocks/front-end-development-libraries.json 把bootstrap列在 Front End Development Libraries(前端开发库)超级块之下,与 jquery、sass、react、redux 等并列。

也就是说,这段讲解栅格的 markdown 不是孤立的教程片段,而是被challengeOrder精确编排、会被课程构建流水线消费的真实课程内容。挑战文件的 YAML frontmatter(第 1-11 行)提供了完整元数据:

字段本关取值含义
idbad87fee1348bd9aedb08845全局唯一挑战 ID,被bootstrap.jsonchallengeOrder引用
titleResponsively Style Radio Buttons挑战标题
challengeType0挑战类型编号
forumTopicId18270关联论坛话题
dashedNameresponsively-style-radio-buttonsURL 友好名称
requiredFont Awesome 4.2.0 CSS页面额外注入的依赖,raw: true表示以原始标签引入

这些字段与--description----hints----seed----solutions--区块共同构成 freeCodeCamp 挑战文件的固定格式,仓库中的 curriculum/schema 目录下提供了配套的 schema 校验器与快照测试来保证这类文件格式合规。

五、自动化测试:这一关是如何被"裁判"验证的

原文档的--hints--段(第 21-43 行)是三条会在浏览器里执行的自动化断言,理解它们等于理解了本关的全部验收标准。它们用 DOM 查询来检查你的 HTML 结构:

断言 1:所有 radio 必须在一个.row容器里

const radioButtons = document.querySelectorAll(`div.row input[type=radio]`); assert.lengthOf(radioButtons, 2);

div.row input[type=radio]选择所有「位于 class 为 row 的 div 内部、type 为 radio 的 input」,数量必须恰好为 2。这说明文档描述中"嵌套进 row"里的"嵌套",对测试而言等价于成为 row 的后代节点

断言 2:每个 radio 要各归其位,处在自己的.col-xs-6列里

const radioButtons = document.querySelectorAll(`div.col-xs-6 input[type=radio]`); assert.lengthOf(radioButtons, 2);

要求两个 radio 必须分别位于各自col-xs-6的 div 内(成为其内部元素),共 2 个命中。注意:把两个 radio 塞进同一个col-xs-6div 只有 1 次命中,同样无法通过。

断言 3:所有 div 标签必须成对闭合

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

这是对所有<div>的「数量守恒」检查:源码里<div</div>出现次数必须相等。新增一行栅格 = 新增 3 个开标签(外层row+ 内层两个col-xs-6),就必须补上同样数量的 3 个闭合标签,否则缩进/嵌套再漂亮也会被判失败。这也是新手最常见错误(漏闭合)被机器自动揪出的典型场景。

六、种子代码与参考解法:对照式学改造

关卡从一段可运行的 CatPhotoApp 页面开始(--seed--,第 45-107 行),页面已经包含响应式标题、三枚 Font Awesome 按钮、图片、爱好列表和一个form。种子代码中 radio 尚未套栅格,它们和复选框、文本输入框一样平铺在form里:

<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"> <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>

对照参考解法(--solutions--,第 109-175 行),本关要求的改动非常小、非常有针对性——只动 form 内部 radio 的部分:在<form>里、两个 radio 的<label>外层包一层div.row,再各自包一层div.col-xs-6。复选框、输入框、提交按钮保持原样(它们属于后续关卡的任务范围):

<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>

改写前后的结构差异可以用下面的层次图概括(仅展示 radio 区域):

改造前: 改造后: form form ├─ label ▸ input[radio] Indoor ├─ div.row └─ label ▸ input[radio] Outdoor │ ├─ div.col-xs-6 ▸ label ▸ input[radio] Indoor │ └─ div.col-xs-6 ▸ label ▸ input[radio] Outdoor └─ ...
  • 每个col-xs-6占 50% 宽,两块并列 = 一整行;因为宽度是百分比计算,无论设备是 320px 的旧手机还是 4K 显示器,两个选项都会各自占据一半宽度、从「上下堆叠的标签」变成视觉上「左右均匀分布的两列」。
  • label包裹input[radio]的既有结构保持不变——点击文字(Indoor / Outdoor)即可选中对应选项的可用性不被破坏,这正是栅格只负责"外部分布"、不干预"内部控件"的体现。
  • 同组的两个 radio 仍共享name="indoor-outdoor",属于同一单选组,一次只能选一个的语义由 name 属性保证,与栅格无关。

七、动手自测与常见错误排查

按原文档的三条 hints,可以在本地手动复现测试逻辑。把解法 HTML 保存到文件后在浏览器 DevTools Console 中执行下面任一查询来验证:

// 应返回 NodeList,长度为 2 document.querySelectorAll('div.row input[type=radio]'); // 应返回 NodeList,长度为 2 document.querySelectorAll('div.col-xs-6 input[type=radio]');

常见错误与对应提示快速对照:

错误写法后果命中的断言
两个 radio 直接平铺、不加 row/col第 1、2 条断言均为 0 命中断言 1、2 失败
只在外面加div.row、漏掉各自的div.col-xs-6radio 虽是 row 后代,但没有 col 命中断言 2 失败
两个 radio 放进同一个col-xs-6col-xs-6内 radio 数量命中只有 1断言 2 失败
新增 div 忘记写</div>开闭标签数量不相等断言 3 失败
col-xs-6直接加到<input>选择器div.col-xs-6 input无法命中断言 2 失败

八、从本关出发的后续学习路径

本关是「把 Bootstrap 栅格应用到表单」系列的开端。按照 curriculum/structure/blocks/bootstrap.json 中challengeOrder的顺序,紧接着的关卡与本文形成递进关系:

  1. Responsively Style Checkboxes(bad87fee1348bd9aeda08845.md):三个复选框分别放入col-xs-4,进一步印证 12 列拆分法则;
  2. Style Text Inputs as Form Controls:文本输入框套用表单控件样式;
  3. Line up Form Elements Responsively with Bootstrap:把单选、复选、文本输入等所有控件整体放进栅格排版。

可以看到 freeCodeCamp 把「一个可迁移的 CSS 概念」拆解成一组小步练习:先用按钮与图片学会row/col-xs-*,再在本关把它迁移到 radio,下一关迁移到 checkbox,最后做整表单布局——每一步只引入一个新变量。阅读关卡源码时,同时对照 curriculum/structure/blocks/bootstrap.json 的challengeOrder与各自文件中的--seed--/--solutions--,会发现每个解法都只在上一个解法的结果上做最小增量修改,这本身就是一份很好的"响应式表单布局最小改造"的示范序列。

九、小结

Responsively Style Radio Buttons这一关用一次极小的 HTML 结构调整,讲透了三个可长期复用的要点:

  1. Bootstrap 的col-xs-*栅格类可作用在包括表单控件在内的任意元素上,本关用它让两个单选按钮在任意分辨率下各占半行;
  2. 栅格负责外部布局、label 负责控件语义,二者各司其职,结构上通过div.row > div.col-xs-6 > label > input[radio]逐层嵌套表达;
  3. 在 freeCodeCamp 中,一切以--hints--里的 DOM 断言为准——理解document.querySelectorAll选择器如何验证层级,就掌握了这类关卡通过与否的根本判据。

你可以直接在仓库中打开 curriculum/challenges/english/blocks/bootstrap/bad87fee1348bd9aedb08845.md 阅读原文,结合 curriculum/structure/blocks/bootstrap.json 了解它在整个 Bootstrap 模块中的编排位置,然后动手把 radio、checkbox、文本输入组合成一张真正响应式的表单页。

【免费下载链接】freeCodeCampfreeCodeCamp.org's open-source codebase and curriculum. Learn math, programming, and computer science for free.项目地址: https://gitcode.com/GitHub_Trending/fr/freeCodeCamp

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询