首页
/ freeCodeCamp "Delete HTML Elements" 挑战拆解:从删除 `<h1>` 到课程自动判题机制

freeCodeCamp "Delete HTML Elements" 挑战拆解:从删除 `<h1>` 到课程自动判题机制

2026-09-06 18:59:11作者:段琳惟

freeCodeCamp 的 "Basic HTML and HTML5" 课程通过逐个小练习引导学习者搭建 CatPhotoApp(猫咪照片应用),其中 Delete HTML Elements 挑战 教给学习者的核心技能是:从 HTML 文档中准确删除一个元素,同时保证其余元素不被破坏。本文以该挑战文件为骨架,结合本仓库(freeCodeCamp 开源课程库)的源码与结构文件,逐行拆解任务、起始代码、自动判题规则以及挑战文档的前置元数据格式,帮助读者既掌握 HTML 编辑基本功,也理解 freeCodeCamp 课程体系 "一段 Markdown 即一道题" 的实现方式。

挑战背景:为什么要"删掉一个元素"?

这道题的场景设定非常朴素:在构建 CatPhotoApp 的界面上,竖向空间(vertical space)有限,需要移除暂时用不上的元素,为后续课程逐步添加图片、链接、表单等内容腾出干净的区域。

"删除元素"看似是一个"反向操作",但它与"添加元素"同等重要:

  • 前端页面最终呈现给用户的内容由当前代码中实际存在的元素决定,多余的标签会直接占用布局与视觉空间;
  • 只有学会精确删除(而不是连标签带内容一起误删),才能保证页面上其他元素继续正常渲染;
  • 删除这一动作天然要求学习者理解"标签成对出现、内容夹在开标签与闭标签之间"这一 HTML 基本结构。

在课程叙事里,这道挑战处于 Basic HTML and HTML5 的早期阶段。根据 basic-html-and-html5.json 结构文件,Delete HTML Elements(id 为 bad87fed1348bd9aedf08833)排在 "Comment out HTML" 之后、位于整个 block 的第 7 个挑战,前后衔接的是注释、HTML5 元素、图片与链接等系列练习,整体难度定位为纯新手入门。

任务指令与起始代码

挑战的 instructions 只有一句话:

Delete your h1 element so we can simplify our view.

翻译过来即:删除页面上的 <h1> 元素,让视图更简洁。注意任务要求的是删除整个 h1 元素(包括开标签、闭标签与其文本内容),而不是删除标题中的文字或把内容清空。

题目附带的种子代码(seed)如下,学习者将基于它开始编辑:

<h1>Hello World</h1>

<h2>CatPhotoApp</h2>

<p>Kitty ipsum dolor sit amet, shed everywhere shed everywhere stretching attack your ankles chase the red dot, hairball run catnip eat the grass sniff.</p>

起始页面中同时存在三个元素:

元素 内容 在本挑战中的归宿
<h1>Hello World</h1> 一级标题 删除
<h2>CatPhotoApp</h2> 二级标题(应用名) 保留
<p>Kitty ipsum …</p> 段落文字(占位文本) 保留

之所以保留 h2 而不是 h1,是因为 CatPhotoApp 作为一个应用页面,其主标题使用 h2 展示即可,页面层级更符合后续课程逐步填充的真实内容形态。

参考解法:删除后应得到什么结果?

挑战自带的 --solutions-- 段落给出了标准答案。删除 h1 元素后,代码应为:

<h2>CatPhotoApp</h2><p>Kitty ipsum dolor sit amet, shed everywhere shed everywhere stretching attack your ankles chase the red dot, hairball run catnip eat the grass sniff.</p>

也就是说,只需要把种子代码里的 <h1>Hello World</h1> 连同它前后的空行一并移除,剩余的 h2p 元素保持原样即可通过。HTML 对空白(空行、空格)并不敏感,因此即使两行合并书写,渲染结果也一致。

自动判题规则:三个断言如何检查你的代码?

freeCodeCamp 的挑战会在学习者点击提交后,在浏览器中用一段 --hints-- 里定义的 JavaScript 正则断言逐条验证代码。这道题共包含三条 hints,对应仓库 schema 中"每条挑战必须声明 tests(含 text 与 testString)"的要求(见 challenge-schema.js)。

第一条断言检查 h1 是否被删除:

assert.notMatch(code,/<h1>/gi);
assert.notMatch(code,/<\/h1>/gi);

assert.notMatch 表示"源代码中不得匹配到该正则"。两个正则分别针对开标签 <h1> 与闭标签 </h1>(注意闭标签中的反斜杠转义),g 为全局匹配、i 为忽略大小写。也就是说,只要代码里还残留任何一个 h1 标签(无论写成 <H1> 还是 <h1>),这一条就会失败。

第二条断言检查 h2 仍然存在:

assert.match(code,/<h2>[\w\W]*<\/h2>/gi);

[\w\W] 是一个常见技巧:\w 匹配单词字符,\W 匹配非单词字符,两者取并集等价于"匹配任意字符(包括换行)",因此 [\w\W]* 表示"从开标签到闭标签之间的任意内容,允许为空或跨越多行"。这条规则要求页面中仍有一对完整的 h2 标签。

