doctype、lang、charset必须写在最前面:须为文档首行无前置内容;紧随其后且语言值须真实匹配主内容;须为内首个子元素,确保浏览器正确解码。

DOCTYPE、lang、charset 必须写在最前面
浏览器解析 HTML 是从第一字节开始的, 必须是文档第一行,不能有空格、BOM 或注释前置。否则会触发怪异模式(Quirks Mode),导致盒模型、字体渲染等行为不一致。
要紧随其后,语言值必须真实反映页面主内容语言,不能写成 lang="zh" 或留空;JS 动态改 document.documentElement.lang 无效,只起始时生效。
<meta charset="UTF-8"> 必须出现在 前 1024 字节内,且应放在所有其他 <meta> 之前——如果它被 CSS 或 JS 引用挡在后面,某些旧版 Safari 可能乱码。
- 常见错误:
<!-- 注释 --> → 触发怪异模式 -
<meta charset>放在<link rel="stylesheet">后面 → IE/旧 Safari 解析失败 -
lang值与实际内容不符(如中文页写en)→ 屏幕阅读器发音错乱、SEO 权重稀释
语义化标签不是“锦上添花”,而是 DOM 解析前提
搜索引擎和屏幕阅读器不读 class 名,只认原生标签。用 <div class="header"> 替代 <code><header></header>,等于把路标涂掉再手绘一张——人能看懂,机器直接迷路。
<main></main> 必须且只能出现一次,且不能嵌套在 <section></section> 或 <article></article> 内;<nav></nav> 只包裹真实导航链接,广告位、社交图标别塞进去;<aside></aside> 不是“右边栏”,而是与当前内容相关但可剥离的补充信息(比如引用、作者简介)。
- 错误嵌套:
<section><main></main></section>→<main></main>失效,爬虫忽略主体内容 -
<h1></h1>出现在<header></header>的 logo 区域里,而非<main></main>内部 → 主题权重被稀释 - 用
<div> 套三层再加 <code>display: grid→ DOM 层级超 4 层,CSS 选择器匹配成本指数上升,滚动卡顿列表结构必须严格遵循原生语义规则
HTML 列表不是“看起来像就行”。
<ul></ul>和<ol></ol>的子元素只能是<li>,<dl></dl>必须由<dt></dt>+<dd></dd>成对出现——任何偏离都会让 NVDA、VoiceOver 跳过条目,或让querySelectorAll('li')拿不到全部项。嵌套子列表时,
<ul></ul>或<ol></ol>必须直接作为<li>的子元素,中间不能插<div> 或 <code><p></p>。需要标题说明时,用<figure></figure>+<figcaption></figcaption>或独立<h2></h2>,别塞进<ul></ul>开始标签里。- 错误写法:
<ul><div><li>...</li></div></ul>→ 屏幕阅读器跳过该项 - 无
aria-label的侧边导航菜单 → 辅助设备只报“列表,7 个项目”,不知用途 -
<ol type="A"></ol>被 CSSlist-style: none覆盖 → 语义丢失,序号不可访问
DOM 深度与资源加载顺序比代码缩进更致命
单个
<section></section>下子元素超过 50 个,滚动时重排明显卡顿;<div><div><div> 这类纯样式嵌套,每多一层,浏览器构建 DOM 树耗时就非线性增长。而一个没加 <code>defer的<script src="app.js"></script>放在里,会中断 HTML 解析,用户看到白屏——这比 JS 报错影响更大。- 动态插入大量列表项?用
DocumentFragment批量 append,别逐个appendChild - 关键 CSS 放
,用<link rel="preload">提前拉取;非关键 JS 加defer或async - 压缩 HTML 时禁用
removeAttributeQuotes—— 值含空格或特殊字符(如class="btn primary")会解析失败
真正难的不是记住每个标签怎么写,而是判断某个容器有没有独立语义:没有,就删;有,就选最贴切的那个原生标签。DOM 结构一旦定型,后续所有样式、JS、SEO、无障碍都得跟着它走——改起来比重写逻辑还疼。
- 错误写法:











