HTML校验

纯本地静态检查:标签配对栈、重复 id、img 缺 alt、a 缺 href、void 误闭合,逐条报到行号,全程不发网络请求。

演示代码检出 9 个问题(Ctrl/Cmd+Enter 或点「开始校验」重跑):

  • 第 11 行 </div> 与第 11 行 <span> 交叉嵌套
  • 第 12 行 </b> 与第 12 行 <i> 交叉嵌套
  • 第 12 行 </i> 没有对应的开始标签
  • 第 13 行 img 缺 alt
  • 第 14 行 a 缺 href
  • 第 16 行 id="main" 重复
  • 第 17 行 br 是 void 元素,不应有闭合标签
  • 第 18 行 </ul> 与第 18 行 <li> 交叉嵌套
  • 第 19 行 </div> 与第 10 行 <p> 交叉嵌套

使用方法

  1. 把要检查的 HTML 片段粘进输入框(示例已预置 21 行演示代码,含 9 处典型问题),按 Ctrl+Enter 或点「开始校验」,HTML校验会把每个问题报到行号。
  2. 结果区逐条列出问题与行号:未闭合、交叉嵌套、重复 id、img 缺 alt、a 缺 href、void 元素误闭合各算一类。
  3. 按行号回源码修一处、重跑一次,直到问题列表清空。
  4. 点「载入示例」随时还原演示代码;点「复制问题列表」把清单转给同事。
  5. 输入超过 300000 字符会被拒绝并提示分段,空输入会提示不能为空,均不静默给结果。

HTML校验能解决什么

浏览器太「宽容」是这类工具存在的理由:HTML5 规范给解析器内置了一整套纠错规则,写错标签页面照样渲染,于是错误被静默吞掉、问题积累到布局错乱或脚本行为异常才暴露。本页解决的就是「把被浏览器修复的错误找出来」:模板片段闭合没写全、循环输出的 <li> 忘了收尾、两个组件都用了 id="main" 导致 getElementById 拿错节点、图片没写 alt 影响无障碍与 SEO、复制粘贴带来多余的 </div>——这些都能逐条报到行号。安全边界要说在前头:本页只做本地静态检查,输入的代码只在浏览器内存里扫描,不加载其中的任何外部资源、不执行任何脚本、不发一个网络请求,也不接入 W3C 在线校验服务,需要 DTD 级别的正式报告请到 W3C Markup Validation Service 官网。排版美化与本页分工:格式化交给站内 HTML格式化页,压缩交给 HTML压缩页,本页只读不写、不输出改写后的代码。

原理与变量:为什么校验器要自实现标签栈

第一直觉是用 new DOMParser().parseFromString(html, "text/html") 让浏览器解析——但这恰恰是行不通的:HTML5 容错解析几乎不失败,哪怕标签烂成一团,DOMParser 也照样产出一棵「被修复过」的 DOM 树,只有极少数命名空间级别的严重错误才会让文档根出现 <parsererror>,所以「解析不报错」等于什么都没检查。本页的做法是自实现标签配对栈:用正则逐个扫出开始与结束标签(先剥掉 script、style 内容与注释),维护一个栈——遇到开始标签入栈,遇到结束标签时栈顶必须同名;栈顶不是自己但在栈里能找到同名,说明 <b><i></b></i> 这种交叉嵌套;栈里找不到,说明是多余的闭合标签;扫完全部输入栈里还有剩,就是未闭合。三类豁免是必需的,否则误报成灾:一是 14 个 void 元素(br、img、input、meta 等)按 HTML 规范没有结束标签,出现 </br> 反而要报错;二是 p、li、tr、td、option 这些隐式闭合元素,后一个同名开始标签按规范自动关掉前一个,只记不规范不记错误;三是属性解析要处理单双引号与无引号三种写法,避免 > 出现在引号里被误切。重复 id、img 缺 alt、a 缺 href 这三个属性级检查在扫到对应开始标签时同步完成,行号由标签在原文中的换行计数得出——全程不构造 DOM 树,这也是它对畸形输入比 DOMParser 更稳的原因。

完整示例(数值仅作演示)

预置的 21 行演示代码按行号检出 9 个问题:第 11 行 span 没闭合导致 </div> 与它交叉;第 12 行 b 与 i 交叉嵌套、多余的 </i> 没有开始标签;第 13 行 img 缺 alt;第 14 行 a 用 name 却没有 href;第 16 行 id="main" 与第 8 行重复;第 17 行给 void 元素 br 写了 </br>;第 18 行最后一个 li 未闭合;第 19 行的 </div> 把第 10 行没关的 p 一并交叉。这 9 条与「开始校验」实跑结果逐字一致,源码里的检查逻辑与正文描述完全同源。

