freeCodeCamp 挑战精讲:用<ol>与<li>创建 HTML 有序列表(Create an Ordered List)
【免费下载链接】freeCodeCampfreeCodeCamp.org's open-source codebase and curriculum. Learn math, programming, and computer science for free.项目地址: https://gitcode.com/GitHub_Trending/fr/freeCodeCamp
本文以 freeCodeCamp 开源课程仓库中的真实挑战 Create an Ordered List 为主体,拆解其教学意图、参考代码、通关步骤与 8 条自动化验收测试的实现逻辑。读者可以借此完整掌握 HTML 有序列表(numbered list)的语法、它与无序列表的差异,同时理解 freeCodeCamp 挑战文件的编写结构(frontmatter、seed、hints 与 solutions),学会如何阅读与解读课程源码中的每一道测试断言。
一、这个挑战在 freeCodeCamp 课程体系中的位置
Create an Ordered List的挑战文件位于curriculum/challenges/english/blocks/basic-html-and-html5/目录,属于经典的「基础 HTML 与 HTML5」课程块(block)。
从课程结构数据可以确认它在体系中的归属链路:
- 挑战 ID
bad87fee1348bd9aedf08828被登记在课程块清单 curriculum/structure/blocks/basic-html-and-html5.json 的challengeOrder中,紧跟在上一题Create a Bulleted Unordered List(IDbad87fee1348bd9aedf08827)之后——这正是本挑战把「无序列表ul」扩展为「有序列表ol」的教学衔接点; - 该 block 又隶属于responsive-web-design这个 superblock(见 curriculum/structure/superblocks/responsive-web-design.json);
- 而
responsive-web-design位于 curriculum/structure/curriculum.json 所列出的超课程块首位,是经典「响应式 Web 设计认证」路径的第一站。
因此,本挑战是很多学习者接触 HTML 列表语义的第一步,也是最典型的「补全式」交互挑战(challengeType 为 0,即html,对应经典编辑器视图,通过测试后提交——见 packages/shared/src/config/challenge-types.ts 中的html、viewTypes与submitTypes定义)。
二、核心知识点:有序列表<ol>的语法
原文档给出的定义非常精炼,是理解列表语义的起点:
- HTML 提供了一种用于创建有序列表(ordered list,即编号列表)的专门元素;
- 有序列表以开标签
<ol>开始,内部可包含任意数量的列表项<li>(list item),最后以闭合标签</ol>结束。
文档中的经典示例:
<ol> <li>Garfield</li> <li>Sylvester</li> </ol>这段代码会被浏览器渲染成Garfield与Sylvester两条自动编号的列表项,默认显示为「1.」「2.」。之所以叫「有序」,是因为浏览器会自动为每个<li>维护一个递增序号,顺序本身就是语义信息的一部分(比如排行榜、步骤说明、TOP N 排名)。
与之相对,上一挑战介绍的<ul>(无序列表)则用项目符号(bullet)呈现,列表项之间没有先后次序含义。二者的元素嵌套规则一致:列表项必须直接位于列表容器(<ol>或<ul>)之内,且单个列表项可以包含文本、图片、链接乃至另一个嵌套列表等丰富内容。
三、原题目标与初始代码
题目要求(instructions)只有一句话:
Create an ordered list of the top 3 things cats hate the most.(为「猫最讨厌的三样东西」创建一个有序列表。)
在 CatPhotoApp 小应用页面中,已经存在一个表示「Things cats love:」(猫喜欢的东西)的<ul>无序列表;学习者需要紧接着在「Top 3 things cats hate:」标题下方,补上一个<ol>有序列表,列出 3 项内容。
挑战的初始代码(seed)如下,其中题目已经给出了h2、main、a链接、图片和<ul>列表,唯独「Top 3 things cats hate:」之后是空的:
<h2>CatPhotoApp</h2> <main> <p>Click here to view more <a href="#">cat photos</a>.</p> <a href="#"><img src="https://cdn.freecodecamp.org/curriculum/cat-photo-app/relaxing-cat.jpg" alt="A cute orange cat lying on its back."></a> <p>Things cats love:</p> <ul> <li>catnip</li> <li>laser pointers</li> <li>lasagna</li> </ul> <p>Top 3 things cats hate:</p> </main>注意观察结构:无序列表ul下有 3 个非空的<li>;<p>Things cats love:</p>是无序列表的前一个兄弟元素,而<p>Top 3 things cats hate:</p>之后到</main>之间就是新<ol>的落点。
四、通关步骤与标准解法
解题思路分为三步:
- 在
<p>Top 3 things cats hate:</p>这一行之后、</main>之前另起一行; - 写入开标签
<ol>,并在其内部放置 3 个非空、各自闭合的<li>列表项(内容可自由发挥,比如洗澡、打针、剪指甲等); - 写入闭合标签
</ol>,保证标签配对完整。
freeCodeCamp 挑战文件会在# --solutions--区块中附带官方参考答案。本挑战的标准解法将占位内容替换为任意三项即可,结构如下:
<h2>CatPhotoApp</h2> <main> <p>Click here to view more <a href="#">cat photos</a>.</p> <a href="#"><img src="https://cdn.freecodecamp.org/curriculum/cat-photo-app/relaxing-cat.jpg" alt="A cute orange cat lying on its back."></a> <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>hate 1</li> <li>hate 2</li> <li>hate 3</li> </ol> </main>五、逐条拆解--hints--自动化测试断言
本挑战的验收不依赖人工检查,而是由浏览器内运行的测试脚本对 DOM 与源代码做断言。把这些断言读懂,就等于理解了「什么才算是合格答案」。
1. 有序列表必须紧跟指定标题
const previousElement = document.querySelector('ol').previousElementSibling; assert.match(previousElement.textContent, /Top 3 things cats hate:/i);逻辑:页面里必须存在ol,取其「前一个兄弟元素」,且其文本需匹配/Top 3 things cats hate:/i(i忽略大小写)。也就是说,新列表必须放在正确的位置,不能随意挪到页面其他地方。
2. 无序列表必须紧跟Things cats love:标题
const previousElement = document.querySelector('ul').previousElementSibling; assert.match(previousElement.textContent, /Things cats love:/i);防止学习者误改原有<ul>的位置或删除标题段落。
3. 页面只能有一个ul与一个ol
assert.lengthOf(document.querySelectorAll('ul'), 1); assert.lengthOf(document.querySelectorAll('ol'), 1);querySelectorAll返回所有匹配元素的集合,lengthOf断言其数量必须恰好为 1。这要求学习者在已有结构上补全,而不是另起炉灶或重复添加。
4.ul与ol内各需恰好 3 个li
assert.lengthOf(document.querySelectorAll('ul li'), 3); assert.lengthOf(document.querySelectorAll('ol li'), 3);注意ul li是后代选择器:它统计所有嵌套在ul内部的li。数量必须精确为 3,说明「TOP 3」必须名副其实。
5. 标签必须成对闭合
对ul、ol、li三类元素,测试都以「正反统计匹配次数」的方式校验闭合标签:
assert.match(code, /<\/ul>/g); assert.strictEqual(code.match(/<\/ul>/g).length, code.match(/<ul>/g).length);code是学习者提交的源码字符串。/<\/ul>/g全局统计闭合标签出现次数,/<ul>/g统计开标签次数,二者必须严格相等——防止出现漏写</ul>这类「浏览器能容忍、但语义错误」的写法。ol与li采用同样的成对校验:
assert.match(code, /<\/ol>/g); assert.strictEqual(code.match(/<\/ol>/g).length, code.match(/<ol>/g).length); assert.match(code, /<\/li>/g); assert.match(code, /<li>/g); assert.strictEqual(code.match(/<\/li>/g).length, code.match(/<li>/g).length);6. 列表项内容不能为空
[...document.querySelectorAll('ul li')].forEach((val) => assert.isNotEmpty(__helpers.removeWhiteSpace(val.textContent)) );__helpers.removeWhiteSpace是挑战测试运行环境注入的辅助函数,从用法可以推断其作用是剔除文本中的空白字符(空格、换行、缩进),再做isNotEmpty非空断言——即val.textContent在去除纯空白后仍有真实内容。也就是说,<li> </li>这类「空壳列表项」是不能过关的,ol内的 3 个li同样适用这条规则。
六、从文件结构理解 freeCodeCamp 挑战的编写规范
细看这个.md文件本身,它其实是一种带结构化分区的「可执行课程文档」,并非普通说明文章。头部是 YAML frontmatter:
| 字段 | 取值(本挑战) | 含义 |
|---|---|---|
id | bad87fee1348bd9aedf08828 | 全局唯一挑战 ID,也是 block 清单中challengeOrder登记的 ID |
title | Create an Ordered List | 挑战名称 |
challengeType | 0 | 挑战类型,0对应 HTML 型挑战(见 challenge-types.ts),采用经典编辑器视图并以运行测试的方式提交 |
videoUrl | Scrimba 视频地址 | 关联的讲解视频 |
forumTopicId | 16824 | 对应的论坛讨论主题 |
dashedName | create-an-ordered-list | 供 URL 使用的连字符化名称,需匹配 slug 规则 |
正文则按固定顺序使用# --description--、# --instructions--、# --hints--、# --seed--(内含## --seed-contents--)与# --solutions--分区,分别承载「知识讲解」「任务要求」「测试断言」「初始代码」与「参考答案」。
这套格式并非约定俗成,而是由仓库内的工程化工具强制校验:比如 curriculum/schema/challenge-schema.js 就要求challengeType必须是 0–33 之间的数字、dashedName必须通过slugRE正则校验;对应的 curriculum/schema/challenge-schema.test.mjs 会逐文件跑校验快照测试。挑战构建与读取逻辑位于 curriculum/src/get-challenges.ts,前端侧则由tools/client-plugins/gatsby-source-challenges负责把这类.md吸入页面数据层。
对于希望为开源课程贡献新题目或复习旧题的读者而言,直接对照本挑战文件与上述 schema 源码,是理解「一道合格挑战长什么样」的最短路径。
七、小结
通过Create an Ordered List这道题,可以一次掌握三重能力:
- HTML 语义层面:会用
<ol>+<li>表达「有顺序的编号列表」,并知道它与<ul>在语义与默认渲染上的区别; - 代码规范层面:所有开标签必须成对闭合、列表项内容不能为空、每类列表只需出现一次,这些正是 freeCodeCamp 测试体系对新手 HTML 的硬性要求;
- 工程阅读层面:理解了 challengeType、frontmatter、seed 与 hints 的组织方式,具备阅读课程源码与自行验证题目逻辑的基础。
如果你能不看答案、独立让页面通过全部 8 条断言,那么这道经典的基础 HTML 挑战就算真正拿下了。
【免费下载链接】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),仅供参考