使用方法
- 把要检查的 HTML 片段粘进输入框(示例已预置 21 行演示代码,含 9 处典型问题),按 Ctrl+Enter 或点「开始校验」,HTML校验会把每个问题报到行号。
- 结果区逐条列出问题与行号:未闭合、交叉嵌套、重复 id、img 缺 alt、a 缺 href、void 元素误闭合各算一类。
- 按行号回源码修一处、重跑一次,直到问题列表清空。
- 点「载入示例」随时还原演示代码;点「复制问题列表」把清单转给同事。
- 输入超过 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 面板逐项核实。
相关工具
- HTML格式化:修完结构后统一缩进排版。
- HTML压缩:上线前去空白,压缩不改结构。
- CSS校验:样式表侧的同类静态检查。
- JSON校验工具:接口数据侧的语法校验。
- HTML转纯文本转换器:剥离标签看纯内容。
- HTMLEntity速查表:实体写法与转义对照。
- HTML转Markdown转换器:迁移内容到 Markdown。