输入范围与检查项对照表

输入允许任意 HTML 片段(不要求是完整文档),上限 300000 字符;空输入、超长各有独立提示。检查项与浏览器行为对照如下表,默认显示 12 行,点「加载更多」补出后 4 行,可导出带 BOM 的 CSV。

检查项对照(数值仅作演示)
检查项示例HTML5 浏览器行为本工具处理
未闭合标签<div>内容到文档末尾自动闭合报错误 + 行号
交叉嵌套<b><i></b></i>各自拆开修复,DOM 与源码不一致报错误 + 行号
多余闭合标签</span> 无对应开始直接忽略报错误 + 行号
void 元素误闭合<br></br>忽略 </br>报错误 + 行号
重复 id两处 id="main"getElementById 只返回第一个报错误 + 行号
img 缺 alt<img src="a.png">照常渲染,读屏器与爬虫受损报错误 + 行号
a 缺 href<a name="top">不是链接,不可 Tab 聚焦报错误 + 行号
p 隐式关闭<p>a<p>b自动关闭前一个 p并入嵌套检查提示
li 隐式关闭<li>a<li>b自动并列两个 li未闭合者报错误
注释包裹错误<!-- 注释 -->注释内容进 DOM剥掉注释再扫描
属性引号内 >title="a>b"按引号配对解析按引号配对切分
大小写混写<DIV></div>标签名统一小写后配对统一小写后配对

输入范围、边界与精度说明

检查链路:剥 script 与 style 内容 → 剥注释 → 正则扫标签(处理引号内 >)→ 逐个过配对栈(void 豁免、隐式闭合豁免)→ 栈空性判定 → 属性级检查同步完成 → 汇总输出。边界上:空输入提示「不能为空」;超过 300000 字符拒绝并建议分段——正则扫描是 O(n),但超长文本的逐条渲染会拖垮移动端列表;无法识别的畸形标签(如 <= 开头)按非标签文本跳过,不硬报错;行号按原始换行计数,粘贴含 \r\n 的 Windows 换行不影响结果。精度上要明确 HTML校验不做什么:不做 DTD/doctype 校验、不检查 CSS 与 JS 语法、不做字符编码探测、不验证 ARIA 属性合法性——HTML5 本身没有 schema 可言,这类「合法性」只有 W3C 官方服务的规则集能裁,本页只对结构性与属性级缺陷负责,9 类检查项与对照表一一对应,不多报也不少报。

常见问题

为什么浏览器能正常打开的页面还会查出问题?

HTML5 解析规范内置了几十条纠错规则,会静默修复未闭合、错位嵌套;「能渲染」只说明浏览器兜住了,不代表源码没错。修复过程还可能改变 DOM 结构,让选择器与脚本命中错误节点。

和 W3C 官方校验服务有什么区别?

W3C 服务做 DTD 级校验且需把代码发到它的服务器;本页是纯本地静态检查,只覆盖结构性错误与属性级缺陷,但代码不出浏览器。两者互补,正式合规报告请用 W3C。

会执行我粘贴的脚本吗?

不会。本页用正则与配对栈扫描文本,不构造 DOM 树、不执行内联脚本、不加载外部资源,你的代码只是被逐字符读一遍。

<li> 不闭合也算错误吗?

HTML5 规范允许 li、p、tr、td 等在特定上下文隐式闭合,浏览器不会报错;但本工具仍会在嵌套检查里点名,因为显式闭合能避免脚本遍历与 CSS 相邻选择器踩坑。

重复 id 会有什么实际后果?

getElementById 与 #id 选择器只命中第一个,CSS :target、锚点跳转也可能定位错误;表单 label 的 for 属性指向重复 id 时关联到错误控件。

为什么 img 缺 alt 被算成错误?

alt 是读屏器用户获取图片信息的唯一来源,也是图片加载失败时的占位文本,装饰性图片规范要求显式写 alt="",缺属性与空值含义不同,所以本页把缺属性报为问题。

一次能检查多大的文件?

上限 300000 字符(约 30 万字节),超过会被拒绝并建议按模块分段;这既是性能保护,也让问题清单保持在可逐条处理的长度。

粘贴的代码会留在页面上吗?

只存在于当前标签页内存里,刷新即清空;本页无任何存储与网络行为,可在开发者工具的 Network 与 Application 面板逐项核实。

相关工具