HTML5 权威指南:从入门到精通
资料来源:WHATWG HTML Living Standard(html.spec.whatwg.org)、MDN Web Docs、W3C WAI、W3Schools、freeCodeCamp 等权威资源综合整理
最新标准:HTML Living Standard(WHATWG 维护,2019 年起为唯一权威版本)
目录
- [HTML 历史与标准演进](#HTML 历史与标准演进)
- 文档基础结构
- [所有 HTML 元素速查](#所有 HTML 元素速查)
- [语义化 HTML5](#语义化 HTML5)
- 表单与输入控件
- 多媒体元素
- [HTML5 核心 API](#HTML5 核心 API)
- 全局属性与数据属性
- 可访问性(ARIA)
- [SEO 与 Meta 标签](#SEO 与 Meta 标签)
- 性能优化最佳实践
- 完整页面模板
一、HTML 历史与标准演进
1.1 关键时间线
1991 Tim Berners-Lee 发布首份 HTML 文档(18 个标签)
1995 HTML 2.0 ------ 首个 IETF 标准
1997 HTML 3.2 / HTML 4.0 W3C 接管
1999 HTML 4.01 ------ 最后一个"传统"版本
2000 XHTML 1.0 ------ XML 严格语法分支
2004 WHATWG 成立(Apple + Mozilla + Opera)
2008 HTML5 首个公开草案
2014 HTML5 正式成为 W3C 推荐标准(Recommendation)
2019 W3C 将 HTML 和 DOM 标准主导权移交 WHATWG
现在 HTML Living Standard(无版本号,持续更新)
1.2 两大机构对比
| W3C | WHATWG | |
|---|---|---|
| 全称 | World Wide Web Consortium | Web Hypertext Application Technology Working Group |
| 核心成员 | 全球 400+ 组织 | Apple、Google、Mozilla、Microsoft |
| 维护文档 | 历史版本(HTML5 ~5.3 已退休) | HTML Living Standard(当前权威) |
| 规范 URL | www.w3.org/TR/html/ | html.spec.whatwg.org |
| 更新方式 | 版本快照 | 持续滚动更新 |
1.3 Inmon vs Kimball 的 HTML 版本
| 版本 | 发布年份 | 状态 |
|---|---|---|
| HTML 4.01 | 1999 | 已退休(2018) |
| XHTML 1.0 / 1.1 | 2000/2001 | 已退休(2018) |
| HTML5 | 2014 | 已退休(2018) |
| HTML 5.1 / 5.2 | 2016/2017 | 已退休(2021) |
| HTML Living Standard | 2019 至今 | ✅ 当前标准 |
关键认知 :
<!DOCTYPE html>就是现代 HTML 的声明,已无版本之分。
二、文档基础结构
2.1 最小化合法文档
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>
</head>
<body>
<p>Hello, World!</p>
</body>
</html>
逐行解释:
| 行 | 作用 |
|---|---|
<!DOCTYPE html> |
触发标准模式(Standards Mode),避免怪异模式渲染 |
<html lang="zh-CN"> |
根元素;lang 属性帮助屏幕阅读器、搜索引擎识别语言 |
<meta charset="UTF-8"> |
字符编码声明,必须在 <head> 最顶部,防止乱码 |
<meta name="viewport" ...> |
移动端视口控制,是响应式设计的基础 |
<title> |
浏览器标签栏标题,也是 SEO 最重要的标签之一 |
2.2 <head> 内常用元素
html
<head>
<!-- 必须 -->
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>页面标题(≤60字)</title>
<!-- SEO -->
<meta name="description" content="页面描述(150-160字)">
<meta name="author" content="作者名">
<meta name="robots" content="index, follow">
<link rel="canonical" href="https://example.com/page">
<!-- 社交媒体 Open Graph -->
<meta property="og:title" content="标题">
<meta property="og:description" content="描述">
<meta property="og:image" content="https://example.com/img.jpg">
<meta property="og:url" content="https://example.com/page">
<meta property="og:type" content="website">
<!-- Twitter Card -->
<meta name="twitter:card" content="summary_large_image">
<meta name="twitter:title" content="标题">
<!-- 样式表(阻塞渲染,放最前) -->
<link rel="stylesheet" href="styles.css">
<!-- 预加载关键资源 -->
<link rel="preload" href="hero.jpg" as="image">
<link rel="preload" href="font.woff2" as="font" type="font/woff2" crossorigin>
<!-- DNS 预解析 -->
<link rel="dns-prefetch" href="//api.example.com">
<link rel="preconnect" href="https://fonts.googleapis.com">
<!-- 网站图标 -->
<link rel="icon" type="image/svg+xml" href="/favicon.svg">
<link rel="apple-touch-icon" href="/apple-touch-icon.png">
<!-- 结构化数据(JSON-LD) -->
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "WebPage",
"name": "页面标题"
}
</script>
</head>
2.3 文档内容模型(Content Model)
HTML 规范将所有内容分为七类,决定哪些元素可以嵌套哪些元素:
| 内容类别 | 典型元素 | 说明 |
|---|---|---|
| 元数据内容 | <link> <meta> <script> <style> |
只能出现在 <head> 中 |
| 流式内容 | 几乎所有块级元素 | 可出现在 <body> 中 |
| 分区内容 | <article> <aside> <nav> <section> |
在文档大纲中创建新区块 |
| 标题内容 | <h1>~<h6> <hgroup> |
定义区块标题 |
| 短语内容 | <a> <em> <strong> <span> |
段落级行内元素 |
| 嵌入内容 | <img> <video> <audio> <canvas> <iframe> |
引入外部资源 |
| 交互内容 | <a> <button> <input> <select> |
用户可交互的元素 |
三、所有 HTML 元素速查
3.1 文档元数据
| 元素 | 作用 |
|---|---|
<html> |
根元素 |
<head> |
文档头部元数据容器 |
<title> |
文档标题(必须唯一) |
<base> |
所有相对 URL 的基础 URL |
<link> |
外部资源链接关系 |
<meta> |
各类元数据 |
<style> |
内嵌 CSS |
<script> |
内嵌或外部 JavaScript |
<noscript> |
无脚本时的备用内容 |
3.2 分区(语义)元素
| 元素 | 语义 | 注意事项 |
|---|---|---|
<body> |
文档主体 | 每页只有一个 |
<main> |
页面主要内容 | 每页只能有一个可见 <main> |
<header> |
页眉/区块标题区域 | 可在多个区块中使用 |
<footer> |
页脚/区块附属信息 | 可在多个区块中使用 |
<nav> |
导航链接区域 | 仅用于主要导航,非所有链接集合 |
<section> |
主题内容分区(通常有标题) | 不要用来替代 <div> |
<article> |
独立自包含内容 | 可独立分发,如博客文章 |
<aside> |
相关但非必要的附属内容 | 侧边栏、广告、引用 |
<figure> |
独立的媒体单元 | 图表、代码块、图片 |
<figcaption> |
<figure> 的说明文字 |
<figure> 的第一个或最后一个子元素 |
<address> |
联系信息 | 仅用于最近的 <article> 或 <body> 的作者联系信息 |
<hgroup> |
标题组合(主标题+副标题) | 包含 <h1>-<h6> 的组合 |
<search> |
搜索区域 | HTML Living Standard 新增 |
3.3 标题元素
html
<h1>一级标题(每页仅一个,SEO 最重要)</h1>
<h2>二级标题</h2>
<h3>三级标题</h3>
<h4>四级标题</h4>
<h5>五级标题</h5>
<h6>六级标题</h6>
标题层级最佳实践:
<h1>每页只出现一次,描述页面核心主题- 标题层级不可跳级(h1 → h3 是错误的)
- 不要用标题元素来控制字体大小(那是 CSS 的职责)
3.4 文本块级元素
| 元素 | 作用 |
|---|---|
<p> |
段落 |
<blockquote> |
块级引用,cite 属性指向来源 |
<pre> |
预格式化文本(保留空白和换行) |
<hr> |
主题分隔线(空元素) |
<div> |
无语义的块级容器(最后选择) |
<details> |
可展开的详细信息容器 |
<summary> |
<details> 的可见摘要/标题 |
<dialog> |
模态框或弹窗 |
<menu> |
工具栏形式的命令列表 |
3.5 行内文本语义元素
| 元素 | 语义 | 渲染 |
|---|---|---|
<a> |
超链接 | 蓝色下划线 |
<strong> |
重要性(语义强调) | 粗体 |
<em> |
重音强调 | 斜体 |
<b> |
无额外重要性的粗体(如关键词) | 粗体 |
<i> |
技术术语、外语、思想等 | 斜体 |
<u> |
不清晰表达的标注 | 下划线 |
<s> |
不再准确的内容 | 删除线 |
<del> |
已删除的内容(带语义) | 删除线 |
<ins> |
新增的内容(带语义) | 下划线 |
<mark> |
标记/高亮文本 | 黄色背景 |
<small> |
小注、版权信息 | 小字体 |
<sub> |
下标 | H₂O |
<sup> |
上标 | E=mc² |
<abbr> |
缩写,title 属性提供全称 |
点状下划线 |
<cite> |
作品名称 | 斜体 |
<q> |
行内引用 | 自动加引号 |
<dfn> |
定义术语 | 斜体 |
<code> |
行内代码 | 等宽字体 |
<kbd> |
键盘输入 | 等宽字体 |
<samp> |
程序输出示例 | 等宽字体 |
<var> |
变量名 | 斜体 |
<time> |
时间/日期,datetime 属性为机器可读格式 |
普通文本 |
<data> |
机器可读数据,value 属性 |
普通文本 |
<span> |
无语义的行内容器 | 无样式 |
<bdi> |
双向文本隔离(阿拉伯语、希伯来语等) | 普通文本 |
<bdo> |
显式指定文本方向 | 普通文本 |
<ruby> <rt> <rp> |
注音标记(CJK 注音) | 小号上标 |
<wbr> |
建议换行位置 | 无可见效果 |
<br> |
强制换行(空元素) | 换行 |
3.6 列表元素
html
<!-- 无序列表 -->
<ul>
<li>苹果</li>
<li>香蕉</li>
</ul>
<!-- 有序列表 -->
<ol start="3" reversed>
<li>第三项</li>
<li value="10">第十项</li>
</ol>
<!-- 描述列表(术语-定义对) -->
<dl>
<dt>HTML</dt>
<dd>超文本标记语言,网页结构的基础</dd>
<dt>CSS</dt>
<dd>层叠样式表,控制页面的外观和布局</dd>
</dl>
3.7 表格元素
html
<table>
<caption>2025年销售数据</caption>
<colgroup>
<col style="width: 40%">
<col span="2" style="width: 30%">
</colgroup>
<thead>
<tr>
<th scope="col">产品</th>
<th scope="col">Q1</th>
<th scope="col">Q2</th>
</tr>
</thead>
<tbody>
<tr>
<td>产品A</td>
<td>1200</td>
<td>1500</td>
</tr>
</tbody>
<tfoot>
<tr>
<th scope="row">合计</th>
<td>1200</td>
<td>1500</td>
</tr>
</tfoot>
</table>
表格元素全览:
| 元素 | 作用 |
|---|---|
<table> |
表格容器 |
<caption> |
表格标题(首个子元素) |
<colgroup> |
列组(用于列样式) |
<col> |
单列定义(空元素) |
<thead> |
表头行组 |
<tbody> |
表体行组(可多个) |
<tfoot> |
表脚行组 |
<tr> |
表行 |
<th> |
表头单元格;scope="row/col/rowgroup/colgroup" |
<td> |
数据单元格;colspan rowspan 合并单元格 |
3.8 嵌入元素
| 元素 | 用途 | 关键属性 |
|---|---|---|
<img> |
嵌入图片 | src alt width height loading srcset sizes |
<picture> |
响应式图片容器 | 包含 <source> 和备用 <img> |
<source> |
媒体/图片条件来源 | srcset media type |
<video> |
嵌入视频 | src controls autoplay muted loop poster |
<audio> |
嵌入音频 | src controls autoplay muted loop |
<track> |
字幕轨道(用于 video/audio) | kind src srclang label default |
<canvas> |
2D/WebGL 绘图画布 | width height |
<svg> |
内联 SVG 矢量图 | --- |
<math> |
MathML 数学公式 | --- |
<iframe> |
嵌入外部页面 | src sandbox allow loading |
<embed> |
外部内容插件(不常用) | src type |
<object> |
嵌入外部对象 | data type |
<map> |
图片热点地图 | name |
<area> |
热点区域(空元素) | shape coords href alt |
3.9 脚本元素
html
<!-- 阻塞渲染(避免) -->
<script src="app.js"></script>
<!-- defer: HTML解析完后执行,不阻塞,保持顺序 -->
<script src="app.js" defer></script>
<!-- async: 下载完就执行,不等待HTML,不保证顺序 -->
<script src="analytics.js" async></script>
<!-- type="module": 自动defer,支持ES模块 -->
<script type="module" src="main.js"></script>
<!-- 内联脚本 -->
<script>
console.log('Hello');
</script>
<!-- 内联模板 -->
<template id="my-template">
<p>这段内容不会被渲染,可被JavaScript克隆使用</p>
</template>
<!-- 自定义元素定义占位 -->
<slot name="header">默认内容</slot>
四、语义化 HTML5
4.1 为什么语义化很重要
html
<!-- ❌ 非语义化(旧式写法) -->
<div id="header">
<div class="nav">...</div>
</div>
<div class="main-content">
<div class="article">
<div class="article-title">...</div>
</div>
</div>
<div id="footer">...</div>
<!-- ✅ 语义化(现代写法) -->
<header>
<nav>...</nav>
</header>
<main>
<article>
<h1>...</h1>
</article>
</main>
<footer>...</footer>
语义化的四大价值:
- 可访问性:屏幕阅读器能识别地标区域,帮助视障用户导航
- SEO:搜索引擎更准确理解内容结构和权重
- 可维护性:代码可读性强,团队协作更高效
- 样式默认行为:浏览器为语义元素提供合理的默认样式
4.2 典型页面语义结构
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>...</head>
<body>
<!-- 网站全局页眉 -->
<header>
<a href="/" aria-label="网站首页">
<img src="logo.svg" alt="公司名称">
</a>
<nav aria-label="主导航">
<ul>
<li><a href="/products">产品</a></li>
<li><a href="/about">关于我们</a></li>
<li><a href="/contact">联系我们</a></li>
</ul>
</nav>
<!-- 页内搜索 -->
<search>
<form action="/search" method="get">
<label for="q">搜索</label>
<input type="search" id="q" name="q">
<button type="submit">搜索</button>
</form>
</search>
</header>
<!-- 页面主内容(每页唯一) -->
<main id="main-content">
<!-- 英雄区域 -->
<section aria-labelledby="hero-heading">
<h1 id="hero-heading">欢迎来到我们的网站</h1>
<p>这里是介绍文字。</p>
</section>
<!-- 文章内容 -->
<article>
<header>
<h2>文章标题</h2>
<p>
<time datetime="2025-03-30">2025年3月30日</time>
by <address><a href="/author/zhang">张三</a></address>
</p>
</header>
<section>
<h3>第一部分</h3>
<p>内容...</p>
<figure>
<img src="chart.png" alt="展示Q1销售数据的柱状图">
<figcaption>图1:2025年Q1销售数据</figcaption>
</figure>
</section>
<footer>
<p>标签:<a href="/tag/html">HTML</a></p>
</footer>
</article>
<!-- 相关内容(侧边栏) -->
<aside aria-label="相关文章">
<h2>你可能感兴趣</h2>
<ul>
<li><a href="/post/1">相关文章1</a></li>
<li><a href="/post/2">相关文章2</a></li>
</ul>
</aside>
</main>
<!-- 网站全局页脚 -->
<footer>
<nav aria-label="页脚导航">
<a href="/privacy">隐私政策</a>
<a href="/terms">使用条款</a>
</nav>
<p><small>© 2025 公司名称. All rights reserved.</small></p>
</footer>
</body>
</html>
4.3 常见语义误用纠错
| 误用 | 正确用法 | 原因 |
|---|---|---|
用 <table> 做布局 |
用 CSS Flexbox/Grid | 表格语义是"表格数据" |
<br> 用于段落间距 |
用 <p> 分段 + CSS margin |
<br> 只是换行,不表示段落 |
<b> 代替 <strong> |
加粗用 <strong>(重要语义)或 CSS |
<b> 只是视觉粗体,无语义 |
<i> 代替 <em> |
强调用 <em>,技术术语/书名才用 <i> |
同上 |
<h1> 到处用来"大标题" |
严格按层级使用 h1-h6 |
破坏文档大纲,影响 SEO 和无障碍 |
<div> 堆满导航 |
用 <nav> 包裹主导航链接集合 |
失去地标导航能力 |
<section> 当通用容器 |
无语义容器用 <div> |
<section> 必须有标题 |
4.4 <details> 和 <dialog> 实用示例
html
<!-- 无需 JavaScript 的折叠内容 -->
<details>
<summary>点击展开详细说明</summary>
<p>这里是隐藏的详细内容,点击上方 summary 可以切换显示/隐藏。</p>
</details>
<!-- 原生模态框 -->
<dialog id="myDialog">
<h2>确认操作</h2>
<p>确定要删除此记录吗?</p>
<form method="dialog">
<button value="cancel">取消</button>
<button value="confirm">确认</button>
</form>
</dialog>
<button onclick="document.getElementById('myDialog').showModal()">
打开模态框
</button>
五、表单与输入控件
5.1 表单容器属性
html
<form
action="/submit" <!-- 提交目标 URL -->
method="post" <!-- GET 或 POST -->
enctype="multipart/form-data" <!-- 上传文件时必须设置 -->
novalidate <!-- 禁用浏览器内置验证(自定义验证时用) -->
autocomplete="on" <!-- 是否启用自动完成 -->
target="_blank" <!-- 提交结果在新标签打开 -->
>
5.2 所有 <input> 类型
html
<!-- 文字类 -->
<input type="text"> <!-- 普通文本 -->
<input type="password"> <!-- 密码(隐藏字符) -->
<input type="email"> <!-- 邮箱(自动验证格式) -->
<input type="url"> <!-- URL(自动验证格式) -->
<input type="tel"> <!-- 电话号码(移动端弹出数字键盘) -->
<input type="search"> <!-- 搜索框(有清除按钮) -->
<input type="number"> <!-- 数字(有上下箭头) -->
<!-- 时间日期类 -->
<input type="date"> <!-- 日期选择器 -->
<input type="time"> <!-- 时间选择器 -->
<input type="datetime-local"> <!-- 本地日期时间选择器 -->
<input type="month"> <!-- 月份选择器 -->
<input type="week"> <!-- 周选择器 -->
<!-- 选择类 -->
<input type="checkbox"> <!-- 复选框 -->
<input type="radio"> <!-- 单选框 -->
<input type="range"> <!-- 滑块(min/max/step) -->
<input type="color"> <!-- 颜色选择器 -->
<!-- 文件 -->
<input type="file"> <!-- 文件选择;multiple 多文件;accept 过滤类型 -->
<input type="file" multiple accept="image/*,.pdf">
<!-- 隐藏 -->
<input type="hidden" name="token" value="abc123">
<!-- 按钮类(推荐用 <button> 替代) -->
<input type="submit" value="提交">
<input type="reset" value="重置">
<input type="button" value="按钮">
<input type="image" src="btn.png" alt="图片按钮">
5.3 常用输入属性
html
<input
type="text"
id="username"
name="username" <!-- 提交时的 key,必须有 -->
value="默认值"
placeholder="请输入用户名(3-20字符)"
required <!-- 必填 -->
disabled <!-- 禁用(不提交) -->
readonly <!-- 只读(会提交) -->
autofocus <!-- 页面加载后自动聚焦 -->
autocomplete="username" <!-- 自动填充提示 -->
minlength="3" <!-- 最小字符数 -->
maxlength="20" <!-- 最大字符数 -->
pattern="[a-zA-Z0-9_]+" <!-- 正则验证 -->
title="只允许字母、数字和下划线" <!-- 验证失败时的提示 -->
spellcheck="true" <!-- 开启拼写检查 -->
inputmode="numeric" <!-- 移动端键盘类型:numeric/email/tel/url/decimal -->
tabindex="1" <!-- Tab 键顺序 -->
form="form-id" <!-- 关联到指定 form(不在 form 内时) -->
list="suggestions" <!-- 关联 datalist -->
>
<!-- 数字类特有 -->
<input type="number" min="0" max="100" step="5">
<input type="range" min="0" max="100" step="1" value="50">
<!-- 文件类特有 -->
<input type="file" accept=".jpg,.png,image/*" multiple capture="camera">
5.4 完整表单示例(含验证与无障碍)
html
<form action="/register" method="post" novalidate id="registerForm">
<!-- 姓名 -->
<div class="form-group">
<label for="fullname">
姓名 <abbr title="必填" aria-label="必填">*</abbr>
</label>
<input
type="text"
id="fullname"
name="fullname"
required
minlength="2"
maxlength="50"
autocomplete="name"
aria-required="true"
aria-describedby="fullname-hint fullname-error"
>
<span id="fullname-hint" class="hint">2-50个字符</span>
<span id="fullname-error" class="error" role="alert" hidden>
请输入有效的姓名
</span>
</div>
<!-- 邮箱 -->
<div class="form-group">
<label for="email">邮箱地址 <abbr title="必填">*</abbr></label>
<input
type="email"
id="email"
name="email"
required
autocomplete="email"
aria-required="true"
aria-describedby="email-error"
>
<span id="email-error" class="error" role="alert" hidden>
请输入有效的邮箱地址
</span>
</div>
<!-- 密码 -->
<div class="form-group">
<label for="password">密码 <abbr title="必填">*</abbr></label>
<input
type="password"
id="password"
name="password"
required
minlength="8"
pattern="^(?=.*[a-z])(?=.*[A-Z])(?=.*\d).{8,}$"
autocomplete="new-password"
aria-required="true"
aria-describedby="password-hint"
>
<span id="password-hint" class="hint">
至少8位,包含大小写字母和数字
</span>
</div>
<!-- 手机号 -->
<div class="form-group">
<label for="phone">手机号码</label>
<input
type="tel"
id="phone"
name="phone"
pattern="1[3-9]\d{9}"
placeholder="13812345678"
autocomplete="tel"
inputmode="numeric"
aria-describedby="phone-hint"
>
<span id="phone-hint" class="hint">选填,格式:1XXXXXXXXXX</span>
</div>
<!-- 出生日期 -->
<div class="form-group">
<label for="birthday">出生日期</label>
<input
type="date"
id="birthday"
name="birthday"
min="1900-01-01"
max="2010-12-31"
>
</div>
<!-- 性别(单选) -->
<fieldset>
<legend>性别</legend>
<label>
<input type="radio" name="gender" value="male"> 男
</label>
<label>
<input type="radio" name="gender" value="female"> 女
</label>
<label>
<input type="radio" name="gender" value="other"> 其他
</label>
</fieldset>
<!-- 兴趣爱好(复选) -->
<fieldset>
<legend>兴趣爱好(可多选)</legend>
<label><input type="checkbox" name="hobby" value="reading"> 阅读</label>
<label><input type="checkbox" name="hobby" value="coding"> 编程</label>
<label><input type="checkbox" name="hobby" value="sports"> 运动</label>
</fieldset>
<!-- 城市(下拉) -->
<div class="form-group">
<label for="city">所在城市</label>
<select id="city" name="city">
<option value="">-- 请选择 --</option>
<optgroup label="华东">
<option value="shanghai">上海</option>
<option value="hangzhou">杭州</option>
</optgroup>
<optgroup label="华南">
<option value="guangzhou">广州</option>
<option value="shenzhen">深圳</option>
</optgroup>
</select>
</div>
<!-- 简介(多行文本) -->
<div class="form-group">
<label for="bio">个人简介</label>
<textarea
id="bio"
name="bio"
rows="4"
maxlength="500"
placeholder="介绍一下自己(选填,不超过500字)"
></textarea>
</div>
<!-- 头像上传 -->
<div class="form-group">
<label for="avatar">头像</label>
<input
type="file"
id="avatar"
name="avatar"
accept="image/jpeg,image/png,image/webp"
>
</div>
<!-- 协议同意 -->
<div class="form-group">
<label>
<input type="checkbox" name="agree" required aria-required="true">
我已阅读并同意 <a href="/terms" target="_blank">使用条款</a>
</label>
</div>
<!-- 操作按钮 -->
<div class="form-actions">
<button type="submit">注册</button>
<button type="reset">重置</button>
<button type="button" onclick="history.back()">取消</button>
</div>
</form>
<!-- 带自动补全的输入 -->
<input list="browsers" name="browser" id="browser">
<datalist id="browsers">
<option value="Chrome">
<option value="Firefox">
<option value="Safari">
<option value="Edge">
</datalist>
5.5 表单验证 JavaScript 示例
javascript
const form = document.getElementById('registerForm');
form.addEventListener('submit', (e) => {
e.preventDefault();
let isValid = true;
// 清除旧错误
form.querySelectorAll('.error').forEach(el => el.hidden = true);
form.querySelectorAll('input').forEach(el => {
el.removeAttribute('aria-invalid');
});
// 验证每个字段
const fields = form.querySelectorAll('[required]');
fields.forEach(field => {
if (!field.validity.valid) {
const errorId = field.getAttribute('aria-describedby')
?.split(' ')
.find(id => id.endsWith('-error'));
if (errorId) {
const errorEl = document.getElementById(errorId);
if (errorEl) {
errorEl.textContent = getErrorMessage(field);
errorEl.hidden = false;
}
}
field.setAttribute('aria-invalid', 'true');
isValid = false;
}
});
if (isValid) {
form.submit();
} else {
// 聚焦到第一个错误字段
form.querySelector('[aria-invalid="true"]')?.focus();
}
});
function getErrorMessage(field) {
if (field.validity.valueMissing) return '此字段为必填项';
if (field.validity.typeMismatch) return `请输入有效的${field.type}格式`;
if (field.validity.tooShort) return `最少需要 ${field.minLength} 个字符`;
if (field.validity.tooLong) return `最多允许 ${field.maxLength} 个字符`;
if (field.validity.patternMismatch) return field.title || '格式不正确';
if (field.validity.rangeUnderflow) return `最小值为 ${field.min}`;
if (field.validity.rangeOverflow) return `最大值为 ${field.max}`;
return '输入内容无效';
}
六、多媒体元素
6.1 图片优化
html
<!-- 基础图片(always 必须有 alt) -->
<img src="photo.jpg" alt="描述图片内容的简短文字" width="800" height="600">
<!-- 纯装饰性图片(空 alt,屏幕阅读器跳过) -->
<img src="divider.png" alt="">
<!-- 响应式图片(不同尺寸) -->
<img
src="photo-800.jpg"
srcset="photo-400.jpg 400w,
photo-800.jpg 800w,
photo-1600.jpg 1600w"
sizes="(max-width: 480px) 100vw,
(max-width: 900px) 50vw,
800px"
alt="响应式图片示例"
loading="lazy"
decoding="async"
fetchpriority="low"
>
<!-- 艺术方向图片(不同设备展示完全不同构图) -->
<picture>
<!-- 移动端:竖向裁剪 -->
<source
srcset="hero-portrait.webp"
media="(max-width: 767px)"
type="image/webp"
>
<!-- 桌面端:横向全图 -->
<source
srcset="hero-landscape.webp"
media="(min-width: 768px)"
type="image/webp"
>
<!-- 备用(不支持 WebP 或 picture 的浏览器) -->
<img src="hero-landscape.jpg" alt="网站英雄图" loading="eager" fetchpriority="high">
</picture>
<!-- 图片配说明 -->
<figure>
<img src="chart.svg" alt="2025年Q1-Q4各地区销售对比柱状图">
<figcaption>图1:2025年各季度各地区销售数据对比</figcaption>
</figure>
图片属性速查:
| 属性 | 值 | 说明 |
|---|---|---|
alt |
文字描述 | 必须存在;装饰图用 "" |
loading |
lazy / eager |
懒加载(首屏用 eager) |
decoding |
async / sync / auto |
异步解码提升性能 |
fetchpriority |
high / low / auto |
LCP 图片用 high |
width / height |
数字(像素) | 必须设置,防止 CLS 布局偏移 |
crossorigin |
anonymous / use-credentials |
跨域图片资源 |
6.2 视频
html
<video
controls
width="1280"
height="720"
poster="thumbnail.jpg"
preload="metadata"
playsinline
>
<!-- 多格式支持(浏览器选第一个支持的) -->
<source src="video.webm" type="video/webm">
<source src="video.mp4" type="video/mp4">
<!-- 字幕(无障碍必须) -->
<track kind="subtitles" src="subs-zh.vtt" srclang="zh" label="中文" default>
<track kind="subtitles" src="subs-en.vtt" srclang="en" label="English">
<track kind="captions" src="cc-zh.vtt" srclang="zh" label="中文字幕(无障碍)">
<track kind="chapters" src="chapters.vtt" srclang="zh" label="章节">
<!-- 浏览器不支持时显示 -->
<p>您的浏览器不支持视频播放,请<a href="video.mp4">下载视频</a>。</p>
</video>
常用 video 属性:
| 属性 | 说明 |
|---|---|
controls |
显示播放控件 |
autoplay |
自动播放(须配合 muted) |
muted |
静音(自动播放的前提) |
loop |
循环播放 |
poster |
封面图 URL |
preload |
none/metadata/auto |
playsinline |
iOS 内联播放(不全屏) |
6.3 音频
html
<audio controls preload="metadata">
<source src="podcast.ogg" type="audio/ogg">
<source src="podcast.mp3" type="audio/mpeg">
您的浏览器不支持音频播放。
</audio>
七、HTML5 核心 API
7.1 Canvas API(2D 绘图)
javascript
// HTML
// <canvas id="myCanvas" width="800" height="600"></canvas>
const canvas = document.getElementById('myCanvas');
const ctx = canvas.getContext('2d');
// === 矩形 ===
ctx.fillStyle = '#3498db';
ctx.fillRect(10, 10, 200, 100); // 实心矩形
ctx.strokeStyle = '#e74c3c';
ctx.lineWidth = 3;
ctx.strokeRect(220, 10, 200, 100); // 空心矩形
ctx.clearRect(50, 30, 100, 50); // 清除区域
// === 路径 ===
ctx.beginPath();
ctx.moveTo(50, 200);
ctx.lineTo(200, 200);
ctx.lineTo(125, 100);
ctx.closePath();
ctx.fillStyle = 'rgba(46, 204, 113, 0.8)';
ctx.fill();
ctx.stroke();
// === 圆弧 ===
ctx.beginPath();
ctx.arc(400, 150, 80, 0, Math.PI * 2); // 圆形
ctx.fillStyle = '#9b59b6';
ctx.fill();
// === 文字 ===
ctx.font = 'bold 24px "Microsoft YaHei", sans-serif';
ctx.fillStyle = '#2c3e50';
ctx.textAlign = 'center';
ctx.fillText('Hello Canvas!', 400, 400);
// === 渐变 ===
const gradient = ctx.createLinearGradient(0, 500, 800, 500);
gradient.addColorStop(0, '#f39c12');
gradient.addColorStop(1, '#e74c3c');
ctx.fillStyle = gradient;
ctx.fillRect(0, 480, 800, 80);
// === 图片 ===
const img = new Image();
img.onload = () => ctx.drawImage(img, 600, 100, 150, 100);
img.src = 'photo.jpg';
// === 变换 ===
ctx.save();
ctx.translate(400, 300);
ctx.rotate(Math.PI / 4); // 45度
ctx.scale(1.5, 0.8);
ctx.fillRect(-50, -30, 100, 60);
ctx.restore();
// === 动画(requestAnimationFrame)===
let x = 0;
function animate() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.fillStyle = '#3498db';
ctx.fillRect(x, 300, 50, 50);
x = (x + 2) % canvas.width;
requestAnimationFrame(animate);
}
animate();
7.2 Geolocation API(地理位置)
javascript
// 判断支持
if (!navigator.geolocation) {
alert('浏览器不支持地理位置功能');
}
// 一次性获取
navigator.geolocation.getCurrentPosition(
// 成功回调
(position) => {
const { latitude, longitude, accuracy, altitude } = position.coords;
const timestamp = new Date(position.timestamp);
console.log(`纬度: ${latitude}`);
console.log(`经度: ${longitude}`);
console.log(`精度: ${accuracy} 米`);
},
// 失败回调
(error) => {
const messages = {
1: '用户拒绝了位置权限',
2: '无法获取位置信息',
3: '请求超时'
};
console.error(messages[error.code] || '未知错误');
},
// 选项
{
enableHighAccuracy: true, // 高精度(耗电)
timeout: 5000, // 超时毫秒数
maximumAge: 60000 // 缓存有效期(毫秒)
}
);
// 持续监听(追踪位置变化)
const watchId = navigator.geolocation.watchPosition(
(pos) => console.log('位置更新:', pos.coords),
(err) => console.error(err)
);
// 停止监听
navigator.geolocation.clearWatch(watchId);
7.3 Web Storage API(本地存储)
javascript
// ======== localStorage(永久存储,关闭浏览器也保留)========
// 存储(值必须是字符串)
localStorage.setItem('theme', 'dark');
localStorage.setItem('user', JSON.stringify({ name: '张三', age: 30 }));
// 读取
const theme = localStorage.getItem('theme'); // 'dark'
const user = JSON.parse(localStorage.getItem('user')); // {name:'张三',age:30}
// 删除
localStorage.removeItem('theme');
localStorage.clear(); // 清空所有
// 遍历
for (let i = 0; i < localStorage.length; i++) {
const key = localStorage.key(i);
const value = localStorage.getItem(key);
console.log(key, value);
}
// ======== sessionStorage(会话存储,关闭标签页即清除)========
sessionStorage.setItem('formData', JSON.stringify({ step: 2 }));
const formData = JSON.parse(sessionStorage.getItem('formData'));
// ======== 封装工具函数(带过期时间)========
const storage = {
set(key, value, ttl = null) {
const item = { value, expiry: ttl ? Date.now() + ttl : null };
localStorage.setItem(key, JSON.stringify(item));
},
get(key) {
const raw = localStorage.getItem(key);
if (!raw) return null;
const item = JSON.parse(raw);
if (item.expiry && Date.now() > item.expiry) {
localStorage.removeItem(key);
return null;
}
return item.value;
},
remove(key) { localStorage.removeItem(key); }
};
// 使用
storage.set('token', 'abc123', 7 * 24 * 60 * 60 * 1000); // 7天过期
const token = storage.get('token');
localStorage vs sessionStorage vs Cookie:
| 特性 | localStorage | sessionStorage | Cookie |
|---|---|---|---|
| 生命周期 | 永久(手动清除) | 标签页关闭即清除 | 可设过期时间 |
| 大小限制 | ~5MB | ~5MB | ~4KB |
| 服务器访问 | 否 | 否 | 是(随请求发送) |
| 跨标签页 | 共享 | 不共享 | 共享 |
| 跨域 | 否(同源) | 否(同源) | 可设 domain |
7.4 Web Workers API(后台线程)
javascript
// ======== 主线程 main.js ========
// 创建 Worker
const worker = new Worker('worker.js');
// 向 Worker 发送消息
worker.postMessage({ type: 'compute', data: 1000000 });
// 接收 Worker 返回的消息
worker.onmessage = (event) => {
console.log('Worker 计算结果:', event.data);
document.getElementById('result').textContent = event.data.result;
};
// 错误处理
worker.onerror = (error) => {
console.error('Worker 错误:', error.message);
};
// 终止 Worker
// worker.terminate();
// ======== Worker 线程 worker.js ========
self.onmessage = (event) => {
const { type, data } = event.data;
if (type === 'compute') {
// 耗时计算(不阻塞主线程 UI)
let sum = 0;
for (let i = 1; i <= data; i++) {
sum += i;
}
// 返回结果
self.postMessage({ result: sum, type: 'done' });
}
};
// ======== Shared Worker(多标签页共享)========
// const shared = new SharedWorker('shared-worker.js');
// shared.port.postMessage('hello');
// shared.port.onmessage = (e) => console.log(e.data);
7.5 WebSocket API(双向实时通信)
javascript
const ws = new WebSocket('wss://api.example.com/ws');
// 连接建立
ws.onopen = () => {
console.log('WebSocket 已连接');
ws.send(JSON.stringify({ type: 'auth', token: 'abc123' }));
};
// 接收消息
ws.onmessage = (event) => {
const data = JSON.parse(event.data);
console.log('收到消息:', data);
};
// 错误处理
ws.onerror = (error) => {
console.error('WebSocket 错误:', error);
};
// 连接关闭
ws.onclose = (event) => {
console.log(`连接关闭,代码: ${event.code},原因: ${event.reason}`);
// 断线重连
setTimeout(() => { /* 重新连接 */ }, 3000);
};
// 发送消息
ws.send(JSON.stringify({ type: 'message', text: '你好!' }));
ws.send(new Blob([data])); // 发送二进制数据
ws.send(new ArrayBuffer(8));
// 检查连接状态
// ws.readyState: 0=CONNECTING 1=OPEN 2=CLOSING 3=CLOSED
// 关闭连接
ws.close(1000, '正常关闭');
7.6 拖拽 API(Drag & Drop)
html
<!-- 可拖拽元素 -->
<div class="card" draggable="true" id="card1">
可拖拽的卡片
</div>
<!-- 放置区域 -->
<div class="dropzone" id="zone1">放置区域</div>
javascript
const cards = document.querySelectorAll('.card');
const zones = document.querySelectorAll('.dropzone');
// ===== 拖拽源事件 =====
cards.forEach(card => {
card.addEventListener('dragstart', (e) => {
e.dataTransfer.setData('text/plain', e.target.id);
e.dataTransfer.effectAllowed = 'move';
e.target.classList.add('dragging');
});
card.addEventListener('dragend', (e) => {
e.target.classList.remove('dragging');
});
});
// ===== 放置目标事件 =====
zones.forEach(zone => {
zone.addEventListener('dragover', (e) => {
e.preventDefault(); // 必须阻止默认行为才能放置
e.dataTransfer.dropEffect = 'move';
zone.classList.add('drag-over');
});
zone.addEventListener('dragleave', () => {
zone.classList.remove('drag-over');
});
zone.addEventListener('drop', (e) => {
e.preventDefault();
const id = e.dataTransfer.getData('text/plain');
const card = document.getElementById(id);
zone.appendChild(card);
zone.classList.remove('drag-over');
});
});
7.7 Fetch API(网络请求)
javascript
// GET 请求
const response = await fetch('https://api.example.com/data');
if (!response.ok) throw new Error(`HTTP ${response.status}`);
const data = await response.json();
// POST 请求(提交 JSON)
const result = await fetch('/api/users', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${token}`
},
body: JSON.stringify({ name: '张三', email: 'zhang@example.com' })
});
// 提交表单数据
const formData = new FormData(document.getElementById('myForm'));
await fetch('/upload', { method: 'POST', body: formData });
// 超时控制
const controller = new AbortController();
const timeout = setTimeout(() => controller.abort(), 5000);
try {
const res = await fetch('/api/slow', { signal: controller.signal });
} finally {
clearTimeout(timeout);
}
7.8 IntersectionObserver(懒加载 / 无限滚动)
javascript
// 图片懒加载
const lazyImages = document.querySelectorAll('img[data-src]');
const imageObserver = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
const img = entry.target;
img.src = img.dataset.src;
img.removeAttribute('data-src');
imageObserver.unobserve(img);
}
});
}, {
rootMargin: '200px 0px', // 提前200px开始加载
threshold: 0.01
});
lazyImages.forEach(img => imageObserver.observe(img));
html
<!-- 懒加载图片 HTML -->
<img
src="placeholder.svg"
data-src="real-photo.jpg"
alt="懒加载图片"
width="800"
height="600"
>
7.9 Web Audio API(音频处理)
javascript
const audioCtx = new AudioContext();
// 播放音频文件
const response = await fetch('sound.mp3');
const buffer = await response.arrayBuffer();
const decoded = await audioCtx.decodeAudioData(buffer);
const source = audioCtx.createBufferSource();
source.buffer = decoded;
// 增益控制(音量)
const gainNode = audioCtx.createGain();
gainNode.gain.value = 0.5;
// 连接音频图:source → gain → output
source.connect(gainNode);
gainNode.connect(audioCtx.destination);
source.start(0); // 立即播放
八、全局属性与数据属性
8.1 全局属性(所有 HTML 元素通用)
html
<!-- id: 文档内唯一标识符 -->
<div id="unique-id"></div>
<!-- class: 样式/JavaScript 钩子 -->
<div class="card featured active"></div>
<!-- style: 内联样式(尽量避免) -->
<p style="color: red; font-size: 1.2rem;"></p>
<!-- title: 鼠标悬停提示(工具提示) -->
<abbr title="HyperText Markup Language">HTML</abbr>
<!-- lang: 元素语言(覆盖文档语言) -->
<p lang="en">This is English.</p>
<p lang="ja">日本語のテキスト</p>
<!-- dir: 文字方向 -->
<p dir="rtl">هذا نص عربي</p> <!-- 从右到左 -->
<!-- tabindex: Tab 键顺序 -->
<div tabindex="0">可以通过 Tab 键聚焦的非交互元素</div>
<input tabindex="-1"> <!-- 从 Tab 顺序移除但可以编程聚焦 -->
<!-- hidden: 隐藏元素(等效 display:none,但语义更强) -->
<p hidden>此内容被隐藏</p>
<!-- contenteditable: 用户可直接编辑内容 -->
<div contenteditable="true">用户可以直接在这里编辑文本</div>
<!-- draggable: 是否可拖拽 -->
<img src="photo.jpg" draggable="true" alt="">
<!-- spellcheck: 拼写检查 -->
<textarea spellcheck="true"></textarea>
<!-- translate: 是否翻译 -->
<span translate="no">品牌名称不翻译</span>
<!-- accesskey: 键盘快捷键(Alt+key)-->
<button accesskey="s">保存 (Alt+S)</button>
<!-- inert: 禁用交互(HTML Living Standard 新增) -->
<div inert>
此区域内所有元素都不可交互(用于模态框后的内容遮罩)
</div>
<!-- popover: 弹出层(HTML Living Standard 新增) -->
<button popovertarget="my-popover">打开弹出层</button>
<div id="my-popover" popover>弹出内容</div>
8.2 自定义数据属性(data-*)
html
<!-- 在 HTML 中存储任意数据 -->
<article
data-article-id="42"
data-author="张三"
data-publish-date="2025-03-30"
data-tags="html,css,javascript"
data-is-featured="true"
>
<h2>文章标题</h2>
</article>
javascript
const article = document.querySelector('article');
// 读取(dataset 会自动将 kebab-case 转为 camelCase)
console.log(article.dataset.articleId); // "42"
console.log(article.dataset.author); // "张三"
console.log(article.dataset.publishDate); // "2025-03-30"
// 写入
article.dataset.views = '1250';
// 删除
delete article.dataset.isFeatured;
// CSS 选择器
// article[data-is-featured="true"] { border: 2px solid gold; }
九、可访问性(ARIA)
9.1 ARIA 基础概念
ARIA(Accessible Rich Internet Applications):W3C 规范,为辅助技术(屏幕阅读器等)添加额外的语义信息。
黄金原则 :优先使用原生 HTML 语义元素,仅在不够用时才用 ARIA 增强。
<button>永远优于<div role="button">。
9.2 ARIA 属性分类
① Landmark Roles(地标角色)
html
<!-- 对应语义元素的角色(优先用语义元素) -->
<header role="banner">...</header> <!-- 或 role="banner" -->
<nav role="navigation">...</nav>
<main role="main">...</main>
<aside role="complementary">...</aside>
<footer role="contentinfo">...</footer>
<form role="form">...</form>
<section role="region" aria-labelledby="sec1">...</section>
<search role="search">...</search>
② aria-label 系列(命名)
html
<!-- aria-label: 直接提供名称 -->
<button aria-label="关闭对话框">✕</button>
<nav aria-label="主导航">...</nav>
<nav aria-label="页脚导航">...</nav>
<!-- aria-labelledby: 引用已有文本作为名称 -->
<section aria-labelledby="products-heading">
<h2 id="products-heading">产品列表</h2>
</section>
<!-- aria-describedby: 提供额外描述 -->
<input aria-describedby="email-hint email-error">
<span id="email-hint">请使用工作邮箱</span>
<span id="email-error" role="alert">邮箱格式错误</span>
③ 状态属性
html
<!-- 表单验证状态 -->
<input aria-invalid="true"> <!-- 值无效 -->
<input aria-required="true"> <!-- 必填 -->
<input aria-readonly="true"> <!-- 只读 -->
<input aria-disabled="true"> <!-- 禁用 -->
<!-- 展开/折叠 -->
<button aria-expanded="false" aria-controls="menu">菜单</button>
<ul id="menu" hidden>...</ul>
<!-- 选中状态 -->
<button role="tab" aria-selected="true">标签1</button>
<button role="tab" aria-selected="false">标签2</button>
<li role="option" aria-selected="true">选中选项</li>
<!-- 按下状态 -->
<button aria-pressed="true">✔ 已收藏</button>
<!-- 当前状态 -->
<a href="/home" aria-current="page">首页</a> <!-- 当前页面 -->
<li aria-current="step">步骤2</li> <!-- 当前步骤 -->
<!-- 隐藏(对辅助技术不可见) -->
<span aria-hidden="true">★</span> 4.8分
<!-- 忙碌状态(加载中) -->
<div aria-busy="true" aria-live="polite">内容加载中...</div>
④ 实时区域(Live Regions)
html
<!-- 礼貌性通知(读完当前内容后播报) -->
<div aria-live="polite" aria-atomic="true" id="notification"></div>
<!-- 紧急通知(立即中断播报) -->
<div aria-live="assertive" role="alert" id="error-msg"></div>
<!-- 状态通知 -->
<div role="status" aria-live="polite">已保存 3 个更改</div>
javascript
// 动态注入通知内容,屏幕阅读器会自动播报
document.getElementById('notification').textContent = '表单提交成功!';
9.3 完整无障碍组件示例
html
<!-- 无障碍下拉菜单 -->
<nav aria-label="用户菜单">
<button
id="user-menu-btn"
aria-haspopup="true"
aria-expanded="false"
aria-controls="user-menu"
>
张三的账户 ▼
</button>
<ul
id="user-menu"
role="menu"
aria-labelledby="user-menu-btn"
hidden
>
<li role="none">
<a href="/profile" role="menuitem">个人资料</a>
</li>
<li role="none">
<a href="/settings" role="menuitem">设置</a>
</li>
<li role="none">
<button role="menuitem" onclick="logout()">退出登录</button>
</li>
</ul>
</nav>
<!-- 标签页(Tab) -->
<div>
<div role="tablist" aria-label="产品分类">
<button role="tab" aria-selected="true" id="tab-1" aria-controls="panel-1">手机</button>
<button role="tab" aria-selected="false" id="tab-2" aria-controls="panel-2">电脑</button>
<button role="tab" aria-selected="false" id="tab-3" aria-controls="panel-3">配件</button>
</div>
<div role="tabpanel" id="panel-1" aria-labelledby="tab-1">手机产品...</div>
<div role="tabpanel" id="panel-2" aria-labelledby="tab-2" hidden>电脑产品...</div>
<div role="tabpanel" id="panel-3" aria-labelledby="tab-3" hidden>配件产品...</div>
</div>
<!-- 进度条 -->
<div
role="progressbar"
aria-valuenow="65"
aria-valuemin="0"
aria-valuemax="100"
aria-valuetext="上传进度 65%"
style="width: 65%"
>
<span>65%</span>
</div>
9.4 键盘导航最佳实践
javascript
// Tab 键管理(焦点陷阱,用于模态框)
function trapFocus(modal) {
const focusable = modal.querySelectorAll(
'a, button, input, textarea, select, details, [tabindex]:not([tabindex="-1"])'
);
const first = focusable[0];
const last = focusable[focusable.length - 1];
modal.addEventListener('keydown', (e) => {
if (e.key !== 'Tab') return;
if (e.shiftKey) {
if (document.activeElement === first) {
last.focus();
e.preventDefault();
}
} else {
if (document.activeElement === last) {
first.focus();
e.preventDefault();
}
}
});
first.focus(); // 打开模态框时聚焦第一个可聚焦元素
}
// Esc 键关闭弹窗
document.addEventListener('keydown', (e) => {
if (e.key === 'Escape') closeModal();
});
十、SEO 与 Meta 标签
10.1 完整 SEO Meta 标签配置
html
<head>
<!-- ===== 基础必须项 ===== -->
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<!-- ===== 核心 SEO 标签 ===== -->
<!-- 标题:每页唯一,50-60字,包含主关键词 -->
<title>页面关键词 - 网站名称 | 副标题</title>
<!-- 描述:每页唯一,150-160字,包含关键词,吸引点击 -->
<meta name="description" content="这是一段吸引人点击的页面摘要,包含主要关键词,让用户一眼看出页面价值。建议在150-160字之间。">
<!-- 爬虫指令 -->
<meta name="robots" content="index, follow">
<!-- 不要索引或跟踪:<meta name="robots" content="noindex, nofollow"> -->
<!-- 规范 URL(防止重复内容) -->
<link rel="canonical" href="https://www.example.com/current-page/">
<!-- 多语言替代版本 -->
<link rel="alternate" hreflang="zh-CN" href="https://www.example.com/zh/page/">
<link rel="alternate" hreflang="en" href="https://www.example.com/en/page/">
<link rel="alternate" hreflang="x-default" href="https://www.example.com/page/">
<!-- ===== Open Graph(社交媒体分享)===== -->
<meta property="og:type" content="article">
<meta property="og:title" content="页面标题">
<meta property="og:description" content="页面描述(约200字)">
<meta property="og:image" content="https://example.com/og-image.jpg">
<!-- 推荐 1200×630 像素,≤8MB -->
<meta property="og:image:width" content="1200">
<meta property="og:image:height" content="630">
<meta property="og:image:alt" content="图片描述">
<meta property="og:url" content="https://example.com/page/">
<meta property="og:site_name" content="网站名称">
<meta property="og:locale" content="zh_CN">
<!-- 文章专用 Open Graph -->
<meta property="article:published_time" content="2025-03-30T08:00:00+08:00">
<meta property="article:modified_time" content="2025-03-30T10:00:00+08:00">
<meta property="article:author" content="https://example.com/author/zhang/">
<meta property="article:section" content="技术">
<meta property="article:tag" content="HTML5">
<!-- ===== Twitter Card ===== -->
<meta name="twitter:card" content="summary_large_image">
<meta name="twitter:site" content="@your_handle">
<meta name="twitter:creator" content="@author_handle">
<meta name="twitter:title" content="页面标题">
<meta name="twitter:description" content="页面描述">
<meta name="twitter:image" content="https://example.com/twitter-image.jpg">
<meta name="twitter:image:alt" content="图片描述">
<!-- ===== 结构化数据(JSON-LD)===== -->
<!-- 文章 Schema -->
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "Article",
"headline": "文章标题",
"description": "文章描述",
"image": ["https://example.com/photo1.jpg"],
"datePublished": "2025-03-30T08:00:00+08:00",
"dateModified": "2025-03-30T10:00:00+08:00",
"author": [{
"@type": "Person",
"name": "张三",
"url": "https://example.com/author/zhang"
}],
"publisher": {
"@type": "Organization",
"name": "网站名称",
"logo": {
"@type": "ImageObject",
"url": "https://example.com/logo.png"
}
}
}
</script>
<!-- FAQ Schema(可触发富文本搜索结果) -->
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "FAQPage",
"mainEntity": [
{
"@type": "Question",
"name": "HTML5 有哪些新特性?",
"acceptedAnswer": {
"@type": "Answer",
"text": "HTML5 引入了语义元素、Canvas、音视频、Web Storage、Web Workers 等大量新特性。"
}
}
]
}
</script>
<!-- 面包屑 Schema -->
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "BreadcrumbList",
"itemListElement": [
{"@type":"ListItem","position":1,"name":"首页","item":"https://example.com"},
{"@type":"ListItem","position":2,"name":"技术","item":"https://example.com/tech"},
{"@type":"ListItem","position":3,"name":"HTML5 指南"}
]
}
</script>
<!-- ===== 其他常用 Meta ===== -->
<meta name="author" content="张三">
<meta name="copyright" content="© 2025 公司名称">
<meta name="theme-color" content="#3498db"> <!-- 浏览器 UI 主题色 -->
<!-- PWA -->
<link rel="manifest" href="/manifest.json">
<meta name="mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="default">
<meta name="apple-mobile-web-app-title" content="App 名称">
</head>
10.2 SEO 标题最佳实践
| 标题格式 | 示例 |
|---|---|
| 关键词在前 | `HTML5 Canvas 教程 - 零基础学绘图 |
| 长度控制 | 桌面 ≤60字;移动端 ≤50字 |
| 每页唯一 | 禁止所有页面标题相同 |
| 品牌居后 | `页面内容 |
十一、性能优化最佳实践
11.1 资源加载优化
html
<head>
<!-- 1. 预连接(DNS + TCP + TLS,用于已知第三方域名)-->
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<!-- 2. DNS 预解析(仅 DNS,用于不确定是否用的域名)-->
<link rel="dns-prefetch" href="//cdn.example.com">
<!-- 3. 预加载关键资源(当前页面一定会用)-->
<link rel="preload" href="hero.jpg" as="image" fetchpriority="high">
<link rel="preload" href="main.css" as="style">
<link rel="preload" href="Inter.woff2" as="font" type="font/woff2" crossorigin>
<!-- 4. 预取(下一页可能用到,空闲时加载)-->
<link rel="prefetch" href="/next-page.html">
<link rel="prefetch" href="next-page-image.jpg" as="image">
<!-- 5. 样式表(阻塞渲染,最先加载)-->
<link rel="stylesheet" href="critical.css">
<!-- 6. 非关键 CSS 异步加载 -->
<link rel="preload" href="non-critical.css" as="style"
onload="this.onload=null;this.rel='stylesheet'">
<noscript><link rel="stylesheet" href="non-critical.css"></noscript>
</head>
<body>
<!-- 7. 首屏图片:不懒加载,高优先级 -->
<img
src="hero.jpg"
alt="首屏主图"
loading="eager"
fetchpriority="high"
width="1920"
height="1080"
>
<!-- 8. 非首屏图片:懒加载 -->
<img
src="photo.jpg"
alt="文章配图"
loading="lazy"
decoding="async"
width="800"
height="600"
>
<!-- 9. JavaScript:推迟执行(defer 保持顺序,async 不保证)-->
<script src="analytics.js" async></script>
<script src="app.js" defer></script>
<!-- 10. 关键 CSS 内联(首屏渲染无需等待外部文件)-->
<style>
/* 仅首屏关键样式,约 1-2KB -->
body { margin: 0; font-family: system-ui; }
header { ... }
.hero { ... }
</style>
</body>
11.2 响应式图片完整方案
html
<!-- 场景1:同一图片,不同分辨率(retina 适配)-->
<img
src="photo.jpg"
srcset="photo.jpg 1x, photo@2x.jpg 2x, photo@3x.jpg 3x"
alt="图片"
>
<!-- 场景2:同一图片,适配不同屏幕宽度 -->
<img
src="photo-800.jpg"
srcset="
photo-400.jpg 400w,
photo-800.jpg 800w,
photo-1200.jpg 1200w,
photo-2400.jpg 2400w
"
sizes="
(max-width: 480px) 100vw,
(max-width: 768px) 100vw,
(max-width: 1200px) 800px,
1200px
"
alt="响应式图片"
width="800"
height="600"
loading="lazy"
>
<!-- 场景3:艺术方向 + 格式降级 + 懒加载 -->
<picture>
<!-- 现代格式 AVIF(最小体积) -->
<source
type="image/avif"
srcset="hero-480.avif 480w, hero-800.avif 800w, hero-1200.avif 1200w"
sizes="(max-width: 768px) 100vw, 800px"
>
<!-- 次选格式 WebP -->
<source
type="image/webp"
srcset="hero-480.webp 480w, hero-800.webp 800w, hero-1200.webp 1200w"
sizes="(max-width: 768px) 100vw, 800px"
>
<!-- 最终备用 JPEG -->
<img
src="hero-800.jpg"
alt="英雄图片"
width="800"
height="450"
loading="lazy"
decoding="async"
>
</picture>
11.3 核心 Web 指标(Core Web Vitals)优化
| 指标 | 全称 | 目标值 | HTML 优化手段 |
|---|---|---|---|
| LCP | Largest Contentful Paint | < 2.5s | 预加载英雄图、内联关键 CSS、fetchpriority="high" |
| CLS | Cumulative Layout Shift | < 0.1 | 图片/视频设 width+height、避免动态注入内容 |
| INP | Interaction to Next Paint | < 200ms | defer JavaScript、使用 Web Workers |
| FCP | First Contentful Paint | < 1.8s | 减少渲染阻塞资源、关键 CSS 内联 |
| TTFB | Time to First Byte | < 800ms | 服务器优化、CDN、HTTP/2 |
11.4 HTML 代码规范与工具
html
<!-- ✅ 良好习惯 -->
<!-- 1. 属性值始终使用双引号 -->
<img src="photo.jpg" alt="图片描述">
<!-- 2. 布尔属性无需赋值 -->
<input required disabled readonly>
<!-- 而非 <input required="required"> -->
<!-- 3. 空元素无需自闭合斜杠(HTML5 不需要) -->
<br> <hr> <img src=""> <input type="text">
<!-- 而非 <br /> <hr /> -->
<!-- 4. 小写标签和属性名 -->
<div class="container"> <!-- 不是 <DIV CLASS="container"> -->
<!-- 5. 用 HTML 表达结构,CSS 控制样式 -->
<strong>重要文字</strong> <!-- 不要 <b style="font-weight:bold"> -->
验证工具:
- W3C 验证器:validator.w3.org
- Lighthouse(Chrome DevTools → Lighthouse 面板)
- axe DevTools(无障碍检查)
- PageSpeed Insights:pagespeed.web.dev
十二、完整页面模板
html
<!DOCTYPE html>
<html lang="zh-CN" dir="ltr">
<head>
<!-- 基础 -->
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<!-- 标题与描述 -->
<title>完整 HTML5 页面模板 | 示例网站</title>
<meta name="description" content="一个包含所有现代最佳实践的完整 HTML5 页面模板,涵盖语义结构、SEO、无障碍和性能优化。">
<!-- 规范 URL -->
<link rel="canonical" href="https://example.com/page/">
<!-- Open Graph -->
<meta property="og:type" content="website">
<meta property="og:title" content="完整 HTML5 页面模板">
<meta property="og:description" content="现代最佳实践的完整 HTML5 页面模板">
<meta property="og:image" content="https://example.com/og.jpg">
<meta property="og:url" content="https://example.com/page/">
<!-- 爬虫 -->
<meta name="robots" content="index, follow">
<!-- 主题色 -->
<meta name="theme-color" content="#2563eb">
<!-- 图标 -->
<link rel="icon" href="/favicon.svg" type="image/svg+xml">
<link rel="icon" href="/favicon-32x32.png" sizes="32x32">
<link rel="apple-touch-icon" href="/apple-touch-icon.png">
<link rel="manifest" href="/manifest.json">
<!-- 预连接 -->
<link rel="preconnect" href="https://fonts.googleapis.com">
<!-- 预加载关键资源 -->
<link rel="preload" href="/fonts/inter.woff2" as="font" type="font/woff2" crossorigin>
<!-- 关键 CSS 内联 -->
<style>
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
html { scroll-behavior: smooth; }
body { font-family: system-ui, -apple-system, sans-serif; line-height: 1.6; }
/* 跳过导航链接(无障碍)*/
.skip-link {
position: absolute; top: -100%; left: 1rem;
background: #000; color: #fff; padding: .5rem 1rem;
z-index: 9999; border-radius: 0 0 4px 4px;
}
.skip-link:focus { top: 0; }
</style>
<!-- 外部 CSS -->
<link rel="stylesheet" href="/styles/main.css">
<!-- 结构化数据 -->
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "WebPage",
"name": "完整 HTML5 页面模板",
"url": "https://example.com/page/",
"description": "现代最佳实践的完整 HTML5 页面模板",
"publisher": {
"@type": "Organization",
"name": "示例网站",
"logo": {"@type":"ImageObject","url":"https://example.com/logo.png"}
}
}
</script>
</head>
<body>
<!-- 跳过导航(屏幕阅读器和键盘用户的快捷入口)-->
<a class="skip-link" href="#main-content">跳过导航,直接到主内容</a>
<!-- 全局通知区域(无障碍)-->
<div id="notifications" aria-live="polite" aria-atomic="true"></div>
<!-- ===== 网站页眉 ===== -->
<header role="banner">
<div class="container">
<a href="/" aria-label="示例网站首页" class="logo">
<img src="/logo.svg" alt="示例网站" width="120" height="40">
</a>
<!-- 主导航 -->
<nav aria-label="主导航">
<button
id="nav-toggle"
aria-controls="main-nav"
aria-expanded="false"
aria-label="打开导航菜单"
class="nav-toggle"
>
<span></span><span></span><span></span>
</button>
<ul id="main-nav" role="list">
<li><a href="/" aria-current="page">首页</a></li>
<li><a href="/products">产品</a></li>
<li><a href="/blog">博客</a></li>
<li><a href="/about">关于</a></li>
<li><a href="/contact">联系我们</a></li>
</ul>
</nav>
<!-- 搜索 -->
<search>
<form action="/search" method="get" role="search">
<label for="search-input" class="visually-hidden">搜索</label>
<input
type="search"
id="search-input"
name="q"
placeholder="搜索..."
autocomplete="off"
>
<button type="submit" aria-label="执行搜索">
<svg aria-hidden="true" width="20" height="20"><!-- 搜索图标 --></svg>
</button>
</form>
</search>
</div>
</header>
<!-- ===== 面包屑 ===== -->
<nav aria-label="面包屑导航">
<ol class="breadcrumb">
<li><a href="/">首页</a></li>
<li><a href="/blog">博客</a></li>
<li aria-current="page">HTML5 完全指南</li>
</ol>
</nav>
<!-- ===== 主内容 ===== -->
<main id="main-content" tabindex="-1">
<!-- 英雄区域 -->
<section aria-labelledby="hero-heading" class="hero">
<div class="container">
<h1 id="hero-heading">HTML5 完全学习指南</h1>
<p class="hero-desc">从基础到进阶,掌握现代 Web 开发核心技术</p>
<div class="hero-actions">
<a href="#guide-start" class="btn btn-primary">开始学习</a>
<a href="/download" class="btn btn-secondary">下载 PDF</a>
</div>
</div>
<picture>
<source srcset="hero.avif" type="image/avif">
<source srcset="hero.webp" type="image/webp">
<img
src="hero.jpg"
alt=""
width="1920"
height="600"
loading="eager"
fetchpriority="high"
decoding="async"
>
</picture>
</section>
<!-- 主要内容区 -->
<div class="content-layout container" id="guide-start">
<!-- 文章内容 -->
<article aria-labelledby="article-heading">
<header>
<h2 id="article-heading">HTML5 核心知识</h2>
<p class="meta">
发布于 <time datetime="2025-03-30">2025年3月30日</time>
· 作者:<a href="/author/zhang" rel="author">张三</a>
· 预计阅读 <span>20 分钟</span>
</p>
</header>
<section aria-labelledby="sec-semantic">
<h3 id="sec-semantic">语义化结构</h3>
<p>HTML5 引入了大量语义化元素...</p>
<figure>
<img
src="semantic-layout.png"
alt="HTML5 页面典型语义化布局示意图,展示 header、nav、main、aside、footer 的位置关系"
width="800"
height="600"
loading="lazy"
decoding="async"
>
<figcaption>HTML5 页面语义化布局示意图</figcaption>
</figure>
</section>
<section aria-labelledby="sec-apis">
<h3 id="sec-apis">HTML5 API</h3>
<p>Canvas、Web Storage、Geolocation 等强大 API...</p>
<details>
<summary>Canvas API 代码示例</summary>
<pre><code>const ctx = canvas.getContext('2d');
ctx.fillStyle = 'blue';
ctx.fillRect(0, 0, 100, 100);</code></pre>
</details>
</section>
<footer>
<p>标签:
<a href="/tag/html5" rel="tag">HTML5</a>、
<a href="/tag/web" rel="tag">Web 开发</a>
</p>
<address>
如有问题请联系:<a href="mailto:zhang@example.com">zhang@example.com</a>
</address>
</footer>
</article>
<!-- 侧边栏 -->
<aside aria-label="相关内容">
<section>
<h2>目录</h2>
<nav aria-label="文章目录">
<ol>
<li><a href="#sec-semantic">语义化结构</a></li>
<li><a href="#sec-apis">HTML5 API</a></li>
</ol>
</nav>
</section>
<section>
<h2>相关文章</h2>
<ul>
<li><a href="/css3-guide">CSS3 完全指南</a></li>
<li><a href="/js-guide">JavaScript 深度解析</a></li>
</ul>
</section>
</aside>
</div>
</main>
<!-- ===== 网站页脚 ===== -->
<footer role="contentinfo">
<div class="container">
<div class="footer-grid">
<section>
<h2>关于我们</h2>
<p>致力于提供高质量的 Web 开发学习内容。</p>
</section>
<section>
<h2>快速链接</h2>
<nav aria-label="页脚导航">
<ul>
<li><a href="/privacy">隐私政策</a></li>
<li><a href="/terms">使用条款</a></li>
<li><a href="/sitemap.xml">网站地图</a></li>
<li><a href="/rss.xml" type="application/rss+xml">RSS 订阅</a></li>
</ul>
</nav>
</section>
<section>
<h2>社交媒体</h2>
<ul>
<li>
<a href="https://github.com/example" rel="noopener noreferrer" target="_blank">
GitHub <span class="visually-hidden">(在新标签页打开)</span>
</a>
</li>
</ul>
</section>
</div>
<p class="copyright">
<small>© <time datetime="2025">2025</time> 示例网站. 保留所有权利.</small>
</p>
</div>
</footer>
<!-- 回到顶部按钮 -->
<a
href="#"
id="back-to-top"
aria-label="回到页面顶部"
hidden
>↑</a>
<!-- JavaScript(底部,defer 或异步)-->
<script src="/js/app.js" defer></script>
</body>
</html>
附录:重要参考资源
| 资源 | URL | 说明 |
|---|---|---|
| HTML Living Standard | html.spec.whatwg.org | 官方最权威规范 |
| MDN Web Docs | developer.mozilla.org/zh-CN/docs/Web/HTML | 最全面的参考文档 |
| W3C WAI(无障碍) | www.w3.org/WAI | WCAG、ARIA 规范 |
| Can I Use | caniuse.com | 浏览器兼容性查询 |
| W3C 验证器 | validator.w3.org | HTML 语法验证 |
| PageSpeed Insights | pagespeed.web.dev | Core Web Vitals 检测 |
| Schema.org | schema.org | 结构化数据类型库 |
| WebAIM | webaim.org | 无障碍实践指南 |