首页
/ freeCodeCamp HTML 入门课程:深入理解 `<!DOCTYPE html>` 文档声明挑战

freeCodeCamp HTML 入门课程:深入理解 `<!DOCTYPE html>` 文档声明挑战

2026-09-06 18:56:27作者:田桥桑Industrious

本篇技术指南围绕 freeCodeCamp 课程中 “Declare the Doctype of an HTML Document” 这一交互式编程挑战展开,结合开源仓库中该挑战的源 Markdown 文件 587d78aa367417b2b2512aed.md 及对应的挑战构建与测试基础设施,系统讲解 <!DOCTYPE html> 声明的语法、位置与大小写规则,以及它与 <html><head><body> 共同构成的 HTML 文档骨架。读完本文,你将掌握标准 HTML5 文档最小结构的写法,并理解 freeCodeCamp 是如何用自动化测试来判定这类挑战“通过”的。

一、挑战定位:Basic HTML 与 HTML5 模块中的承上启下关键一环

在 freeCodeCamp 课程体系中,这一挑战位于 basic-html-and-html5(基础 HTML 与 HTML5)模块。模块顺序由 basic-html-and-html5.json 定义,在 28 道题中,本文讨论的 “Declare the Doctype of an HTML Document” 排在倒数第二(index 27),紧随其后的是 “Define the Head and Body of an HTML Document”(index 28)。

这个位置很有深意:此前约 27 道挑战已经带学习者掌握了各类具体 HTML 元素——从 h1h2 标题,到段落 p、链接 a、图片 img、表单 form、列表、单选按钮与复选框等。但正如此前教程在文档中明确指出的,仅仅会使用单个元素是不够的,有些元素负责为整张页面提供总体结构,且应当出现在每一个 HTML 文档中。而 doctype 声明,就是这些"必须出现在每个文档中"的结构性元素的起点。

二、为什么需要 doctype:告诉浏览器"你读的是哪一版 HTML"

2.1 浏览器如何决定渲染模式

DOCTYPE(document type,文档类型)声明必须位于 HTML 文档的第一行,它的作用是告诉浏览器:这份页面使用的是哪个版本的 HTML 规范。

HTML 是一门持续演进的语言,会被定期更新。如今主流浏览器大多支持最新的规范——HTML5;但互联网上仍然存在使用早期版本编写的老旧网页。面对混用不同版本规范的网页,浏览器无法靠"猜"来准确解析,因此文档作者必须显式声明。

在 web 历史上,这个机制对应着著名的**标准模式与怪异模式(standards mode vs. quirks mode)**切换:现代规范的文档若缺少 doctype,老式浏览器可能回退到 quirks 模式,用不符合标准的方式渲染页面,导致布局错乱。

2.2 HTML5 的标准写法

在文档第一行写入 doctype,其中 ... 部分替换为对应的 HTML 版本标识:

<!DOCTYPE ...>

针对 HTML5,你需要写的是:

<!DOCTYPE html>

HTML5 的 doctype 是历史版本中最简短的一个。为了便于对比,文档中虽未列出,但可以从标准角度了解:旧规范(如 HTML 4.01 的 transitional / strict 变体)的 doctype 声明通常是一长串带系统标识符的字符串,而 HTML5 仅用 <!DOCTYPE html> 五个词即完成声明。

三、语法细节:!DOCTYPE 的大小写规则

挑战的 --description-- 段落专门强调了两条易被初学者忽略的规则,理解它们可以避免未来写出浏览器解析异常的代码:

语法片段 大小写敏感性 说明
! 必须存在 位于 < 之后,是 doctype 标识的一部分,省略即非法
DOCTYPE 推荐大写 尤其在老式浏览器中,!大写 DOCTYPE 很重要
html 不区分大小写 浏览器解析时对 html 部分的大小写不敏感

概括来说:HTML5 doctype 声明中只有 html 部分是大小写不敏感的!DOCTYPE 应当原样照写。初学者常见的 <!doctype html>DOCTYPE 全小写)在规范层面通常也能被现代浏览器接受,但遵循课程要求、写成教科书式的 <!DOCTYPE html>,是最稳妥、兼容性最好的习惯。这一点在后续测试环节还会得到印证——课程的测试正则显式带有 gi 大小写不敏感标志,意味着测试编写者已考虑到用户可能输入不同大小写形式。

四、<html> 根元素:包裹其余所有代码

声明完 doctype 之后,文档的其余 HTML 代码必须被包裹在 html 标签中——它是整个文档的根元素:

  • 开标签 <html> 紧跟 <!DOCTYPE html> 那一行的正下方;
  • 闭标签 </html> 位于页面最末尾。

课程给出的最小页面骨架示例为:

<!DOCTYPE html>
<html>

</html>

你的 HTML 内容就写在两个 html 标签之间的空位里。值得注意的是,<!DOCTYPE html> 本身不是 HTML 元素,它只是一条给浏览器阅读的声明指令;真正的页面元素树由 <html> 作为根节点开始。正因如此,后续的 "Define the Head and Body of an HTML Document" 挑战中,headbody 必须作为 <html>直接子元素出现(该挑战测试用正则提取 <html>...</html> 内容后校验 head/body 是否存在),形成标准的四层嵌套:html > head/body

一个仅含标题和正文段落的完整 HTML5 骨架可以这样组织:

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title>Example title</title>
  </head>
  <body>
    <h1>A heading</h1>
    <p>Some paragraph content.</p>
  </body>
</html>

