<main>必须唯一且不得嵌套于其他语义容器内,<header>和<section>需依内容功能而非视觉布局使用,语义标签通过隐式ARIA角色与DOM约束保障可访问性、SEO及JS定位可靠性。

<main> 必须唯一、<header> 不能当装饰盒、<section> 不是视觉格子——这些不是规范教条,而是浏览器解析 DOM 时的真实约束条件。语义化标签让结构“稳固”,本质是让 DOM 树可预测、可定位、不可绕过。
DOM 层级被语义标签硬性锚定
浏览器解析 HTML 时,<main>、<nav>、<article> 这类标签会强制生成特定的隐式 ARIA 角色和 outline 行为,同时限制嵌套关系。比如:
-
<main>在整个文档中只能出现一次,且不能是<header>、<footer>、<nav>、<section>的子元素 —— 浏览器会直接忽略嵌套的第二个<main>,或静默降级为<div> -
<article>内部可以有独立的<header>、<footer>,但<section>没有这种自治权;它必须依附于上层语义容器(如<main>或<article>)才有意义 - 用
<div class="main">替代<main>,JS 中document.querySelector('main')就会返回null,哪怕 CSS 样式完全一致
结构断裂常发生在动态操作之后
很多“上线后结构突然失效”的问题,根源不在初始 HTML,而在 JS 操作阶段。例如:
将 Markdown 文章转换为微信公众号兼容的纯 HTML 格式。当用户要求"转成微信格式"、"生成公众号文章 HTML"、"排版到微信草稿箱"、"微信粘贴格式"时触发。输出带内联样式的纯 HTML,微信编辑器可直接渲染,无需额外适配。支持标题、段落、列表、粗体、行内代码、表格、引用块、代码块、配图的完整转换。
- 用
innerHTML = '...'覆盖整个<main>内容,会连带抹除其语义边界,新插入的 HTML 若没重写语义标签,DOM 就退化成一堆<div> - SPA 路由切换时,若只替换
<main>的子节点但未调用replaceChildren()或重建语义包裹,旧的<article>可能残留,新的内容却裸奔在<main>下 - 构建工具(如
html-webpack-plugin)自动注入 wrapper<div id="app">,若开发者没注意它把<main>包进去了,就等于违反了“<main>不得嵌套在<div>中”的契约
标题层级错乱会让整个骨架脱钩
<h1> 到 <h6> 不是字号开关,而是 DOM 中不可跳过的逻辑链。搜索引擎和 VoiceOver 都靠它建立导航路径:
立即学习“前端免费学习笔记(深入)”;
- 跳级(
<h2>后直接<h4>)、重复<h1>、或把标题塞进<div>再用 CSS 模拟,都会导致大纲(outline)断裂 - Lighthouse 报 “Heading elements do not appear in a sequentially-descending order”,往往不是样式问题,而是结构已失联
-
<article>内部的<h2>是它的局部起始,不与外部<h1>比较层级;但若把它放在<main>外、又没包在<section>或其他语义容器里,它就失去上下文,变成孤点
真正容易被忽略的,是语义标签之间的“契约关系”:它们不是独立存在,而是一组相互校验的 DOM 约束。一旦某处松动(比如用 <div class="nav"> 冒充 <nav>),后续所有依赖这个结构的 JS、CSS、辅助技术都会开始漂移——不是立刻报错,而是缓慢失准。


