第三条断言以相同方式检查 p 元素仍然在页面上:

assert.match(code,/<p>[\w\W]*<\/p>/gi);

从这三条规则可以提炼出这道题唯一的通过条件:

  1. 代码中不存在任何 <h1> / </h1>
  2. 代码中存在一对完整的 <h2>...</h2>
  3. 代码中存在一对完整的 <p>...</p>

值得注意的是,正则断言只检查源码文本,并不真正渲染页面;这意味着只要标签配对完整即可,标签间的文本写什么并不影响判题结果。这也是初学者可以刻意做的"边界验证":把 h2 内容改成任意英文单词,挑战依旧会通过。

挑战文件的完整元数据:一段 Markdown 如何成为一道题?

仓库中每个挑战都是一个独立的 Markdown 文件,文件名即挑战的全局唯一 id。本挑战对应文件为 bad87fed1348bd9aedf08833.md,位于 curriculum/challenges/english/blocks/basic-html-and-html5/ 目录下。文件开头以 YAML frontmatter 声明元数据:

---
id: bad87fed1348bd9aedf08833
title: Delete HTML Elements
challengeType: 0
videoUrl: 'https://scrimba.com/p/pVMPUv/ckK73C9'
forumTopicId: 17559
dashedName: delete-html-elements
---

结合仓库的 challenge-schema.js 可以逐项理解其约束与含义:

  • id:挑战全局唯一标识,schema 要求符合 MongoDB ObjectId 格式(Joi.objectId().required()),它同时决定 Markdown 文件名与课程结构文件中的引用关系;
  • title:挑战的展示标题,必填(Joi.string().required());
  • challengeType:挑战类型编号,schema 规定取值范围为 0–33(Joi.number().min(0).max(33).required())。这里取 0,属于 freeCodeCamp 沿用自早期课程的传统代码练习类型,该类型常量在 packages/shared/src/config/challenge-types.ts 中统一维护;
  • videoUrl:可选的讲解视频地址,schema 中允许为空字符串(Joi.string().allow('')),该字段仅在题目提供配套视频讲解时填充;
  • forumTopicId:关联到社区论坛讨论帖的编号,类型为数值(Joi.number());
  • dashedName:小写、用连字符连接的语义化名称(delete-html-elements),schema 以 slugRE 正则校验,主要用于路由与检索。

frontmatter 之后,挑战内容按 # --description--# --instructions--# --hints--# --seed--# --solutions-- 等带 -- 包裹的分节标记划分,课程构建工具据此解析出描述、指令、判题规则、种子代码与标准解法。description 在 schema 中被规定为必填字符串(challenge-schema.js),而 solutions 则要求为至少含一个文件的数组(challenge-schema.js)。此外该 block 还通过 basic-html-and-html5.json 中的 challengeOrder 数组维护挑战顺序,blockLayout 被标记为 legacy-challenge-list,表明这是沿用早期列表式布局的传统课程块。

排错自查:提交总失败时该检查什么?

如果挑战无法通过,90% 的情况源于以下三种:

  1. 误删了 h2p:题目只要求删除 h1,任何多余删除都会触发对应断言失败。恢复方式是把起始代码中需要的行重新粘贴回来。
  2. h1 标签仍残留一半:例如只删了 <h1>Hello World</h1> 却留下空标签,或误删了某个标签的一部分。由于 notMatch 分别匹配开标签与闭标签,残留任意一半都会失败,需整段清除。
  3. 复制粘贴引入多余的 h1:例如在编辑器中保留了旧版本又粘贴了新版本,代码中出现两份内容时,需留意是否遗留了第二处 h1

一个实用的自查方法:对照上文三条断言手工扫描代码——先确认没有 h1 相关文本,再确认 h2p 各有一对完整的开闭标签。

延伸思考:删号是"减法",也是后续构建的基础

这道题虽然只涉及删除一个元素,却为后续课程埋下了两处关键伏笔:其一,同一 block 后续的 "Introduction to HTML5 Elements" 将引入 mainheaderfooter 等语义化标签,学习者在动手"删减"时已熟悉"标签成对存在、内容位于其间"的语法直觉;其二,该练习所在的 CatPhotoApp 叙事会一路延续到图片(Add Images to Your Website)、链接(Link to External Pages with Anchor Elements)、列表与表单等十余道挑战,最终组合成完整的猫咪照片应用页面。

因此,把这道挑战当作"纯删除练习"并不完整——它真正训练的是学习者在既有代码中精确定位目标、最小化修改、并通过自动断言验证结果的能力。理解这一点,也就理解了 freeCodeCamp 课程"以微小的可验证步骤渐进构建完整项目"的教学设计:每一步的成败都通过仓库 schema 中强制定义的 tests 数组即时反馈,让新手在第一次接触 HTML 时就能建立"写完即校验、校验即闭环"的工程习惯。若想继续深挖,可对照阅读本仓库中同目录下的其余挑战文件(如 "Comment out HTML" 与 "Uncomment HTML"),对比它们如何通过注释语法执行类似的"增删"训练。

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