本系列共 6 篇,从零到一构建完整前端知识体系:
① HTML5 全部知识点 + 项目搭建 → ② CSS3 全部知识点 → ③ JavaScript 基础语法 → ④ JavaScript 进阶(DOM/BOM/事件)→ ⑤ ES6+ 与现代工程化 → ⑥ 综合项目实战(TodoList + 数据可视化)
目录
- [HTML5 是什么](#HTML5 是什么)
- [文档骨架:DOCTYPE 与 head](#文档骨架:DOCTYPE 与 head)
- [语义化标签(HTML5 核心)](#语义化标签(HTML5 核心))
- 文本内容标签全解
- 列表与表格
- 超链接与图片
- [表单:HTML5 最重要的交互入口](#表单:HTML5 最重要的交互入口)
- [多媒体:audio / video / source](#多媒体:audio / video / source)
- [全局属性与 data-* 自定义数据](#全局属性与 data-* 自定义数据)
- [HTML5 新 API 概览](#HTML5 新 API 概览)
- 可访问性(a11y)
- 项目搭建实战:个人任务管理器骨架
- 本篇小结与练习
一、HTML5 是什么
HTML(HyperText Markup Language,超文本标记语言) 是网页的"骨架",它负责描述内容的结构与语义,而不是外观(外观交给 CSS),也不是行为(行为交给 JavaScript)。
三者分工:
| 技术 | 职责 | 类比 |
|---|---|---|
| HTML | 结构 / 语义 | 人的骨骼 |
| CSS | 表现 / 样式 | 人的皮肤衣着 |
| JavaScript | 行为 / 交互 | 人的肌肉神经 |
HTML5 是 HTML 的第五个大版本(2014 年正式定稿),相比 HTML4 带来的重大变化:
- 新增语义化标签 :
header、nav、main、article、section、aside、footer、figure等 - 新增表单控件类型 :
email、date、range、color等,以及原生校验 - 新增多媒体标签 :
audio、video,摆脱 Flash - 新增图形能力 :
canvas、svg - 新增存储与离线 :
localStorage、sessionStorage、IndexedDB、Application Cache - 新增一系列 JS API:地理定位、拖放、Web Worker、WebSocket、File API 等
重要观念 :HTML5 不是"一个标签语言",而是 HTML + CSS3 + JavaScript API 的统称技术集合。
二、文档骨架:DOCTYPE 与 head
一个标准 HTML5 文档的最小结构:
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="description" content="这是一个 HTML5 学习示例页面">
<meta name="keywords" content="HTML5,前端,教程">
<meta name="author" content="Your Name">
<title>HTML5 教程 - 第一篇</title>
<link rel="icon" href="./favicon.ico" type="image/x-icon">
<link rel="stylesheet" href="./css/style.css">
</head>
<body>
<!-- 页面内容 -->
<script src="./js/main.js" defer></script>
</body>
</html>
逐行拆解
1. <!DOCTYPE html>
文档类型声明,必须是文件第一行。它告诉浏览器用标准模式渲染。HTML5 的 doctype 极简,不再需要写一长串 DTD。
2. <html lang="zh-CN">
根元素。lang 属性非常重要:
- 影响屏幕阅读器的发音
- 影响浏览器自动翻译判断
- 影响 CSS 的
:lang()选择器与字体回退
常用值:zh-CN(简体中文)、en(英语)、ja(日语)。
3. <head> 中的 meta 标签
| meta | 作用 |
|---|---|
charset="UTF-8" |
字符编码,必须放在 head 最前面,否则中文乱码 |
viewport |
移动端视口设置,响应式必备 |
description |
SEO 描述,搜索引擎结果摘要 |
keywords |
关键词(现代 SEO 权重已很低) |
author |
作者 |
theme-color |
移动端浏览器地址栏颜色 |
http-equiv="refresh" |
定时刷新/跳转(不推荐) |
viewport 详细参数:
html
<!-- 标准写法:宽度等于设备宽度,初始缩放 1 倍 -->
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<!-- 禁止用户缩放(谨慎使用,影响可访问性) -->
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
4. <script> 的加载时机
html
<!-- 1. 普通:遇到即下载并执行,阻塞 HTML 解析 ❌ -->
<script src="a.js"></script>
<!-- 2. async:异步下载,下载完立即执行,执行顺序不确定 -->
<script src="a.js" async></script>
<!-- 3. defer:异步下载,等 HTML 解析完再按顺序执行 ✅ 推荐 -->
<script src="a.js" defer></script>
| 方式 | 下载 | 执行时机 | 顺序 |
|---|---|---|---|
| 默认 | 同步阻塞 | 立即 | 有序 |
async |
并行 | 下载完立即 | 无序 |
defer |
并行 | DOMContentLoaded 之前 | 有序 |
三、语义化标签(HTML5 核心)
这是 HTML5 最重要的思想:用正确的标签做正确的事 ,而不是满屏 <div>。
3.1 页面结构标签
html
<body>
<header> <!-- 页头:logo、标题、导航 -->
<h1>我的网站</h1>
<nav> <!-- 导航区 -->
<ul>
<li><a href="#home">首页</a></li>
<li><a href="#about">关于</a></li>
</ul>
</nav>
</header>
<main> <!-- 页面主内容,一个页面只应有一个 -->
<article> <!-- 独立完整的内容单元,如一篇博客 -->
<h2>文章标题</h2>
<p>正文......</p>
<section> <!-- 文章内的分节,通常带标题 -->
<h3>小节标题</h3>
<p>小节内容</p>
</section>
<figure> <!-- 图文组合 -->
<img src="chart.png" alt="销售趋势图">
<figcaption>图 1:2024 年销售趋势</figcaption>
</figure>
</article>
<aside> <!-- 侧边栏:相关内容、广告、推荐 -->
<h3>相关阅读</h3>
<ul><li><a href="#">文章 A</a></li></ul>
</aside>
</main>
<footer> <!-- 页脚:版权、备案、联系方式 -->
<p>© 2025 我的网站</p>
</footer>
</body>
3.2 各标签语义对比表
| 标签 | 语义 | 使用场景 | 能否嵌套 |
|---|---|---|---|
header |
头部 | 页面/文章/区块的头部 | 可多处 |
nav |
导航 | 主要链接组 | 可多处 |
main |
主体 | 页面唯一核心内容 | 只能一个 |
article |
独立文章 | 博客、新闻、评论、卡片 | 可嵌套 |
section |
章节 | 有主题的内容分组 | 可嵌套 |
aside |
侧边 | 补充内容 | 可多处 |
footer |
页脚 | 版权、相关链接 | 可多处 |
figure |
图文块 | 图 + 说明 | 可嵌套 |
figcaption |
图说明 | figure 的标题 | 只能放 figure 内 |
details |
折叠详情 | 问答、折叠面板 | 可嵌套 |
summary |
折叠标题 | details 的可见标题 | 只能放 details 内 |
mark |
高亮 | 搜索结果高亮 | 行内 |
time |
时间 | 日期时间,配 datetime |
行内 |
address |
联系信息 | 作者联系方式 | 块级 |
3.3 details / summary 原生折叠
html
<details open>
<summary>什么是语义化?</summary>
<p>用合适的标签表达内容含义,让机器和人都能理解。</p>
</details>
open 属性表示默认展开。这是零 JavaScript 实现的折叠面板。
3.4 <time> 与机器可读时间
html
<p>发布于 <time datetime="2025-01-15T09:30">2025 年 1 月 15 日 上午 9:30</time></p>
datetime 让搜索引擎和浏览器能解析出精确时间。
3.5 div 与 span 何时用?
div:无语义的块级容器,纯为布局/样式服务时使用span:无语义的行内容器,为局部文字加样式时使用
原则:能用语义标签就不用 div,但也不必为了语义而强行套壳。
四、文本内容标签全解
4.1 标题 h1 ~ h6
html
<h1>一级标题(页面主标题,建议只有一个)</h1>
<h2>二级标题</h2>
<h3>三级标题</h3>
<h4>四级标题</h4>
<h5>五级标题</h5>
<h6>六级标题</h6>
规则:
- 不要跳级(h1 后直接 h3 ❌)
- 不要用标题标签来"让字变大"(那是 CSS 的活)
- 标题层级构成页面的大纲(outline)
4.2 段落与换行
html
<p>这是一个段落。段落之间会自动有间距。</p>
<p>第一行<br>第二行(br 是强制换行,不产生新段落)</p>
<hr> <!-- 主题分隔线,语义上的分断 -->
br只用于同一段落内的换行(如诗歌、地址),不要用它来做段落间距。
4.3 行内语义标签
html
<strong>重要内容</strong> <!-- 语义:重要 → 默认加粗 -->
<b>关键词</b> <!-- 无语义,仅样式加粗 -->
<em>强调</em> <!-- 语义:强调 → 默认斜体 -->
<i>外来词/术语</i> <!-- 无语义,仅样式斜体 -->
<mark>高亮文本</mark> <!-- 标记,默认黄底 -->
<small>附属细则</small> <!-- 小字,如版权声明 -->
<del>已删除</del> <!-- 删除线,如原价 -->
<ins>新插入</ins> <!-- 下划线,如现价 -->
<sub>下标</sub> H<sub>2</sub>O
<sup>上标</sup> x<sup>2</sup>
<code>console.log()</code> <!-- 代码片段,等宽字体 -->
<kbd>Ctrl + C</kbd> <!-- 键盘输入 -->
<samp>输出结果</samp> <!-- 程序输出 -->
<var>变量名</var> <!-- 变量 -->
<abbr title="HyperText Markup Language">HTML</abbr> <!-- 缩写 -->
<cite>《JavaScript 高级程序设计》</cite> <!-- 作品名 -->
<q>行内引用</q> <!-- 短引用,自动加引号 -->
<blockquote cite="https://example.com">
块级引用,通常缩进显示。
</blockquote>
<pre>预格式化文本
空格 和
换行都会被保留</pre>
<code> + <pre> 组合是展示代码的标准方式:
html
<pre><code>
function greet(name) {
return `Hello, ${name}`;
}
</code></pre>
4.4 实体字符(Escape)
| 实体 | 显示 | 说明 |
|---|---|---|
< |
< | 小于号 |
> |
> | 大于号 |
& |
& | 和号 |
" |
" | 双引号 |
' |
' | 单引号 |
|
(空格) | 不换行空格 |
© |
© | 版权 |
® |
® | 注册商标 |
™ |
™ | 商标 |
× |
× | 乘号 |
÷ |
÷ | 除号 |
♥ |
♥ | 心形 |
→ |
→ | 右箭头 |
想显示
<div>这样的标签文本,必须写成<div>。
五、列表与表格
5.1 三种列表
html
<!-- 1. 无序列表 unordered list -->
<ul>
<li>苹果</li>
<li>香蕉</li>
</ul>
<!-- 2. 有序列表 ordered list -->
<ol start="3" reversed type="A">
<li>第三步</li>
<li>第二步</li>
<li>第一步</li>
</ol>
<!-- 3. 描述列表 description list -->
<dl>
<dt>HTML</dt> <!-- 术语 -->
<dd>超文本标记语言</dd> <!-- 描述 -->
<dt>CSS</dt>
<dd>层叠样式表</dd>
</dl>
<ol> 的属性:
type="1|A|a|I|i"编号类型start="3"起始值reversed倒序
列表嵌套(导航菜单常用):
html
<ul>
<li>前端
<ul>
<li>HTML</li>
<li>CSS</li>
</ul>
</li>
<li>后端</li>
</ul>
⚠️
<ul>/<ol>的直接子元素只能是<li>,不能塞<div>。
5.2 表格
html
<table>
<caption>2024 年季度销售表</caption>
<colgroup>
<col style="width: 120px">
<col span="2">
</colgroup>
<thead>
<tr>
<th scope="col">季度</th>
<th scope="col">销售额</th>
<th scope="col">同比</th>
</tr>
</thead>
<tbody>
<tr>
<th scope="row">Q1</th>
<td>120 万</td>
<td>+8%</td>
</tr>
<tr>
<th scope="row">Q2</th>
<td>150 万</td>
<td>+12%</td>
</tr>
</tbody>
<tfoot>
<tr>
<td colspan="1">合计</td>
<td colspan="2">270 万</td>
</tr>
</tfoot>
</table>
关键属性:
colspan="2":横向合并 2 列rowspan="2":纵向合并 2 行scope="col|row":声明表头作用范围,无障碍必写caption:表格标题,必须紧跟<table>开始标签
HTML5 已废弃的表格属性 (不要用):border、cellpadding、cellspacing、width、align、bgcolor → 全部用 CSS 代替。
六、超链接与图片
6.1 <a> 超链接
html
<!-- 外部链接 -->
<a href="https://developer.mozilla.org" target="_blank" rel="noopener noreferrer">
MDN 文档
</a>
<!-- 站内链接 -->
<a href="./about.html">关于我们</a>
<a href="/products/list.html">产品列表</a>
<!-- 锚点跳转(页面内) -->
<a href="#section-2">跳到第二节</a>
<h2 id="section-2">第二节</h2>
<!-- 邮件 / 电话 -->
<a href="mailto:hi@example.com?subject=咨询&body=你好">发邮件</a>
<a href="tel:+8613800000000">拨打电话</a>
<!-- 下载 -->
<a href="./files/report.pdf" download="年度报告.pdf">下载报告</a>
target 取值:
| 值 | 含义 |
|---|---|
_self |
当前窗口(默认) |
_blank |
新标签页 |
_parent |
父框架 |
_top |
顶层窗口 |
安全提示 :
target="_blank"必须加rel="noopener noreferrer",防止新页面通过window.opener操控原页面(钓鱼攻击)。
6.2 <img> 图片
html
<img
src="./images/avatar.webp"
alt="用户头像:一位戴眼镜的男性"
width="200"
height="200"
loading="lazy"
decoding="async"
>
alt 属性是必须的,三种场景:
- 图片承载信息:写清图片内容(如"2024 年销售额增长 12% 的柱状图")
- 装饰性图片 :写
alt=""(空字符串),让屏幕阅读器跳过 - 图片是链接:写清链接目标(如"返回首页")
性能属性:
loading="lazy"懒加载,进入视口附近才下载decoding="async"异步解码width/height提前占位,防止 CLS(布局抖动)
6.3 响应式图片 srcset / sizes
html
<img
src="./img/photo-800.jpg"
srcset="./img/photo-400.jpg 400w,
./img/photo-800.jpg 800w,
./img/photo-1600.jpg 1600w"
sizes="(max-width: 600px) 100vw,
(max-width: 1200px) 50vw,
800px"
alt="示例照片"
>
srcset提供多个分辨率候选sizes告诉浏览器不同视口下图片的显示宽度- 浏览器自动选择最合适的那个下载
6.4 <picture> 艺术方向 / 格式回退
html
<picture>
<!-- 支持 AVIF 就用它 -->
<source srcset="./img/hero.avif" type="image/avif">
<!-- 支持 WebP 就用它 -->
<source srcset="./img/hero.webp" type="image/webp">
<!-- 竖屏时裁切成竖图 -->
<source media="(orientation: portrait)" srcset="./img/hero-portrait.jpg">
<!-- 兜底 -->
<img src="./img/hero.jpg" alt="产品主图">
</picture>
6.5 <figure> 图文组合
html
<figure>
<img src="./img/chart.png" alt="月度活跃用户趋势图">
<figcaption>图 2:2024 年月活用户增长情况</figcaption>
</figure>
七、表单:HTML5 最重要的交互入口
7.1 表单容器
html
<form action="/api/submit" method="post" enctype="multipart/form-data" novalidate>
...
</form>
| 属性 | 说明 |
|---|---|
action |
提交地址 |
method |
get(参数拼 URL) / post(请求体) |
enctype |
application/x-www-form-urlencoded(默认)/ multipart/form-data(含文件上传必用 )/ text/plain |
novalidate |
关闭浏览器原生校验 |
autocomplete |
on / off |
target |
提交后在哪打开响应 |
7.2 <label> 与表单控件的绑定
两种绑定方式:
html
<!-- 方式 1:for + id(推荐) -->
<label for="username">用户名</label>
<input type="text" id="username" name="username">
<!-- 方式 2:包裹 -->
<label>
密码
<input type="password" name="password">
</label>
绑定后,点击 label 文字即可聚焦输入框;屏幕阅读器也能正确朗读。表单必写 label。
7.3 <input> 的全部 type(HTML5 重点)
html
<!-- ===== 基础类型 ===== -->
<input type="text" name="nickname" placeholder="请输入昵称" maxlength="20">
<input type="password" name="pwd" placeholder="密码">
<input type="hidden" name="token" value="abc123">
<!-- ===== HTML5 新增类型 ===== -->
<input type="email" name="email" placeholder="you@example.com" multiple>
<input type="tel" name="phone" placeholder="13800000000" pattern="1[3-9]\d{9}">
<input type="url" name="site" placeholder="https://example.com">
<input type="number" name="age" min="1" max="120" step="1" value="18">
<input type="range" name="volume" min="0" max="100" step="5" value="50">
<input type="date" name="birthday">
<input type="time" name="meeting">
<input type="datetime-local" name="appointment">
<input type="month" name="month">
<input type="week" name="week">
<input type="color" name="theme" value="#3b82f6">
<input type="search" name="q" placeholder="搜索...">
<!-- ===== 选择类 ===== -->
<input type="checkbox" id="agree" name="agree" value="yes" checked>
<label for="agree">我已阅读并同意协议</label>
<input type="radio" id="male" name="gender" value="male" checked>
<label for="male">男</label>
<input type="radio" id="female" name="gender" value="female">
<label for="female">女</label>
<!-- ===== 文件 ===== -->
<input type="file" name="avatar" accept="image/png, image/jpeg" multiple>
<!-- ===== 按钮类 ===== -->
<input type="submit" value="提交">
<input type="reset" value="重置">
<input type="button" value="普通按钮">
注意 :
radio必须同name才能互斥;checkbox需要value才能提交有效值。
7.4 其他表单控件
html
<!-- 多行文本 -->
<textarea name="intro" rows="5" cols="40" placeholder="介绍一下自己" maxlength="200"></textarea>
<!-- 下拉选择 -->
<select name="city" multiple size="3">
<optgroup label="华北">
<option value="bj" selected>北京</option>
<option value="tj">天津</option>
</optgroup>
<optgroup label="华东">
<option value="sh">上海</option>
<option value="hz">杭州</option>
</optgroup>
</select>
<!-- 数据列表:输入框 + 候选建议 -->
<input list="browsers" name="browser">
<datalist id="browsers">
<option value="Chrome">
<option value="Firefox">
<option value="Safari">
</datalist>
<!-- 进度条 -->
<progress value="70" max="100">70%</progress>
<!-- 度量衡(如磁盘用量) -->
<meter value="0.7" min="0" max="1" low="0.3" high="0.8" optimum="0.2">70%</meter>
<!-- 按钮 -->
<button type="submit">提交</button>
<button type="reset">重置</button>
<button type="button" onclick="alert('hi')">点我</button>
<!-- 分组与说明 -->
<fieldset>
<legend>收货信息</legend>
<label>收件人 <input type="text" name="receiver"></label>
</fieldset>
⚠️
<button>在表单内默认 type="submit" ,若不希望提交必须显式写type="button",这是新手最常见的坑之一。
7.5 原生表单校验属性
html
<input type="text" name="user" required minlength="3" maxlength="10"
pattern="[A-Za-z0-9_]+" title="只能包含字母、数字和下划线">
<input type="email" name="email" required>
<input type="number" name="score" min="0" max="100" step="0.5">
<input type="password" name="pwd" required minlength="8">
| 属性 | 作用 |
|---|---|
required |
必填 |
minlength / maxlength |
字符长度 |
min / max / step |
数值范围与步长 |
pattern |
正则(隐式整体匹配 ,不用写 ^$) |
title |
校验失败时的提示语 |
autofocus |
自动聚焦 |
readonly |
只读(可提交) |
disabled |
禁用(不提交) |
autocomplete |
自动填充 |
inputmode |
移动端键盘类型,如 numeric |
用 JS 控制校验:
javascript
const input = document.querySelector('#username');
console.log(input.validity.valid); // 是否通过校验
console.log(input.validity.valueMissing); // 是否因 required 失败
console.log(input.validationMessage); // 浏览器给的提示文字
input.setCustomValidity('用户名已被占用'); // 自定义错误
input.setCustomValidity(''); // 清除自定义错误
7.6 表单提交行为
html
<!-- GET:参数拼在 URL 后,?a=1&b=2,不安全、有长度限制,适合搜索 -->
<form action="/search" method="get">...</form>
<!-- POST:参数在请求体,适合提交数据 -->
<form action="/api/user" method="post">...</form>
用 JS 阻止默认提交并自行处理(最常用):
javascript
const form = document.querySelector('#myForm');
form.addEventListener('submit', (e) => {
e.preventDefault(); // 阻止浏览器默认提交
const data = new FormData(form); // 收集所有带 name 的字段
console.log(Object.fromEntries(data));
// fetch('/api', { method: 'POST', body: data }) ...
});
八、多媒体:audio / video / source
8.1 <audio>
html
<audio controls preload="metadata" loop>
<source src="./media/song.mp3" type="audio/mpeg">
<source src="./media/song.ogg" type="audio/ogg">
您的浏览器不支持音频播放,<a href="./media/song.mp3">点击下载</a>。
</audio>
8.2 <video>
html
<video
controls
width="640"
height="360"
poster="./img/poster.jpg"
preload="metadata"
playsinline
muted
>
<source src="./media/demo.webm" type="video/webm">
<source src="./media/demo.mp4" type="video/mp4">
<track kind="subtitles" src="./media/demo.zh.vtt" srclang="zh" label="中文" default>
您的浏览器不支持视频播放。
</video>
关键属性表:
| 属性 | 说明 |
|---|---|
controls |
显示播放控件 |
autoplay |
自动播放(多数浏览器要求同时有 muted) |
muted |
静音 |
loop |
循环 |
preload |
none / metadata / auto |
poster |
视频封面图 |
playsinline |
iOS 内联播放,不自动全屏 |
crossorigin |
跨域设置 |
8.3 用 JS 控制播放
javascript
const video = document.querySelector('video');
video.play(); // 播放(返回 Promise)
video.pause(); // 暂停
video.currentTime = 30; // 跳到第 30 秒
video.volume = 0.5; // 音量 0~1
video.muted = true; // 静音
console.log(video.duration); // 总时长(秒)
console.log(video.paused); // 是否暂停
video.addEventListener('timeupdate', () => {
console.log(video.currentTime);
});
video.addEventListener('ended', () => {
console.log('播放结束');
});
8.4 字幕 <track>(WebVTT)
demo.zh.vtt 文件格式:
vtt
WEBVTT
00:00:00.000 --> 00:00:03.000
大家好,欢迎来到 HTML5 教程。
00:00:03.500 --> 00:00:07.000
本节我们讲解多媒体标签。
九、全局属性与 data-* 自定义数据
全局属性:所有 HTML 元素都可以使用的属性。
| 属性 | 说明 |
|---|---|
id |
唯一标识,一页中不可重复 |
class |
类名,可多个,空格分隔 |
style |
行内样式(少用) |
title |
悬停提示 |
hidden |
隐藏元素(等价 display:none) |
tabindex |
Tab 键顺序,0 可聚焦,-1 不可 Tab 但可 JS 聚焦 |
contenteditable |
是否可编辑,true 使其变成富文本容器 |
draggable |
是否可拖拽 |
spellcheck |
拼写检查 |
lang |
语言 |
dir |
文字方向 ltr / rtl |
accesskey |
快捷键 |
data-* |
自定义数据 |
示例:
html
<div id="app" class="container theme-dark" data-user-id="1024" data-role="admin" hidden>
<p contenteditable="true">这段文字可以直接编辑</p>
<p tabindex="0">可以按 Tab 聚焦</p>
<p title="这是提示">鼠标悬停我</p>
</div>
data-* 在 JS 中读取:
javascript
const app = document.querySelector('#app');
// 读取:驼峰命名
console.log(app.dataset.userId); // "1024"
console.log(app.dataset.role); // "admin"
// 写入
app.dataset.status = 'active'; // 变成 data-status="active"
// 删除
delete app.dataset.role;
CSS 中也能用:
css
[data-role="admin"] { border: 2px solid gold; }
[data-status="active"]::after { content: " ●"; color: green; }
data-*是 HTML 与 JS/CSS 通信的桥梁,Vue / React 的很多底层实现都依赖它。
十、HTML5 新 API 概览
这些是 HTML5 带来的、由 JavaScript 调用的能力(后续篇章会深入)。
10.1 Web Storage(本地存储)
javascript
// localStorage:永久保存,除非手动清除
localStorage.setItem('token', 'abc123');
console.log(localStorage.getItem('token')); // "abc123"
localStorage.removeItem('token');
localStorage.clear();
// 存对象要序列化
localStorage.setItem('user', JSON.stringify({ name: 'Tom', age: 18 }));
const user = JSON.parse(localStorage.getItem('user'));
// sessionStorage:关闭标签页即清空
sessionStorage.setItem('draft', '草稿内容');
| 对比 | localStorage | sessionStorage | Cookie |
|---|---|---|---|
| 容量 | ~5MB | ~5MB | ~4KB |
| 生命周期 | 永久 | 会话 | 可设置 |
| 随请求发送 | 否 | 否 | 是 |
| 跨标签共享 | 是 | 否 | 是 |
10.2 Canvas 绘图
html
<canvas id="cv" width="400" height="200"></canvas>
<script>
const ctx = document.getElementById('cv').getContext('2d');
ctx.fillStyle = '#3b82f6';
ctx.fillRect(20, 20, 150, 100);
ctx.beginPath();
ctx.arc(300, 80, 50, 0, Math.PI * 2);
ctx.fillStyle = '#ef4444';
ctx.fill();
</script>
10.3 拖放 API
html
<div id="drag" draggable="true">拖动我</div>
<div id="drop">放到这里</div>
<script>
drag.addEventListener('dragstart', e => e.dataTransfer.setData('text', 'hello'));
drop.addEventListener('dragover', e => e.preventDefault());
drop.addEventListener('drop', e => {
e.preventDefault();
console.log(e.dataTransfer.getData('text')); // "hello"
});
</script>
10.4 地理位置
javascript
navigator.geolocation.getCurrentPosition(
pos => console.log(pos.coords.latitude, pos.coords.longitude),
err => console.error(err.message)
);
10.5 其他重要 API
| API | 用途 |
|---|---|
FileReader |
读取本地文件内容 |
FormData |
表单数据打包上传 |
Web Worker |
后台线程计算,不阻塞 UI |
WebSocket |
双向实时通信 |
History API |
pushState 实现 SPA 路由 |
IntersectionObserver |
懒加载、曝光埋点 |
MutationObserver |
监听 DOM 变化 |
requestAnimationFrame |
高性能动画 |
Notification |
桌面通知 |
Clipboard API |
复制粘贴 |
十一、可访问性(a11y)
写 HTML 时顺手做到,成本极低、收益极高。
核心原则
- 语义化优先 :用
<button>而不是<div onclick>(前者自带键盘与焦点能力) - 图片有 alt :装饰图用
alt="" - 表单有 label
- 表格有 caption 与 scope
- 颜色对比度 ≥ 4.5:1
- 不要移除焦点轮廓(若移除请提供替代样式)
- ARIA 属性:当语义标签不够时补充
ARIA 常用属性
html
<!-- 按钮只有图标时,用 aria-label 说明 -->
<button aria-label="关闭弹窗">✕</button>
<!-- 折叠面板 -->
<button aria-expanded="false" aria-controls="panel">展开</button>
<div id="panel" hidden>内容</div>
<!-- 加载状态 -->
<div aria-live="polite">正在加载...</div>
<!-- 选项卡 -->
<div role="tablist">
<button role="tab" aria-selected="true">标签一</button>
<button role="tab" aria-selected="false">标签二</button>
</div>
ARIA 第一法则:能用原生语义标签解决的,就不要用 ARIA。
十二、项目搭建实战:个人任务管理器骨架
现在我们用纯 HTML5 搭建一个"个人任务管理器"的完整页面骨架。CSS 与 JS 将在后续篇章补全。
12.1 目录结构
task-manager/
├── index.html # 主页面
├── favicon.ico
├── css/
│ ├── reset.css # 样式重置
│ ├── variables.css # CSS 变量
│ └── main.css # 主样式
├── js/
│ ├── main.js # 入口
│ ├── storage.js # localStorage 封装
│ ├── render.js # 渲染逻辑
│ └── events.js # 事件绑定
├── images/
│ ├── logo.svg
│ └── empty-state.svg
├── media/
└── assets/
└── fonts/
12.2 设计要点
- 用语义化标签搭骨架
- 表单使用 HTML5 原生校验
- 用
data-*挂载任务数据,供 JS 读取 - 用
<template>定义任务卡片模板 - 用
localStorage做数据持久化 - 用
<dialog>实现原生弹窗
12.3 完整 index.html
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="description" content="简洁高效的个人任务管理器,基于原生 HTML5/CSS3/JavaScript 构建">
<meta name="theme-color" content="#3b82f6">
<title>TaskFlow · 个人任务管理器</title>
<link rel="icon" href="./images/logo.svg" type="image/svg+xml">
<link rel="stylesheet" href="./css/reset.css">
<link rel="stylesheet" href="./css/variables.css">
<link rel="stylesheet" href="./css/main.css">
</head>
<body>
<!-- ============ 页头 ============ -->
<header class="app-header">
<div class="brand">
<img src="./images/logo.svg" alt="" width="32" height="32">
<h1>TaskFlow</h1>
</div>
<nav aria-label="主导航">
<ul class="nav-list">
<li><a href="#all" class="nav-link is-active" data-filter="all">全部</a></li>
<li><a href="#active" class="nav-link" data-filter="active">进行中</a></li>
<li><a href="#done" class="nav-link" data-filter="done">已完成</a></li>
</ul>
</nav>
<button type="button" id="themeToggle" class="icon-btn" aria-label="切换深色模式">
<span aria-hidden="true">🌙</span>
</button>
</header>
<!-- ============ 主体 ============ -->
<main class="app-main">
<!-- 添加任务表单 -->
<section aria-labelledby="add-title" class="panel">
<h2 id="add-title" class="sr-only">添加新任务</h2>
<form id="taskForm" class="task-form" novalidate>
<div class="field">
<label for="taskTitle" class="sr-only">任务标题</label>
<input
type="text"
id="taskTitle"
name="title"
placeholder="要做什么?按回车添加..."
required
minlength="1"
maxlength="80"
autocomplete="off"
autofocus
>
</div>
<div class="field">
<label for="taskPriority" class="sr-only">优先级</label>
<select id="taskPriority" name="priority">
<option value="low">低</option>
<option value="normal" selected>普通</option>
<option value="high">高</option>
</select>
</div>
<div class="field">
<label for="taskDue" class="sr-only">截止日期</label>
<input type="date" id="taskDue" name="due">
</div>
<button type="submit" class="btn btn-primary">添加任务</button>
</form>
<!-- 校验错误提示(由 JS 控制显示) -->
<p id="formError" class="form-error" role="alert" hidden></p>
</section>
<!-- 任务统计 -->
<section aria-labelledby="stats-title" class="panel stats">
<h2 id="stats-title" class="sr-only">任务统计</h2>
<dl class="stat-list">
<div class="stat">
<dt>总计</dt>
<dd id="statTotal">0</dd>
</div>
<div class="stat">
<dt>进行中</dt>
<dd id="statActive">0</dd>
</div>
<div class="stat">
<dt>已完成</dt>
<dd id="statDone">0</dd>
</div>
</dl>
<div class="progress-wrap">
<label for="progressBar" class="sr-only">完成进度</label>
<progress id="progressBar" value="0" max="100">0%</progress>
</div>
</section>
<!-- 任务列表 -->
<section aria-labelledby="list-title" class="panel">
<div class="panel-head">
<h2 id="list-title">我的任务</h2>
<button type="button" id="clearDone" class="btn btn-ghost">清除已完成</button>
</div>
<ul id="taskList" class="task-list" aria-live="polite">
<!-- 任务项由 JS 渲染,或首次打开时用下面的静态示例 -->
<li class="task-item" data-id="1" data-priority="high" data-done="false">
<label class="task-check">
<input type="checkbox" class="task-toggle">
<span class="checkmark" aria-hidden="true"></span>
<span class="task-title">完成 HTML5 第一篇学习</span>
</label>
<div class="task-meta">
<span class="badge badge-high">高</span>
<time datetime="2025-01-20">1 月 20 日</time>
</div>
<button type="button" class="icon-btn task-delete" aria-label="删除任务">🗑</button>
</li>
</ul>
<!-- 空状态 -->
<div id="emptyState" class="empty-state" hidden>
<img src="./images/empty-state.svg" alt="" width="160" height="120">
<p>还没有任务,先添加一个吧 ✨</p>
</div>
</section>
</main>
<!-- ============ 页脚 ============ -->
<footer class="app-footer">
<p>
© 2025 TaskFlow ·
使用原生 <abbr title="HyperText Markup Language 5">HTML5</abbr> 构建
</p>
<address>
反馈请发至 <a href="mailto:hi@taskflow.dev">hi@taskflow.dev</a>
</address>
</footer>
<!-- ============ 任务模板 ============ -->
<template id="taskTemplate">
<li class="task-item" data-id="" data-priority="normal" data-done="false">
<label class="task-check">
<input type="checkbox" class="task-toggle">
<span class="checkmark" aria-hidden="true"></span>
<span class="task-title"></span>
</label>
<div class="task-meta">
<span class="badge"></span>
<time></time>
</div>
<button type="button" class="icon-btn task-delete" aria-label="删除任务">🗑</button>
</li>
</template>
<!-- ============ 编辑弹窗(原生 dialog) ============ -->
<dialog id="editDialog" class="dialog">
<form method="dialog" class="dialog-form">
<h2>编辑任务</h2>
<label for="editTitle">标题</label>
<input type="text" id="editTitle" name="title" maxlength="80" required>
<label for="editPriority">优先级</label>
<select id="editPriority" name="priority">
<option value="low">低</option>
<option value="normal">普通</option>
<option value="high">高</option>
</select>
<label for="editDue">截止日期</label>
<input type="date" id="editDue" name="due">
<menu class="dialog-actions">
<button type="button" id="dialogCancel" class="btn btn-ghost">取消</button>
<button type="submit" id="dialogSave" class="btn btn-primary">保存</button>
</menu>
</form>
</dialog>
<!-- ============ 脚本 ============ -->
<script src="./js/storage.js" defer></script>
<script src="./js/render.js" defer></script>
<script src="./js/events.js" defer></script>
<script src="./js/main.js" defer></script>
</body>
</html>
12.4 骨架中的 HTML5 知识点索引
| 用到的技术 | 出现位置 |
|---|---|
<!DOCTYPE html> + lang |
第 1~2 行 |
meta charset / viewport / description / theme-color |
head |
link rel="icon" |
head |
header / nav / main / section / footer |
整体结构 |
h1 / h2 层级 |
各区块标题 |
sr-only 隐藏标题(视觉隐藏但可读) |
多处 |
aria-label / aria-labelledby / aria-live / aria-hidden / role="alert" |
无障碍 |
form + novalidate |
添加表单 |
input type="text" + required + minlength + maxlength |
标题输入 |
input type="date" |
截止日期 |
select + option |
优先级 |
input type="checkbox" |
任务勾选 |
input type="email"(页脚 mailto) |
联系方式 |
progress |
完成进度 |
dl / dt / dd |
统计信息 |
ul / li |
任务列表、导航 |
data-id / data-priority / data-done |
任务数据挂载 |
time datetime |
截止日期 |
abbr title |
页脚 HTML5 缩写 |
address |
页脚联系信息 |
<template> |
任务卡片模板 |
<dialog> |
编辑弹窗 |
<menu> |
弹窗按钮组 |
hidden 属性 |
空状态、错误提示 |
defer 脚本加载 |
底部脚本 |
autocomplete="off" / autofocus |
输入框 |
© 实体 |
页脚 |
12.5 配套 css/variables.css(预览,第二篇详解)
css
:root {
/* 颜色 */
--color-primary: #3b82f6;
--color-primary-dark: #2563eb;
--color-danger: #ef4444;
--color-success: #22c55e;
--color-warning: #f59e0b;
--bg-page: #f8fafc;
--bg-card: #ffffff;
--bg-hover: #f1f5f9;
--text-primary: #0f172a;
--text-secondary: #64748b;
--text-muted: #94a3b8;
--border-color: #e2e8f0;
/* 圆角 */
--radius-sm: 6px;
--radius-md: 10px;
--radius-lg: 16px;
/* 阴影 */
--shadow-sm: 0 1px 2px rgba(15, 23, 42, 0.06);
--shadow-md: 0 4px 12px rgba(15, 23, 42, 0.08);
/* 间距 */
--space-1: 4px;
--space-2: 8px;
--space-3: 12px;
--space-4: 16px;
--space-6: 24px;
--space-8: 32px;
}
/* 深色主题 */
[data-theme="dark"] {
--bg-page: #0f172a;
--bg-card: #1e293b;
--bg-hover: #334155;
--text-primary: #f1f5f9;
--text-secondary: #cbd5e1;
--border-color: #334155;
}
12.6 配套 css/reset.css(基础重置)
css
*, *::before, *::after { box-sizing: border-box; }
* { margin: 0; padding: 0; }
html { -webkit-text-size-adjust: 100%; }
body {
font-family: system-ui, -apple-system, "Segoe UI", "PingFang SC",
"Microsoft YaHei", sans-serif;
line-height: 1.6;
-webkit-font-smoothing: antialiased;
}
img, picture, video, canvas, svg {
display: block;
max-width: 100%;
}
input, button, textarea, select {
font: inherit;
color: inherit;
}
a { color: inherit; text-decoration: none; }
ul, ol { list-style: none; }
button { background: none; border: none; cursor: pointer; }
/* 屏幕阅读器专用隐藏类 */
.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;
}
12.7 js/storage.js 预览(第四、五篇详解)
javascript
// localStorage 的简单封装,避免到处写 JSON.parse
const STORAGE_KEY = 'taskflow.tasks';
export const storage = {
load() {
try {
return JSON.parse(localStorage.getItem(STORAGE_KEY)) ?? [];
} catch {
return [];
}
},
save(tasks) {
localStorage.setItem(STORAGE_KEY, JSON.stringify(tasks));
}
};
12.8 运行方式
- 新建
task-manager文件夹,按上面结构建好目录 - 把
index.html、reset.css、variables.css、storage.js等文件写入对应位置 - 用 VS Code + Live Server 插件 打开
index.html,或直接双击用浏览器打开 - 打开 DevTools(F12):
- Elements 面板查看 DOM 结构
- Application → Local Storage 查看本地存储
- Lighthouse 面板跑一次可访问性评分
十三、本篇小结与练习
知识点回顾
| 模块 | 核心要点 |
|---|---|
| 文档骨架 | doctype、lang、meta charset/viewport、script 的 defer/async |
| 语义化 | header/nav/main/article/section/aside/footer/figure/details |
| 文本 | h1~h6、p、strong/em、mark、code/pre、实体字符 |
| 列表表格 | ul/ol/dl、table + thead/tbody/tfoot/caption/scope/colspan |
| 链接图片 | a 的 target/rel/download、img 的 alt/loading/srcset、picture |
| 表单 | 全部 input type、label 绑定、原生校验、fieldset/legend、datalist |
| 多媒体 | audio/video/source/track、常用属性与 JS 控制 |
| 全局属性 | data-*、hidden、contenteditable、tabindex |
| 新 API | Storage、Canvas、拖放、地理定位等概览 |
| 可访问性 | alt、label、aria-*、对比度 |
动手练习
- 基础 :写一个"个人简历"页面,包含
header(姓名+联系方式)、main(article分教育/工作经历,用time标时间)、footer,图片使用figure+figcaption。 - 进阶 :写一个"注册表单",要求用到
email、tel(带pattern)、date、range、color、file、checkbox、radio、datalist,并全部用label绑定,用fieldset+legend分组。 - 挑战 :用
<details>+<table>做一个"FAQ + 价格对比表",要求caption、scope、colspan齐全,并用 Lighthouse 检查可访问性 ≥ 90 分。
常见坑清单
- ❌ 忘记
charset→ 中文乱码 - ❌
ul里直接放div→ 结构非法 - ❌
label的for与input的id不匹配 → 点击无效 - ❌
<button>在表单内不写type→ 意外提交 - ❌ 图片不写
alt→ 无障碍不合格 - ❌
target="_blank"不加rel="noopener"→ 安全漏洞 - ❌ 用
div代替button→ 失去键盘可访问性 - ❌ 脚本写在 head 且不加
defer→ 拿不到 DOM
下一篇预告
第二篇:CSS3 全部知识点
- 选择器全解(基础 / 组合 / 伪类 / 伪元素 / 属性选择器)
- 层叠、继承、优先级计算
- 盒模型、
box-sizing、外边距塌陷 - 现代布局:Flexbox 全属性 + Grid 全属性
- 定位、层叠上下文、BFC
- 过渡、动画、变形(transform)
- 媒体查询与响应式设计
- CSS 变量、
clamp()、容器查询 - 把本项目的
main.css完整实现出来
请把上面的 index.html 保存好,下一篇我们将把它变成一个漂亮的成品界面。 🚀