很多刚入门前端的朋友,在拿到一个设计稿之后的第一反应往往是:开始写div,一层套一层,哪里不对就再加一个div。等页面勉强显示出来,自己再看都觉得头疼,更别提后续要加功能、调样式、改文案时会多么痛苦。我见过太多“能跑就行”的页面,结构混乱到让人想直接重写。其实问题不在写代码的耐心,而在于一开始就没有规划好 HTML 结构。一个看似朴素的页面骨架,决定了它以后好不好维护、能不能被搜索引擎理解、能不能无障碍地被所有人使用。这篇就围绕“HTML网页结构搭建”,从语义化标签的逐一点位,聊到整站规划时该想清楚的几件事,最后给一套可以直接参考的博客页面骨架代码。
标题里的“白话前端”系列,我一贯的主张是:不绕弯子、不上术语轰炸,直接把原理和取舍讲透。HTML 是所有前端页面最底层的地基,地基歪了,后面用再好的 CSS 框架、再牛的 JS 框架都救不回来。这篇文章的内容适合刚从div走出来的初级前端,也适合那些已经写了一阵子页面、但总感觉结构哪里别扭的开发者。核心价值就一句话:用语义化的方式搭结构,用整站思维做规划,让每一个标签都名正言顺。
1. 为什么还要较真 HTML 结构:先想清楚再动手
1.1 语义化标签不是给浏览器看的,是给人看的
很多人觉得 HTML 标签只要显示正确就行,浏览器又不在乎是div还是header。这话只对了一半。浏览器确实会把header、section、div默认都渲染成块级元素,视觉上几乎一样,但差异体现在三个容易被忽略的地方。
第一个差异是代码可读性。当你休假回来,看到一坨<div class="wrap"><div class="inner"><div class="content">时,想弄清楚哪个区块对应页面的哪个部分,得靠 class 名称和注释来猜。但如果代码里直接写着<header>、<article>、<footer>,一眼就能看出页面骨架。这不是给浏览器省事,是给你自己和团队里的同事省事。
第二个差异是辅助技术的理解。屏幕阅读器在朗读页面时,会借助 HTML5 语义标签来判断内容类型。比如<nav>会让读屏软件告诉用户“这里是导航区域”,用户可以跳过或者直接前往;<main>会标记页面的主要内容区,方便用户快速跳转。如果全用div,这些信息全部丢失,视障用户的操作成本会成倍增加。无障碍不是加分项,而现在很多大厂的项目已经把它列为硬性要求。
第三个差异是搜索引擎的抓取逻辑。搜索引擎爬虫在分析页面时,会特别关注标题层级、导航、正文区域。语义化标签让爬虫更容易识别“哪部分是站点导航”“哪部分是主体内容”“每个区块的重点是什么”。这不一定直接带来排名提升,但结构化清晰的页面,在搜索结果摘要展示和富媒体结果命中等场景下,往往更有优势。
1.2 从“能显示”到“好维护”的分水岭
我常把写 HTML 比作做收纳。你买了一个新柜子,把所有东西一股脑塞进去,柜门一关,表面看着整齐,但下次找东西时就得把整个柜子翻个底朝天。而真正合理的做法是,先想清楚这个柜子用来装什么、内部怎么分区、什么类别的物品放哪一层,再动手。
页面结构也是这样。写代码之前,先看一眼设计稿,把页面分成几个大区域:顶部导航、主体内容、侧栏、底部信息。然后想清楚这些区域之间是并列关系还是嵌套关系。想清楚之后,再选标签。这个“先分区再写码”的过程,就是整站规划的最小单元。很多新手跳过这一步,直接开工,到最后样式调不对才发现是结构层级搞错了。
举个例子。一个常见的博客文章页,头部有站点 logo 和导航,中间左侧是文章正文,右侧是作者信息、相关文章,底部是版权信息。如果一开始就把左侧和右侧都放进一个<div>, 然后在这个 div 里又套了七八层 div,等你想给右侧栏做 fixed 定位或者折叠效果时,会发现选择器路径又长又脆弱。而如果一开始就用<main>包文章正文、用<aside>包侧栏,后续所有针对侧栏的操作都清晰明了。
所以,较真 HTML 结构,不是为了显摆规范,而是为了让你自己的生活更轻松。前期多花十分钟规划,后期能省下几小时的排查时间。
2. 语义化标签实操指南:每个标签怎么用才算用对
2.1 页面骨架五件套:header、nav、main、footer、aside
一个典型的页面整体骨架,可以拆成五个部分。我用一份最常见的企业官网布局来对应说明。
<header>通常位于页面顶部,放 logo、站点名称、搜索框、导航栏。需要注意,header并不一定只能出现在页面顶部,在article或者section内部也可以有自己的header,用来表示该区块的头部信息。不要一看到“头部”就觉得只能有一个。
<nav>是导航区域专用标签。主要放页面之间的跳转链接,比如主导航、侧栏导航。导航内部通常是一个<ul>列表,列表项里再放<a>。这里有个常见疑问:页面底部那些友情链接、法律条款链接,是不是也要用nav?如果这些链接组成了一个明确的导航区块,用nav也没问题;但如果只是零散的几个链接,直接用<div>或<footer>里的<ul>就好。判断标准是“是不是一组连贯的、用于站内引导的链接”。
<main>用于包裹整个页面唯一的、核心的主体内容。一个页面只能有一个<main>。文章正文、产品列表、搜索结果列表都应该放在main里。不要把它当成普通的容器到处用,不然它承载的“主体内容”含义就会被稀释。还有个容易忽略的点:main标签不应是任何article、aside、header、footer、nav的后代,也就是说它是和这些区域平级的顶层结构。
<footer>通常放版权信息、联系方式、站点地图链接、备案号等。和header类似,footer也不局限于页面底部,文章内部的footer可以放作者、发布时间、标签等元信息。一个页面可以有多个footer,但用来表示页面级页脚的那个footer,一般放在body的直接子级。
<aside>是侧边栏标签,用于放与主体内容间接相关的内容,比如推荐文章、广告、作者简介、目录。判断一个区块要不要用aside,就看它如果被移除,会不会影响主内容的完整理解。不会,就适合用aside;如果移除后用户看不懂正文了,那它就是正文的一部分,应该留在main里。
2.2 内容级标签:article、section、h1-h6、figure 等
骨架标签解决的是“页面分块”的问题,内容级标签解决的是“块里怎么组织”的问题。
<article>表示一个独立的、可复用的内容单元。它可以是一篇完整的博客文章、一条论坛帖子、一条用户评论、一则新闻。关键特征是“独立成篇”,把它从当前页面抽离到任何地方,语义依然完整。在博客首页,每一篇摘要都应该包在article里;在文章详情页,正文全文用article;甚至评论区里的每一条评论,也是一个article。这有助于读屏软件和搜索引擎理解内容边界。
<section>是比article更宽泛的内容分区。通常一个section应该有主题和标题。比如博文里“引言”“实现步骤”“踩坑记录”三个部分,可以用三个section。需要注意,section不是万能容器。如果你只是想给某个区域加一个独立的样式钩子,用div就够;只有这个区域在逻辑上是一个内容分区时,才用section。无脑用section和当初无脑用div没有本质区别。
<h1>到<h6>是标题标签,形成文档大纲。一个页面的内容层级,应该像论文目录一样清晰。很多人会纠结:一个页面只能有一个h1吗?在 HTML5 规范下,理论上每个section都可以有自己的h1,但在实际浏览器和搜索引擎的处理中,最稳妥和普遍的做法是:整个页面的主要内容区只保留一个h1,它代表这个页面的核心主题,下面的分区用h2,再往下的子分区用h3。不要跳级,不要为了视觉效果就把h3拉大字号充作h1,标题层级承担的是文档结构的职责,视觉样式交给 CSS 处理。
<figure>和<figcaption>用来包裹插图、图表、代码片段等独立内容,figcaption是它的说明文字。比如一篇教程里的命令行截图,可以用figure包住图片,再用figcaption写一句“安装完成后查看版本号”。这个组合的好处是图片和说明文字在语义上绑定,搜索引擎和辅助技术都能理解它们的关联关系。
2.3 常见误区:一杆子打死 div,还是无脑堆标签
网上有些观点把div说得一无是处,好像用了div就不专业。这是从一个极端走到另一个极端。实际上,div是一个非常诚实的无语义容器。当页面中的某个区域既不是导航、也不是主体内容、又不是独立成篇的内容,纯粹是为了布局或分组而存在时,用div完全正确。比如轮播图的外层容器、弹窗的背景遮罩、flex 布局的中间层,这些在语义上什么都不算,硬塞一个section反而误导别人。
另一个误区是无脑堆砌语义标签。为了“显得语义化”,把页脚里的每个链接都包上nav,把每段文字都塞进article,结果标签层级膨胀,代码比原来更难读。语义化的核心是准确,而不是数量。遇到拿不准的标签,我的判断标准很简单:先问自己“这个区域的内容,在语义上是什么?”,如果答案是“站点的导航”,就选nav;如果答案是“页面里的侧边补充信息”,就选aside;如果答案是“我也不知道它是什么,反正就是要包一块”,就用div。
说白了,HTML 标签是词汇表,你用词汇的目的是准确表达一篇文章的组织结构,而不是为了显得自己词汇量丰富而去用生僻词。准确永远比华丽重要。
3. 整站规划:从单个页面视角拉高到站点视角
3.1 目录结构与命名规范
很多教程只教单个页面怎么写,但实际工作中,我们很少只做一个孤零零的页面。一个完整的网站有首页、列表页、详情页、关于页、联系我们页,这些页面之间共享着同一套头部导航和底部信息。如果只盯着单页写,会发现很多重复劳动,也会为了改一个链接而在几十个页面里来回翻。所以在写 HTML 之前,整站层面先做三件事。
第一件事是设计目录结构。一个最简单、也最通用的前端目录是这样的:项目根目录下,index.html作为入口首页,about.html、contact.html等一级页面直接放根目录或放pages目录,assets目录里再细分css、js、images。如果你的站点层级很深,比如有多级栏目,可以考虑给每个栏目建一个子目录,每个子目录里放自己的index.html。这样做的核心逻辑是:URL 的结构要尽可能反映站点的层级关系,用户看到链接就知道大概是在哪个栏目下。
第二件事是统一命名规范。我习惯用英文小写加短横线的方式命名文件与目录,例如article-detail.html、user-profile.css。不使用驼峰大写,避免在 Linux 服务器上因为大小写敏感而出问题。CSS 类名使用有含义的 BEM 风格,比如.article-card__title表示“文章卡片里的标题”。你可能觉得命名这事无所谓,但当你维护一个超过几十个文件的站点时,一套统一的命名规范能让你在文件管理器里快速找到目标文件。
第三件事是提前确定公共代码的复用方式。如果你没有使用任何构建工具,纯静态页面时,公共头部和尾部往往只能靠复制粘贴。这样做虽然简单,但后续维护成本很高。如果你已经用上了模板引擎或组件化框架,那么可以把 header 和 footer 抽成公共组件,每个页面只需引用组件。这是整站规划里回报率最高的一个动作,请务必在写第一个页面前就想好。
3.2 多个页面之间的共享区块怎么抽
最常见的共享区块就是全站统一头部和底部。头部里包括 logo、导航菜单、搜索框,底部包括版权信息和备案链接。这些区块在整站级别必须保持高度一致,否则用户会在不同页面之间感到错位。
比较理想的做法是,在没有工程化工具的情况下,也至少使用服务端模板的 include 功能,或者通过 JavaScript 动态加载公共头部和尾部。但这种动态加载的方式有个缺点:在页面加载初期会出现一段无样式或空白的内容,并且对搜索引擎爬虫不友好。所以如果你的项目偏重内容型站点,更推荐使用服务端模板渲染或静态站点生成器来拼接公共区块。
共享区块里还有一类容易被忽略的内容:<head>里的公共头部标签。比如整套页面的<meta>标签、Open Graph 协议标签、favicon 引用、公用的 CSS 和 JS 文件。这些虽然在视觉上看不见,却是整站“气质”的一致性来源。我建议做一个通用的 head 片段,里面包含全站统一的字符集、视口声明、基础 SEO 标签,新页面直接复用。这比每个页面临时写一套要稳定得多。
还有一类共享区块是面包屑导航。面包屑不仅能帮助用户定位当前位置,还是整站层级结构的直接体现。它的 HTML 结构通常是一个<nav>里放一个<ol>列表,每个列表项代表一个层级。当前页对应的那一项要设置aria-current="page"来标识。这个属性在无障碍中很实用,屏幕阅读器会把它读成“当前页面”。
3.3 导航与面包屑:用户和搜索引擎都依赖的路径
导航是整站规划的命脉。在做 HTML 结构时,主导航的标签结构要清晰,让用户和爬虫都能方便地找到。我习惯的写法是:<nav>内包裹一个<ul>,每个一级栏目是一个<li>,里面放一个<a>;如果有下拉菜单,就在该<li>内部再嵌套一个<ul>。这样嵌套列表的语义非常明确,比一堆div + div + a要可靠得多。
关于当前页面在导航中的高亮,除了在 CSS 上加一个.active类,也可以在 HTML 层面用aria-current="page"标记。这个属性告诉辅助技术“这个链接指向的是当前页面”,这样读屏用户不会误以为这是一个指向其他页面的普通链接。如果你用后端模板渲染,可以当前页为某个导航项输出这个属性,实现起来很简单。
面包屑对整站规划的意义在于,它可以把单页面的内部结构串联成站点的层级树。比如你在“产品中心 > 产品详情”这个路径中,商品名称是当前页,产品中心就是父级栏目。在 HTML 中使用<ol>而不是<ul>,是因为面包屑的每一项是有顺序的。给每项之间加分隔符时,建议用 CSS 的::before伪元素生成斜杠,而不是在 HTML 里手写文字“>”,否则对读屏软件来说会读成很奇怪的符号。
3.4 移动端优先与响应式结构要点
现在做整站规划,基本不可能不面对移动端。虽然响应式布局主要靠 CSS,但 HTML 结构同样影响移动端的表现。核心原则是:内容层级要轻,交互区域要明确。
先说轻。移动端屏幕小,如果 HTML 结构嵌套过深,每一层都会有外边距、内边距、边框等样式叠加,在手机上的渲染性能和视觉表现都会受影响。我见过有的页面为了一个弹窗,套了五六层 div,最终导致内部元素的宽度计算混乱。移动端结构应该尽量扁平化,能用兄弟并列就不用多层嵌套。
再说明确。移动端的点击区域至少需要 44 像素左右,这在 HTML 层面意味着,不要把链接或按钮包在一个很细小的 span 里,然后靠 CSS 弹性拉伸。更好的做法是直接用<a>或<button>作为交互元素,设置好内边距。如果一定要在某个容器上绑定点击事件,也请把内层的链接或按钮做成标准交互元素,避免靠 JS 手写键盘事件。
还有一点容易被忽略:移动端优先的设计,HTML 里的内容顺序非常重要。默认情况下,移动端是从上到下线性阅读,所以你要把最重要的内容排在前面。比如文章详情页,页面上从上到下应该是标题区、正文内容、作者信息、相关推荐。有些桌面端布局会把侧栏放在左边,这时如果 HTML 里侧栏在前面,那么移动端用户会先看到侧栏再看到正文,体验就很差。所以在写结构时,要把移动端的内容优先级作为默认顺序,桌面端通过 CSS 的 flex 或 grid 来调整列的顺序。
4. 实操:搭一个标准博客页面骨架(含代码)
4.1 设计目标与页面分区
纸上谈兵到此为止,我们直接上手搭一个标准博客页面。设计目标如下:页面是博客的文章列表页,顶部是全站统一头部,包含站点名称和主导航;中间主体分成左右两栏,左侧是文章列表,右侧是侧边栏,放作者信息和热门文章;底部是全站统一页脚。这个页面模拟的是一个最简单的整站里的列表页,同时也是大多数内容站都能套用的模板。
页面结构分区如下。
- 全站统一头部
header:里层放一个div容器,左中是 logo 和导航,右侧放搜索框 - 主要区域
main:里层用一个div容器做两栏布局容器,左侧用section放文章列表,右侧用aside放侧栏 - 全站统一底部
footer:里层放版权信息
按照移动端优先的思路,main内部左侧文章列表放在前面,右侧侧栏放在后面;桌面端布局时用 CSS Grid 或 Flexbox 把侧栏排到右边。
4.2 HTML 代码逐步拆解
我先放一份完整的 HTML 骨架,你可以直接复制,也可以到自己的项目中微调。这份代码的重点在结构,样式细节请根据你自己的设计稿处理。
<!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>某某博客</title> <meta name="description" content="分享前端技术、工程化实践与个人思考"> </head> <body> <header class="site-header"> <div class="container header-inner"> <a href="index.html" class="logo">某某博客</a> <nav class="main-nav" aria-label="主导航"> <ul> <li><a href="index.html" aria-current="page">首页</a></li> <li><a href="articles.html">文章</a></li> <li><a href="about.html">关于</a></li> <li><a href="contact.html">联系</a></li> </ul> </nav> <form class="search-form" role="search" action="/search" method="get"> <label class="sr-only" for="search-input">搜索</label> <input type="search" id="search-input" name="q" placeholder="搜索文章..."> <button type="submit">搜索</button> </form> </div> </header> <main class="site-main"> <div class="container main-inner"> <section class="article-list-section" aria-labelledby="latest-title"> <h1 id="latest-title">最新文章</h1> <article class="article-card"> <h2 class="article-card__title"><a href="post-1.html">语义化标签到底怎么用</a></h2> <p class="article-card__meta"> <time datetime="2025-01-18">2025年1月18日</time> </p> <p>这篇文章聊一聊语义化标签的使用边界,包括 header、section、aside 的实际场景。</p> </article> <article class="article-card"> <h2 class="article-card__title"><a href="post-2.html">整站规划中的目录结构</a></h2> <p class="article-card__meta"> <time datetime="2025-01-10">2025年1月10日</time> </p> <p>搭建一个中大型内容站点前,目录结构的推演比写首页更重要。</p> </article> <article class="article-card"> <h2 class="article-card__title"><a href="post-3.html">响应式布局的结构基础</a></h2> <p class="article-card__meta"> <time datetime="2025-01-02">2025年1月2日</time> </p> <p>好的 HTML 结构可以让响应式布局事半功倍,这篇文章给出具体案例。</p> </article> </section> <aside class="site-aside"> <section class="author-card"> <h2>关于作者</h2> <p>一位一直在一线写页面的前端从业者,关注工程化和交互体验。</p> </section> <section class="hot-articles"> <h2>热门文章</h2> <ul> <li><a href="post-1.html">语义化标签到底怎么用</a></li> <li><a href="post-5.html">如何拆解设计稿</a></li> <li><a href="post-8.html">前端项目命名规范</a></li> </ul> </section> </aside> </div> </main> <footer class="site-footer"> <div class="container footer-inner"> <p>© 2025 某某博客. 保留所有权利。</p> </div> </footer> </body> </html>下面我逐步解释这段代码里的关键点。
先看<head>。<!DOCTYPE html>是文档类型声明,必须作为文档第一行。<html lang="zh-CN">里的lang属性向浏览器和读屏软件声明页面主要语言是简体中文,这个属性很容易被忽略,但它对中文页面的无障碍体验和搜索引擎的语种判断都有影响。viewport元信息用于移动端适配,缺少它,手机浏览器会把页面当作桌面页面缩小显示,导致字体极小。这是移动端优先的最基本设置。
再看<body>的整体三层结构:header、main、footer是 body 的平级子元素,对应我前面说的页面骨架五件套中的三个。main内部用.container和.main-inner做布局容器。这里为什么用div?因为它们只是布局钩子,不具备任何内容语义。在section上我加了aria-labelledby="latest-title",这个属性把它和h1联系起来,告诉辅助技术“这个区域的标题是‘最新文章’”。这是一种很实用的无障碍增强,代价很低,但经常被忽略。
文章列表区用了section而不是main,因为main只有一个,且整个页面主体已经用main包裹了。每一篇文章用article包裹,里面的标题是h2,整个页面唯一的h1是“最新文章”。为什么不用h1来表示每一篇文章的标题?因为这里是一个列表页,每篇文章只是这个页面中的一个内容单元,页面的唯一主题是“最新文章列表”,所以列表页的h1应该是那个页面级的标题,而不是某篇文章的标题。等你点进文章详情页,才会把那篇文章的标题作为这个详情页的h1。
侧栏aside里又嵌套了两个section,一个写作者信息,一个写热门文章。它们都有关联的h2标题。侧栏在移动端会显示在主内容之后,因为 HTML 中它确实在section之后;桌面端你可以通过 CSS Grid 把aside从左列移到右列,而结构上仍保持先主后次的顺序,兼顾了阅读优先和无障碍跳转。
footer里直接用p包裹版权文字,没有必要再加一层div。很多新手习惯到处包裹div,这是冗余结构的第一来源。记住一个原则:能用语义标签表达就用语义标签,语义标签不够表达纯粹的布局需求时再用div,而不是反过来先堆div再想语义。
4.3 配合 CSS 与 JS 时的注意点
结构只是地基,页面最终还得配合 CSS 和 JS。这三个角色各有分工,HTML 负责语义,CSS 负责外观,JS 负责行为。如果你在写 HTML 时就已经想好它们如何协作,很多后期坑是可以避开的。
先说 CSS 选择器。语义化标签配合 class 使用,可以写出精简的选择器。比如侧栏的section可以用.site-aside section来统一设置侧栏内分区的间距,而不需要在每个section上重复写 class。如果你的 HTML 全是嵌套的div,CSS 选择器不是层数爆炸,就是依赖具体标签位置,改一个地方很容易带崩一片。还有一个实际感受:当结构语义清晰时,即使别人接手你的代码,只看 HTML 就能大概猜到 CSS 应该怎么写。
再说 JS 的事件绑定。JS 拿元素时,优先用语义选择器,比如document.querySelector('header .search-form'),比层层parentNode去寻找可靠得多。如果页面的结构符合通用语义,许多第三方脚本也能开箱即用,比如阅读进度条会去找article,目录生成脚本会去找h2、h3。这些都是建立在良好 HTML 结构之上的生态福利,别辜负它。
还得提一处细节:role="search"。这个 ARIA 角色加在搜索表单上,告诉辅助技术“这是一个搜索区域”。HTML5 里其实没有专门的 search 标签,所以这是语义化标签之外的补充手段。类似的情况还有role="navigation",不过nav本身已经隐含了 navigation 角色,不必重复添加。我见过有人不信任原生标签,硬要加上一堆冗余 ARIA 角色,这是画蛇添足。ARIA 的使用准则是“优先使用原生 HTML 特性,只在 HTML 无法表达时才用 ARIA 补足”。
5. 常见问题与排查技巧实录
5.1 为什么 section 没有标题会不“完美”
这里说的“不完美”不是指浏览器报错,而是指语义完整性。如果你用section包裹一块内容,最好在里面提供一个标题(h1-h6),因为section在 HTML5 文档大纲算法里被认为是一个独立的章节,章节没有标题就像一本书的某个章节没有篇名。虽然实际开发中,浏览器不会因为你没写标题就报错,很多设计稿里的侧栏卡片也确实没有可视标题,但从语义和特殊用户角度,没有标题的区块会让屏幕阅读器无法跳到对应的内容区域。
如果设计稿上确实没有可见标题,可以用一个视觉上隐藏但读屏可读的标题来解决。方法是用 CSS 把这个标题定位到屏幕外,比如:
.sr-only { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; border: 0; }然后在需要隐藏标题时,给它加上sr-only类。这种方式兼顾了视觉设计和语义完整。这条经验在我维护无障碍项目时特别常用,建议收藏。
5.2 h1 到底能不能出现多次
这是前端圈的老常客。我在前面给出了保守做法:一个页面尽量只有一个h1,且它应该概括当前页面的核心主题。技术上说,HTML5 允许多个h1,但不同浏览器的文档大纲算法并未严格遵循标准,搜索引擎的处理方式也有差异。与其冒着理解偏差的风险,不如采用最稳妥的方案:每个页面只用一个h1。
有一种例外需要特别说明:如果你的项目使用了 Web Components 或者很复杂的嵌套section,每个组件内部自带一个h1,这种写法在不支持新版大纲算法的浏览器里,会出现标题层级混乱。我的建议是无论组件多复杂,都不要让多个h1出现在同一个页面的可见文档流里。组件内部的标题,可以是用h2或h3,具体层级看它实际所处的位置。记住,标题层级是文档的逻辑结构,不是样式的附属品。
5.3 嵌套那么深,怎么检查结构
写完一个页面后,我强烈建议你检查一下 DOM 结构和标题大纲。最简单的方法是用浏览器开发者工具里的 Elements 面板,查看 body 下的直接子元素,确认header、main、footer平级存在。另一个方法是使用各种能生成页面大纲的插件或网站,它们会把h1、h2、h3的关系可视化地列出来,一眼就能看出有没有跳级、有没有缺标题。
还有一个我自己常用的笨办法:把页面里所有标签的结束标签列出来,不看内容只看轮廓。比如</section>后面跟的是</div>还是</aside>,能反映嵌套是否正确。如果标签闭合混乱,说明结构在写的过程中已经失控了。此时不要急着打补丁,建议把该区块重写。宁可删掉重来,也不要在摇摇欲坠的地基上继续盖楼。
另外可以使用 HTML 校验工具,直接把代码贴进去,工具会标出标签嵌套错误、属性缺失、重复 id 等问题。很多看起来莫名其妙的样式问题,追根溯源都是标签没有闭合导致的。这种静态校验多跑几次,你会发现自己写结构时会更谨慎。
5.4 浏览器默认样式带来的“看起来不对劲”
刚写完 HTML,还没写 CSS 时,你会看到h1很大、ul有缩进、button有边框,这些全是浏览器默认样式。有时候你觉得自己结构没问题,但页面看着歪歪扭扭,就以为是 HTML 错了,其实只是没有重置默认样式。通常我们会在项目里引入一份 CSS reset 或使用像是 Normalize.css 之类的样式归一化文件,让所有浏览器从同一个起点渲染。
这和 HTML 结构有什么关系?关系在于,你在写结构时需要预想到一些默认样式带来的布局影响。比如ul在默认样式下左内边距比较大,如果导航用了ul但没重置,导航项就会看起来偏右。这不是你结构写错了,而是默认样式在起作用。不要因为这个就去改 HTML,用 CSS 重置即可。
还有一点和默认样式相关的常见问题:img默认是inline元素,底部会和文字基线之间有几像素的空隙。如果你在figure里直接放img,会发现图片下方多出一条空隙。解决方法是给img设置display: block或vertical-align: middle。这个问题和标签语义无关,但很多新手会误以为是自己标签用错了。结构和样式是两码事,排查问题时要分清边界。
6. 最后分享一点维护结构的习惯
文章到这里,该讲的原理和示例代码都已经讲了。最后想聊聊我自己在实际项目中养成的几个小习惯,供你参考。写完一段 HTML,我会刻意去读一遍标签名,不用看 class 和样式,只读<header>、<nav>、<main>、<section>、<article>、<aside>、<footer>,就像在念一个文章的提纲。如果读起来通顺,说明结构是合理的;如果读起来很绕,说明结构里可能塞了太多无意义的容器。这个方法简单但很有效。
另一个习惯是维护一套自己的“页面结构模版”。不管项目大小,先用标准结构铺底:头部、导航、主区、侧栏、底部分别对应哪些标签,固定下来。每次新建页面时,从模板开始,而不是从空白开始。这样一段时间之后,所有页面的结构风格都会趋于统一,团队协作时对方也很容易接手。
最后,永远记住一点:HTML 结构是为内容服务的。你不能为了满足所谓的规范,把一个本该是简单的列表变得无比复杂;也谈不上用了几个高级标签就显得厉害。真正厉害的人,是把一个复杂页面组织得让人看得明白、改得动、跑得稳。我希望这篇文章能让你在下次动手写结构前,多想一步;在你面对一坨混乱的标签时,有勇气推倒重来。这步棋走对了,后面的 CSS 和 JS 都会顺很多。