五、实践任务拆解:动手把骨架搭起来

挑战的 --instructions-- 给出的任务非常具体,共分三步:

  1. 在空白文档顶部添加 HTML5 的 DOCTYPE 标签——即写上第一行 <!DOCTYPE html>
  2. 在其下方添加成对的 <html> 开标签与 </html> 闭标签,让它们包裹住全部内容;
  3. html 标签之间放入一个 h1 元素,标题文本内容不限。

编辑器的初始代码是一个完全空白的 HTML 文档(--seed-contents-- 为空):


也就是说,学习者需要从零写出整个 HTML 骨架,这比"在既有代码上补一句"更能训练对文档结构的整体记忆。

六、标准答案与验证测试:代码如何被判"通过"

6.1 挑战中的标准解答

挑战源文件中的 --solutions-- 给出了参考答案:

<!DOCTYPE html>
<html>
  <h1> Hello world </h1>
</html>

(出于课程循序渐进考虑,此阶段尚未引入 head/bodyh1 直接作为 <html> 的子元素书写是可接受的——下一道挑战会专门练习将它们收进 headbody 中。)

6.2 三道自动化测试各自在验证什么

--hints-- 段定义了三条检验标准,每条都对应一段可执行的 JS 断言(assert),它们正好覆盖了文档强调的每个知识点:

测试一:必须出现 <!DOCTYPE html> 标签

assert.match(code,/<!DOCTYPE\s+?html\s*?>/gi);

这段断言对用户的源码做正则匹配。\s+?\s*? 宽容了 doctype 声明中可能存在的空白,末尾的 gi 标志表示全局匹配且忽略大小写,因此 <!doctype HTML> 之类写法也能通过——这与文档中"html 部分不区分大小写"的说法一致。

测试二:页面中应当只有一个 html 元素

assert.lengthOf(document.querySelectorAll('html'), 1);

此断言通过浏览器 DOM API querySelectorAll('html') 统计根元素的实例数,恰好为 1 才算通过。它强制学习者成对、且仅有一对 html 标签,既不能遗漏闭标签,也不能重复嵌套。

测试三:html 标签应包裹住一个 h1 元素

assert.match(code,/<html>\s*?<h1>\s*?.*?\s*?<\/h1>\s*?<\/html>/gi);

正则按顺序匹配 <html><h1> → 任意文本 → </h1></html>,验证了结构约束:h1 必须位于 html 标签内部且被正确闭合。

6.3 测试在课程仓库中如何被执行

了解这段断言背后的运行机制,有助于理解 freeCodeCamp 课程内容与测试的真实关系。在仓库的测试基础设施 test-challenges.js 中,每一道挑战都要经历两类自动化验证:

  1. 初始代码必须让测试失败Test suite must fail on the initial contents):即一个空白的 --seed-- 绝不应当误判为已通过;
  2. 给出的标准答案必须让测试通过Check tests against solutions):仓库会针对 --solutions-- 中的解答创建测试运行器(createTestRunner),把解答内容注入页面后逐条执行 hints 中的 testString

测试的执行依赖于真实的浏览器页面环境(测试框架通过 Puppeteer 打开一个页面对 window.FCCTestRunner 发出求值调用),因此 querySelectorAll('html') 这类 DOM 断言能拿到真实的文档结构。此外,所有挑战在入库前还会经过 Joi 模式校验,规则定义在 challenge-schema.js 中——比如 titledashedNametests(内含 testString)、solutions 都是必填字段,而 challengeType: 0 对应的正是 challenge-types.ts 中定义的 html 类型(其提交方式为 tests,即以通过测试为准)。

七、源码纵深:doctype 在挑战预览页中的特殊处理

一个值得深挖的细节是:在 freeCodeCamp 的挑战运行环境中,浏览器出于安全考虑会把用户代码放在受限的 iframe 中执行。对于 HTML 类型挑战,构建系统会重建一个完整的文档上下文。在构建源码 build.ts 中可以看到工具函数 prefixDoctype

const doctype = sources.contents?.match(/^<!DOCTYPE html>/i)?.[0] || '';
return doctype + build;

它的作用正是:从学习者源码的开头提取出 <!DOCTYPE html> 声明(同样使用 i 忽略大小写、并兼容 doctype 前后可能存在的差异),再把它拼接到构建好的文档前面,确保被测页面严格以 doctype 开头。也就是说,即使编辑器呈现给用户的是片段式代码,测试与预览环节也会尽可能还原"以 doctype 开头 + 完整 html 骨架"的真实页面形态——这也是为什么课程要求用户把 doctype 与 html 结构写完整。

八、小结与下一步

通过这道挑战,你实际掌握的是编写任何 HTML 页面时都必须先做对的三件事

  1. 在第一行写出 <!DOCTYPE html>,向浏览器声明文档遵循 HTML5 规范;
  2. 用一对 <html> 根标签包裹全部内容,让页面拥有一棵合法的元素树;
  3. 记住语法雷区:! 不可缺少、DOCTYPE 保持大写、只有 html 大小写不敏感。

本挑战与紧随其后的 "Define the Head and Body of an HTML Document"(见同目录下 587d78aa367417b2b2512aec.md)一起,构成了 freeCodeCamp 对"HTML 文档总纲"的完整训练:先学会声明文档类型与建立根结构,再学会把元信息放进 head、把可视化内容放进 body。掌握这个总纲后,后续课程中学习的 linkmetastyle 等元素就有了正确的归属位置。

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

项目优选

收起
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++
915
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