Skip to content

HTML 速查表

用于创建网页的标准标记语言。

01

文档结构

基本页面模板

每个 HTML5 文档以 <!DOCTYPE html> 开头,后跟带 lang 属性的 <html> 以支持可访问性和 SEO。<head> 包含元数据(charset、viewport、title),<body> 持有可见内容。viewport meta 标签确保在移动设备上正确渲染。

html
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>My Web Page</title>
</head>
<body>
  <h1>Hello, World!</h1>
  <p>Welcome to my page.</p>
</body>
</html>

Head 部分与元数据

<head> 元素包含机器可读的元数据。charset 声明字符编码(UTF-8 覆盖几乎所有字符)。description meta 标签对 SEO 至关重要——搜索引擎在结果中显示它。在脚本上使用 defer 以在 HTML 解析后加载它们。

html
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Page Title - Site Name</title>
  <meta name="description" content="A brief description for SEO">
  <meta name="author" content="John Doe">
  <link rel="stylesheet" href="style.css">
  <script src="app.js" defer></script>
</head>

注释与条件注释

HTML 注释以 <!-- 开头,以 --> 结束。它们不在浏览器中显示,但在页面源代码中可见。注释适用于留下笔记、标记部分(TODO、FIXME)和文档。条件注释(仅 IE)已过时,但可能出现在遗留代码中。

html
<!-- This is a single-line comment -->

<!--
  This is a
  multi-line comment
-->

<!-- TODO: Add form validation -->
<!-- FIXME: Fix layout on mobile -->

<!--[if lt IE 9]>
  <script src="html5shiv.js"></script>
<![endif]-->

标题层次结构

HTML 提供六个标题级别,h1(最高)到 h6(最低)。按层次顺序使用,不跳过级别。搜索引擎使用标题理解页面结构。最佳实践:每页一个 h1,后跟 h2 用于主要部分,h3 用于子部分等。

html
<h1>Main Page Title</h1>
<h2>Major Section</h2>
<h3>Subsection</h3>
<h4>Sub-subsection</h4>
<h5>Minor Heading</h5>
<h6>Lowest Level Heading</h6>

<!-- Use only one h1 per page for SEO -->
<h1>Blog Post Title</h1>
<h2>Introduction</h2>
<h3>Background</h3>

Div 与 Span 容器

<div> 是块级容器,用于分组元素和应用样式。<span> 是内联容器,用于样式化文本的小部分。两者都是无语义的通用元素——在适当情况下优先使用语义标签(header、nav、main、article)以获得更好的可访问性和 SEO。

html
<div class="container">
  <div id="header" class="header">
    <span class="logo">MySite</span>
    <span class="tagline">Best Content</span>
  </div>
  <div class="content">
    <p>Main <span class="highlight">content</span> here.</p>
  </div>
</div>
02

文本格式化

粗体、斜体与强调

<strong> 表示重要文本(屏幕阅读器会强调它),而 <b> 纯粹是视觉粗体。类似地,<em> 表示压力强调(语义),而 <i> 是视觉斜体。为可访问性优先使用 <strong> 和 <em>。<b> 和 <i> 可用于无语义重要性的样式目的。

html
<p>This is <strong>important</strong> text.</p>
<p>This is <b>bold</b> text.</p>
<p>This is <em>emphasized</em> text.</p>
<p>This is <i>italic</i> text.</p>
<p><strong>Warning:</strong> Do not touch!</p>

Mark、Delete 与 Insert

<mark> 高亮相关文本(如搜索高亮)。<del> 标记删除的文本,<ins> 标记插入的文本——适用于显示编辑。<s> 划掉过时信息。<small> 用于旁注或版权。<u> 应谨慎使用,因为它类似于链接。

html
<p>Please <mark>highlight this</mark> word.</p>
<p>The price is <del>$99</del> <ins>$79</ins> now.</p>
<p><s>Old information</s> is struck through.</p>
<p>Use <u>underline</u> sparingly.</p>
<p>Small <small>print</small> for side comments.</p>

上标与下标

<sub> 创建下标文本(基线以下),用于化学公式(H2O)和数学变量。<sup> 创建上标文本(基线以上),用于指数(x²)、序数(1st)和脚注。两者都是调整垂直位置和字体大小的内联元素。

html
<p>H<sub>2</sub>O is water.</p>
<p>E = mc<sup>2</sup> is Einstein's equation.</p>
<p>1st<sup>st</sup> January 2024</p>
<p>CO<sub>2</sub> emissions are rising.</p>
<p>x<sup>2</sup> + y<sup>2</sup> = r<sup>2</sup></p>

引用与引文

<blockquote> 用于块级引用(缩进、多行)。<q> 用于内联短引用(自动添加引号)。cite 属性提供来源 URL。<cite> 标记作品标题或作者名。这些元素改善引用内容的语义标记。

html
<blockquote cite="https://example.com/source">
  <p>The best way to predict the future is to invent it.</p>
  <footer>— <cite>Alan Kay</cite></footer>
</blockquote>

<p>As <q cite="https://example.com">someone once said</q>,
knowledge is power.</p>

代码与预格式化文本

<code> 标记内联代码片段(等宽字体)。<pre> 保留空白和换行用于预格式化文本。组合 <pre><code> 显示带有正确缩进的代码块。<kbd> 表示键盘输入。<samp> 用于程序输出,<var> 用于数学表达式中的变量。

html
<p>Use the <code>console.log()</code> function.</p>
<p>Press <kbd>Ctrl</kbd> + <kbd>C</kbd> to copy.</p>

<pre><code>function hello() {
  console.log("Hello, World!");
  return true;
}</code></pre>

换行与水平线

<br> 在文本内创建换行(谨慎使用,优先用 CSS)。<hr> 创建水平线,表示部分之间的主题分隔。<wbr> 为长单词或 URL 建议换行机会,允许浏览器在最佳点断行。避免使用 <br> 进行间距——改用 CSS 边距。

html
<p>First line<br>Second line<br>Third line</p>

<p>Paragraph one.</p>
<hr>
<p>Paragraph two after a thematic break.</p>

<wbr><!-- word break opportunity for long URLs -->
https://example.com/<wbr>very<wbr>long<wbr>url
04

图像与多媒体

图像

始终为可访问性和 SEO 包含 alt 文本——它为屏幕阅读器描述图像,并在图像加载失败时显示。loading='lazy' 延迟屏幕外图像加载,提高页面速度。srcset 和 sizes 启用响应式图像——浏览器根据设备分辨率和视口选择最佳图像。始终指定 width 和 height 以防止布局偏移。

html
<!-- Basic image -->
<img src="photo.jpg" alt="A sunset over mountains" width="800" height="600">

<!-- Image with lazy loading -->
<img src="hero.jpg" alt="Hero banner" loading="lazy" decoding="async">

<!-- Responsive image with srcset -->
<img
  src="medium.jpg"
  srcset="small.jpg 480w, medium.jpg 800w, large.jpg 1200w"
  sizes="(max-width: 600px) 480px, 800px"
  alt="Responsive photo"
>

Picture 元素

<picture> 提供艺术指导和格式回退。<source> 元素提供替代方案——浏览器选择第一个支持的格式(WebP/AVIF 更小)。media 属性为不同屏幕尺寸启用不同图像。最后的 <img> 是回退,必须始终存在。这对于提供完全不同的图像,它优于 srcset。

html
<picture>
  <source srcset="webp-image.webp" type="image/webp">
  <source srcset="avif-image.avif" type="image/avif">
  <source srcset="wide.jpg" media="(min-width: 800px)">
  <img src="fallback.jpg" alt="Art-directed image">
</picture>

音频

<audio> 嵌入声音内容。controls 添加播放/暂停/音量控件。多个 <source> 元素提供格式回退(MP3 支持最广泛)。autoplay 受浏览器限制——静音自动播放通常被允许。loop 重复音频。如果音频不受支持,则显示内部文本。始终为用户体验提供控件。

html
<audio controls>
  <source src="song.mp3" type="audio/mpeg">
  <source src="song.ogg" type="audio/ogg">
  Your browser does not support the audio element.
</audio>

<!-- Autoplay (muted required in most browsers) -->
<audio autoplay muted loop>
  <source src="background.mp3" type="audio/mpeg">
</audio>

视频

<video> 嵌入视频,带播放、暂停、音量和全屏控件。poster 在播放前设置缩略图。多个源提供格式回退(MP4/H.264 兼容性最好)。<track> 为可访问性添加字幕、说明或描述。playsinline 防止 iOS 上强制全屏。除非自动播放静音,否则始终包含控件。

html
<video controls width="640" height="360" poster="thumbnail.jpg">
  <source src="movie.mp4" type="video/mp4">
  <source src="movie.webm" type="video/webm">
  <track kind="subtitles" src="subs.vtt" srclang="en" label="English">
  Your browser does not support video.
</video>

<!-- Autoplay muted loop (common for backgrounds) -->
<video autoplay muted loop playsinline>
  <source src="bg.mp4" type="video/mp4">
</video>

Figure 与 Figcaption

<figure> 分组自包含内容,如图像、图表、代码列表或引用及其标题。<figcaption> 提供标题或图例。这种语义分组改善可访问性——屏幕阅读器宣布关系。图形可以从文本中引用('见图 1')并在布局中移动而不丢失上下文。

html
<figure>
  <img src="chart.png" alt="Quarterly sales chart showing 20% growth">
  <figcaption>Figure 1: Q4 2024 sales growth by region.</figcaption>
</figure>

<figure>
  <blockquote>
    <p>The best way to predict the future is to invent it.</p>
  </blockquote>
  <figcaption>— Alan Kay, 1971</figcaption>
</figure>

Iframe 嵌入

<iframe> 嵌入外部内容(视频、地图、其他页面)。始终为可访问性包含 title 属性。loading='lazy' 改善屏幕外 iframe 的性能。allow 属性指定功能策略。sandbox 属性为安全限制 iframe 的功能。请谨慎——iframe 可能影响性能和安全。

html
<!-- Embed a YouTube video -->
<iframe
  src="https://www.youtube.com/embed/dQw4w9WgXcQ"
  width="560" height="315"
  title="YouTube video"
  frameborder="0"
  allow="accelerometer; autoplay; encrypted-media"
  allowfullscreen>
</iframe>

<!-- Embed a map -->
<iframe
  src="https://maps.google.com/maps?q=Paris&output=embed"
  title="Map of Paris" loading="lazy">
</iframe>
05

列表

无序列表

<ul> 创建无序(项目符号)列表。每项包裹在 <li> 中。list-style-type CSS 属性更改项目符号样式(disc、circle、square、none)。无序列表用于顺序无关的项。使用 list-style: none 和自定义样式用于导航菜单和功能列表。

html
<ul>
  <li>Apple</li>
  <li>Banana</li>
  <li>Cherry</li>
</ul>

<!-- Custom bullet style via CSS -->
<ul style="list-style-type: square;">
  <li>First item</li>
  <li>Second item</li>
</ul>

有序列表

<ol> 创建有序(编号)列表用于顺序项。start 属性设置起始编号。reversed 以降序显示项。type 属性更改编号样式(1、A、a、I、i)。对步骤、排名或任何顺序重要的序列使用 <ol>。

html
<ol>
  <li>First step</li>
  <li>Second step</li>
  <li>Third step</li>
</ol>

<!-- Start from a specific number -->
<ol start="5">
  <li>Fifth item</li>
  <li>Sixth item</li>
</ol>

<!-- Reverse order -->
<ol reversed>
  <li>Countdown 3</li>
  <li>Countdown 2</li>
  <li>Countdown 1</li>
</ol>

描述列表

<dl> 创建描述列表,将术语(<dt>)与描述(<dd>)配对。这适用于词汇表、常见问题页面和术语-定义对。多个 <dd> 可以描述一个 <dt>,反之亦然。描述列表提供定义表所缺乏的语义,改善可访问性。

html
<dl>
  <dt>HTML</dt>
  <dd>HyperText Markup Language</dd>

  <dt>CSS</dt>
  <dd>Cascading Style Sheets</dd>

  <dt>JavaScript</dt>
  <dd>A programming language for the web</dd>
</dl>

嵌套列表

通过将 <ul> 或 <ol> 放在 <li> 内来嵌套列表。浏览器自动缩进嵌套列表并可能更改项目符号样式。嵌套创建层次结构,如目录、文件树或多级菜单。保持嵌套合理(2-3 级)以提高可用性。深度嵌套的列表变得难以阅读。

html
<ul>
  <li>Fruits
    <ul>
      <li>Apple</li>
      <li>Banana</li>
    </ul>
  </li>
  <li>Vegetables
    <ol>
      <li>Carrot</li>
      <li>Spinach</li>
    </ol>
  </li>
</ul>

带链接的列表(导航)

<nav> 内的链接列表创建语义导航菜单。嵌套的 <ul> 元素形成下拉子菜单。此结构对屏幕阅读器可访问,且易于用 CSS 样式化。对交互式下拉菜单使用 aria 属性(aria-expanded、aria-haspopup)。基于列表的导航是可访问菜单的标准模式。

html
<nav>
  <ul class="menu">
    <li><a href="/">Home</a></li>
    <li><a href="/products">Products</a>
      <ul class="submenu">
        <li><a href="/products/a">Product A</a></li>
        <li><a href="/products/b">Product B</a></li>
      </ul>
    </li>
    <li><a href="/contact">Contact</a></li>
  </ul>
</nav>
06

表格

基本表格

<table> 创建表格数据。<thead> 分组标题行,<tbody> 分组主体行,<tfoot> 分组页脚行。<tr> 是表格行,<th> 是标题单元格(粗体、居中),<td> 是数据单元格。使用 thead/tbody/tfoot 改善可访问性并启用更好的样式。表格应用于数据,而非布局。

html
<table>
  <thead>
    <tr>
      <th>Name</th>
      <th>Age</th>
      <th>City</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Alice</td>
      <td>30</td>
      <td>New York</td>
    </tr>
    <tr>
      <td>Bob</td>
      <td>25</td>
      <td>London</td>
    </tr>
  </tbody>
</table>

跨单元格

colspan 使单元格跨多列(水平合并)。rowspan 使单元格跨多行(垂直合并)。这些属性创建复杂表格布局,如分组多列的标题。请谨慎使用跨单元格——它可能使表格难以被屏幕阅读器解析。始终测试可访问性。

html
<table border="1">
  <tr>
    <th colspan="2">Name</th>
    <th rowspan="2">Age</th>
  </tr>
  <tr>
    <th>First</th>
    <th>Last</th>
  </tr>
  <tr>
    <td>Alice</td>
    <td>Smith</td>
    <td>30</td>
  </tr>
</table>

表格标题与范围

<caption> 为表格提供标题,改善可访问性。<th> 上的 scope 属性告诉屏幕阅读器标题是应用于列(scope='col')还是行(scope='row')。这对复杂表格至关重要。对于更复杂的表格,使用 id 和 headers 属性显式关联单元格与其标题。

html
<table>
  <caption>Employee Salary Report 2024</caption>
  <thead>
    <tr>
      <th scope="col">Name</th>
      <th scope="col">Department</th>
      <th scope="col">Salary</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <th scope="row">Alice</th>
      <td>Engineering</td>
      <td>$95,000</td>
    </tr>
  </tbody>
</table>

列组

<colgroup> 分组列以进行样式化。其中的 <col> 元素将样式(尤其是宽度)应用于特定列。这比为每个单元格设置宽度更高效。列组还支持 span 属性以一次样式化多列。使用此功能在所有行中保持一致的列宽。

html
<table>
  <colgroup>
    <col style="width: 30%;">
    <col style="width: 50%;">
    <col style="width: 20%;">
  </colgroup>
  <tr>
    <th>Product</th>
    <th>Description</th>
    <th>Price</th>
  </tr>
  <tr>
    <td>Laptop</td>
    <td>15-inch laptop</td>
    <td>$999</td>
  </tr>
</table>

样式化表格

border-collapse: collapse 将相邻边框合并为一个。使用 :nth-child(even) 实现斑马条纹行(改善可读性)。:hover 在鼠标悬停时高亮行。th 样式区分标题。保持表格样式干净易读——避免过多边框。响应式表格可能需要在包装器上使用 overflow-x: auto 以适应小屏幕。

html
<style>
  table { border-collapse: collapse; width: 100%; }
  th, td { border: 1px solid #ddd; padding: 8px; text-align: left; }
  th { background-color: #f4f4f4; }
  tr:nth-child(even) { background-color: #f9f9f9; }
  tr:hover { background-color: #e0e0e0; }
</style>
<table>
  <tr><th>Name</th><th>Score</th></tr>
  <tr><td>Alice</td><td>95</td></tr>
  <tr><td>Bob</td><td>87</td></tr>
</table>
07

表单与输入

表单结构

<form> 包裹输入控件。action 指定提交 URL,method 是 GET(在 URL 中可见)或 POST(隐藏)。文件上传需要 enctype='multipart/form-data'。<fieldset> 分组相关字段,<legend> 提供标题。始终使用 for/id 匹配将 <label> 与输入关联以支持可访问性。

html
<form action="/submit" method="POST" enctype="multipart/form-data">
  <fieldset>
    <legend>Personal Information</legend>

    <label for="name">Name:</label>
    <input type="text" id="name" name="name" required>

    <label for="email">Email:</label>
    <input type="email" id="email" name="email" required>
  </fieldset>

  <button type="submit">Submit</button>
</form>

输入类型

HTML5 引入了许多带内置验证和移动友好键盘的输入类型。type='email' 验证电子邮件格式。type='number' 提供微调按钮。type='date' 和 'time' 显示原生选择器。type='color' 打开颜色选择器。type='range' 创建滑块。带 accept 的 type='file' 限制文件类型。type='hidden' 存储不向用户显示的数据。

html
<input type="text" name="username" placeholder="Username">
<input type="email" name="email" required>
<input type="password" name="pwd" minlength="8">
<input type="number" name="age" min="0" max="120" step="1">
<input type="date" name="birthday">
<input type="time" name="appointment">
<input type="color" name="favcolor" value="#ff0000">
<input type="range" name="volume" min="0" max="100">
<input type="file" name="upload" accept="image/*">
<input type="hidden" name="token" value="abc123">

Select 与 Textarea

<select> 创建下拉菜单。<optgroup> 用标签分组相关选项。<option> 定义选项;selected 属性预选一个。<textarea> 用于多行文本输入。rows 和 cols 设置可见大小。maxlength 限制字符数。placeholder 提供提示。两者都支持 required 和 disabled 属性。

html
<label for="country">Country:</label>
<select id="country" name="country">
  <optgroup label="North America">
    <option value="us">United States</option>
    <option value="ca">Canada</option>
  </optgroup>
  <optgroup label="Europe">
    <option value="uk">United Kingdom</option>
    <option value="fr">France</option>
  </optgroup>
</select>

<label for="bio">Bio:</label>
<textarea id="bio" name="bio" rows="4" cols="40"
  maxlength="500" placeholder="Tell us about yourself"></textarea>

复选框与单选按钮

复选框允许多选;单选按钮仅允许一个(当它们共享相同 name 时)。checked 属性预选一个选项。始终将输入包裹在 <label> 中以获得可点击文本。使用 <fieldset> 和 <legend> 分组相关选项以支持可访问性。仅当选中时值才发送到服务器。

html
<fieldset>
  <legend>Select your interests:</legend>
  <label><input type="checkbox" name="interest" value="tech" checked> Tech</label>
  <label><input type="checkbox" name="interest" value="music"> Music</label>
  <label><input type="checkbox" name="interest" value="sports"> Sports</label>
</fieldset>

<fieldset>
  <legend>Choose a plan:</legend>
  <label><input type="radio" name="plan" value="free" checked> Free</label>
  <label><input type="radio" name="plan" value="pro"> Pro</label>
  <label><input type="radio" name="plan" value="enterprise"> Enterprise</label>
</fieldset>

表单验证

HTML5 提供内置客户端验证。required 防止空值提交。minlength/maxlength 约束文本长度。pattern 使用正则表达式进行自定义验证(title 属性指导用户)。type='email' 和 type='url' 验证格式。min/max 用于数字和日期。向表单添加 novalidate 以禁用验证。

html
<form>
  <input type="text" name="username" required
         minlength="3" maxlength="20"
         pattern="[A-Za-z0-9_]+"
         title="3-20 alphanumeric characters">

  <input type="email" name="email" required>

  <input type="url" name="website"
         placeholder="https://example.com">

  <input type="number" name="age" min="18" max="99">

  <button type="submit">Submit</button>
</form>

按钮与 Datalist

type='submit' 提交表单,type='reset' 清除所有字段,type='button' 是自定义按钮。<datalist> 为 <input> 元素提供自动补全建议——用 list/id 链接它们。与 <select> 不同,用户可以输入自定义值。这结合了文本输入的灵活性和建议的便利性。

html
<!-- Button types -->
<button type="submit">Submit Form</button>
<button type="reset">Reset</button>
<button type="button" onclick="alert('Hi')">Click Me</button>

<!-- Datalist for autocomplete suggestions -->
<label for="browser">Choose a browser:</label>
<input list="browsers" name="browser" id="browser">
<datalist id="browsers">
  <option value="Chrome">
  <option value="Firefox">
  <option value="Safari">
  <option value="Edge">
</datalist>
08

语义化 HTML

Header 与导航

<header> 表示介绍性内容——通常是徽标、标题和导航。它可以出现在页面顶部或 <article> 或 <section> 内。<nav> 标识导航链接,帮助屏幕阅读器跳转到导航。一个页面可以有多个标题(例如,每个部分一个)。Header 与 head 不同——它是可见内容。

html
<header>
  <h1>My Website</h1>
  <nav>
    <ul>
      <li><a href="/">Home</a></li>
      <li><a href="/blog">Blog</a></li>
      <li><a href="/contact">Contact</a></li>
    </ul>
  </nav>
</header>

Main 与 Article

<main> 包裹页面的主要内容(每页仅一个)。<article> 表示可以独立分发的自包含内容(博客文章、新闻文章、论坛帖子)。带 datetime 的 <time> 为 SEO 提供机器可读日期。<section> 分组带标题的主题相关内容。这些标签改善文档大纲和可访问性。

html
<main>
  <article>
    <h1>Blog Post Title</h1>
    <p>Published on <time datetime="2024-01-15">January 15, 2024</time></p>
    <p>The main content of the article...</p>
    <section>
      <h2>Subsection</h2>
      <p>More content...</p>
    </section>
  </article>
</main>

Section 与 Aside

<section> 分组带标题的相关内容——当内容有自然标题时使用。<aside> 表示与主要内容间接相关的内容(侧边栏、引文、广告、相关链接)。两者都改善文档结构。部分可以嵌套。Aside 通常样式化为侧边栏,但它是语义的,而非表现性的。

html
<section>
  <h2>Services</h2>
  <p>We offer the following services:</p>
  <article>
    <h3>Web Design</h3>
    <p>Custom website design...</p>
  </article>
</section>

<aside>
  <h3>Related Articles</h3>
  <ul>
    <li><a href="/post1">Related Post 1</a></li>
    <li><a href="/post2">Related Post 2</a></li>
  </ul>
</aside>

Footer

<footer> 包含页面或部分的页脚内容——版权、相关文档链接、联系信息、站点地图。一个页面可以有多个页脚(例如,每篇文章部分一个)。页脚在视觉上不要求位于底部,但在语义上表示结束内容。使用 &copy; 表示版权符号。

html
<footer>
  <div>
    <h3>About Us</h3>
    <p>Company information...</p>
  </div>
  <nav>
    <a href="/privacy">Privacy Policy</a>
    <a href="/terms">Terms of Service</a>
    <a href="/sitemap">Sitemap</a>
  </nav>
  <p>&copy; 2024 My Company. All rights reserved.</p>
</footer>

Details 与 Summary

<details> 创建交互式披露小部件——内容隐藏直到用户点击 <summary>。open 属性默认展开显示。这是原生 HTML,无需 JavaScript。适用于常见问题、可折叠部分和渐进式披露。嵌套的 details 创建多级手风琴。用 [open] 属性选择器样式化。

html
<details>
  <summary>Click to expand FAQ</summary>
  <p>This content is hidden by default and shown when the summary is clicked.</p>
</details>

<!-- Open by default -->
<details open>
  <summary>What is HTML?</summary>
  <p>HTML (HyperText Markup Language) is the standard language for creating web pages.</p>
</details>

<!-- Nested details -->
<details>
  <summary>Advanced Topics</summary>
  <details>
    <summary>Sub-topic</summary>
    <p>Nested content...</p>
  </details>
</details>
09

Meta 标签与 SEO

Charset 与 Viewport

charset='UTF-8' 声明字符编码——对正确显示所有字符至关重要。viewport meta 标签对响应式设计至关重要:width=device-width 匹配设备宽度,initial-scale=1.0 设置缩放级别。没有它,移动浏览器以桌面宽度渲染页面。这两个 meta 标签应在每个 HTML 文档中。

html
<head>
  <!-- Character encoding -->
  <meta charset="UTF-8">

  <!-- Responsive viewport -->
  <meta name="viewport"
        content="width=device-width, initial-scale=1.0">

  <!-- Internet Explorer compatibility -->
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
</head>

Description 与 Keywords

description meta 标签对 SEO 最重要——搜索引擎在结果中标题下显示它(保持在 160 个字符以内)。keywords 基本被现代搜索引擎忽略。robots 控制索引:index/noindex、follow/nofollow。author 标注内容创建者。编写引人入胜的描述以提高点击率。

html
<meta name="description"
      content="Free HTML tutorials for beginners. Learn HTML tags, attributes, and semantic markup with examples.">

<meta name="keywords"
      content="HTML, tutorial, web development, markup">

<meta name="author" content="John Doe">
<meta name="robots" content="index, follow">

Open Graph(Facebook)

Open Graph(og:)meta 标签控制页面在 Facebook、LinkedIn 和其他平台上分享时的显示方式。og:title、og:description、og:image 和 og:url 最重要。og:type 可以是 article、website、product 等。图像应至少为 1200x630 像素。部署前用 Facebook 的 Sharing Debugger 测试。

html
<meta property="og:title" content="My Amazing Article">
<meta property="og:description" content="A brief description of the article.">
<meta property="og:image" content="https://example.com/image.jpg">
<meta property="og:url" content="https://example.com/article">
<meta property="og:type" content="article">
<meta property="og:site_name" content="My Website">
<meta property="og:locale" content="en_US">

Twitter Cards

Twitter Card meta 标签控制链接在 Twitter/X 上的显示方式。twitter:card 可以是 summary、summary_large_image 或 player。大图像卡片最吸引人。twitter:site 和 twitter:creator 链接到 Twitter 账户。如果存在 Open Graph 标签,Twitter 回退到它们,但显式 Twitter 标签提供更多控制。用 Twitter Card Validator 测试。

html
<meta name="twitter:card" content="summary_large_image">
<meta name="twitter:title" content="My Amazing Article">
<meta name="twitter:description" content="A brief description.">
<meta name="twitter:image" content="https://example.com/image.jpg">
<meta name="twitter:site" content="@mywebsite">
<meta name="twitter:creator" content="@author">

Canonical 与结构化数据

canonical 链接标签告诉搜索引擎页面的首选 URL,防止重复内容惩罚。JSON-LD 结构化数据帮助搜索引擎理解你的内容并启用富片段(搜索结果中的星级评分、面包屑、常见问题手风琴)。使用 schema.org 类型如 Article、Product、Event 或 FAQPage。用 Google 的 Rich Results Test 验证。

html
<!-- Canonical URL (prevents duplicate content issues) -->
<link rel="canonical" href="https://example.com/article">

<!-- Structured data (JSON-LD) -->
<script type="application/ld+json">
{
  "@context": "https://schema.org",
  "@type": "Article",
  "headline": "My Article Title",
  "author": "John Doe",
  "datePublished": "2024-01-15",
  "image": "https://example.com/image.jpg"
}
</script>
10

SVG 与 Canvas

SVG 基础

SVG(可缩放矢量图形)创建与分辨率无关的图形,缩放无质量损失。内联 SVG 可用 CSS 样式化并用 JavaScript 操作。常见形状:<circle>、<rect>、<line>、<ellipse>、<polygon>、<path>。SVG 适用于图标、徽标和图表。与光栅图像不同,SVG 在任何尺寸下都清晰。

html
<svg width="200" height="200" xmlns="http://www.w3.org/2000/svg">
  <circle cx="100" cy="100" r="50" fill="blue" stroke="black" stroke-width="2"/>
  <text x="100" y="105" text-anchor="middle" fill="white">Circle</text>
</svg>

<!-- Inline SVG (can be styled with CSS) -->
<svg width="100" height="100">
  <rect x="10" y="10" width="80" height="80" fill="red" rx="10"/>
</svg>

SVG 形状

SVG 形状使用基于坐标的属性。<rect> 需要 x、y、width、height;rx/ry 创建圆角。<circle> 需要 cx、cy(中心)和 r(半径)。<ellipse> 使用 rx 和 ry 表示不同的 x/y 半径。<line> 连接 x1,y1 到 x2,y2。<polygon> 接受点列表。fill 设置内部颜色,stroke 设置轮廓。

html
<svg width="300" height="200">
  <!-- Rectangle -->
  <rect x="10" y="10" width="80" height="50" fill="orange" rx="10" ry="10"/>

  <!-- Circle -->
  <circle cx="200" cy="50" r="40" fill="green"/>

  <!-- Ellipse -->
  <ellipse cx="100" cy="150" rx="60" ry="30" fill="purple"/>

  <!-- Line -->
  <line x1="10" y1="180" x2="290" y2="180" stroke="black" stroke-width="2"/>

  <!-- Polygon -->
  <polygon points="250,10 290,80 210,80" fill="pink"/>
</svg>

SVG 路径

<path> 是最强大的 SVG 元素,使用带命令的 d 属性:M(移动到)、L(线到)、H/V(水平/垂直线)、C(三次贝塞尔)、Q(二次贝塞尔)、A(弧)、Z(闭合路径)。大写 = 绝对坐标,小写 = 相对。viewBox 定义坐标系。SVG 图标使用路径创建可缩放、可样式化的图标。

html
<svg width="200" height="200">
  <!-- M=move, L=line, C=curve, Z=close -->
  <path d="M 10 10 L 100 10 L 100 100 Z" fill="none" stroke="black"/>

  <!-- Bezier curve -->
  <path d="M 10 100 C 50 10, 150 10, 190 100" fill="none" stroke="red"/>

  <!-- Arc -->
  <path d="M 10 100 A 90 90 0 0 1 190 100" fill="none" stroke="blue"/>
</svg>

<!-- SVG icon example -->
<svg width="24" height="24" viewBox="0 0 24 24" fill="currentColor">
  <path d="M12 2L2 7v10l10 5 10-5V7z"/>
</svg>

Canvas 基础

<canvas> 是通过 JavaScript 操作的位图绘图表面。用 getContext('2d') 获取 2D 上下文。与 SVG 不同,canvas 是基于像素的(不可缩放)且不属于 DOM。Canvas 更适合复杂动画、游戏和图像处理。SVG 更适合静态图形、图标和交互式图表。根据你的需求选择。

html
<canvas id="myCanvas" width="300" height="200"></canvas>

<script>
  const canvas = document.getElementById('myCanvas');
  const ctx = canvas.getContext('2d');

  // Draw a rectangle
  ctx.fillStyle = 'blue';
  ctx.fillRect(10, 10, 100, 80);

  // Draw a circle
  ctx.beginPath();
  ctx.arc(200, 50, 30, 0, Math.PI * 2);
  ctx.fillStyle = 'red';
  ctx.fill();

  // Draw text
  ctx.font = '20px Arial';
  ctx.fillText('Hello Canvas', 50, 150);
</script>

内联 SVG 图标

SVG <symbol> 定义可重用图标,用 <use href='#id'> 引用。这是 SVG 精灵技术——定义一次图标,到处使用。symbol 隐藏(display:none)并通过 <use> 实例化。图标通过 fill='currentColor' 从 CSS 'color' 继承颜色。此方法高效(一次定义,多次使用)且完全可样式化。

html
<!-- Reusable SVG symbol -->
<svg style="display:none">
  <symbol id="icon-home" viewBox="0 0 24 24">
    <path d="M12 2L2 12h3v8h6v-6h2v6h6v-8h3z"/>
  </symbol>
</svg>

<!-- Use the icon -->
<svg width="24" height="24" fill="currentColor">
  <use href="#icon-home"/>
</svg>

<!-- Styled icon -->
<svg width="32" height="32" class="icon" style="color: blue;">
  <use href="#icon-home"/>
</svg>
11

可访问性(ARIA)

ARIA 角色

ARIA 角色定义元素对屏幕阅读器的作用。地标角色(banner、navigation、main、complementary、contentinfo)让屏幕阅读器用户在页面区域间跳转。HTML5 语义元素(<header>、<nav>、<main>)具有隐式 ARIA 角色,因此添加 role 属性通常是多余的——但对旧浏览器有用。尽可能优先使用语义 HTML 而非 ARIA。

html
<!-- Landmark roles for page structure -->
<header role="banner">Site Header</header>
<nav role="navigation">Main Menu</nav>
<main role="main">Primary Content</main>
<aside role="complementary">Sidebar</aside>
<footer role="contentinfo">Footer</footer>
<form role="search">Search Form</form>

<!-- Document structure roles -->
<article role="article">Blog Post</article>
<section role="region" aria-label="Comments">Comments</section>

aria-label 与 aria-labelledby

aria-label 在没有可见标签时定义可访问名称(例如,仅图标按钮)。aria-labelledby 引用标记控件的可见文本元素的 ID——当屏幕上已有可见标签时有用。当列出多个 ID 时,它们的文本按顺序连接。当存在可见文本时优先使用 aria-labelledby,因为它保持标签同步。

html
<!-- aria-label provides accessible name directly -->
<button aria-label="Close menu" onclick="closeMenu()">✕</button>
<input type="search" aria-label="Search products" />

<!-- aria-labelledby references visible text -->
<div id="billing-label">Billing Address</div>
<input type="text" aria-labelledby="billing-label" />

<!-- Multiple labels combined -->
<span id="city">City</span>
<span id="required">required</span>
<input aria-labelledby="city required" />

aria-describedby 与工具提示

aria-describedby 通过元素 ID 将元素链接到附加描述文本。屏幕阅读器在标签后宣布此描述。它适用于帮助文本、提示和错误消息。在错误消息上使用 role='alert',以便它们出现时立即宣布。在带错误的字段上设置 aria-invalid='true',以便屏幕阅读器指示无效状态。

html
<label for="password">Password</label>
<input type="password" id="password"
       aria-describedby="pwd-help pwd-rules" />
<p id="pwd-help">Must be at least 8 characters</p>
<p id="pwd-rules">Include uppercase, number, and symbol</p>

<!-- Error messaging -->
<input type="email" id="email" aria-describedby="email-error" aria-invalid="true" />
<p id="email-error" role="alert">Please enter a valid email address</p>

实时区域(aria-live)

实时区域向屏幕阅读器用户宣布动态内容变更。aria-live='polite' 等待暂停后宣布(适用于状态更新)。aria-live='assertive' 立即中断(适用于严重错误)。aria-atomic='true' 读取整个区域内容而非仅变更部分。aria-relevant 指定哪些变更类型(添加、移除、文本)触发宣布。对 SPA、聊天应用和自动更新内容至关重要。

html
<!-- Polite: announces when user is idle -->
<div aria-live="polite" id="status">Saving...</div>

<!-- Assertive: announces immediately, interrupts -->
<div aria-live="assertive" role="alert" id="errors">
  Connection lost!
</div>

<!-- aria-atomic reads entire region, not just changes -->
<div aria-live="polite" aria-atomic="true" id="cart">
  3 items in cart
</div>

<!-- aria-relevant controls what changes are announced -->
<div aria-live="polite" aria-relevant="additions text">
  Chat messages appear here
</div>

焦点管理与 tabindex

tabindex 控制键盘焦点行为。tabindex='0' 使非交互元素(div、span)在自然 DOM 顺序中可聚焦——用于自定义小部件。tabindex='-1' 使元素仅可通过 JS .focus() 以编程方式聚焦——适用于模态和跳转目标。永远不要使用正 tabindex 值,因为它们覆盖自然 tab 顺序并造成混乱。始终将跳过链接作为页面上第一个可聚焦元素提供。

html
<!-- tabindex="0": element is focusable in natural order -->
<div tabindex="0" role="button" onkeypress="handleKey()">
  Custom clickable div
</div>

<!-- tabindex="-1": focusable only via JS, removed from tab order -->
<div tabindex="-1" id="modal">Modal content</div>
<script>
  document.getElementById('modal').focus();
</script>

<!-- tabindex="1+": DO NOT USE — breaks natural tab order -->
<!-- Avoid: <div tabindex="2"> -->

<!-- Skip link for keyboard users -->
<a href="#main-content" class="skip-link">Skip to main content</a>
<main id="main-content">...</main>

隐藏内容与仅屏幕阅读器

aria-hidden='true' 将元素从可访问性树中移除同时保持可见——用于装饰性图标、冗余文本或屏幕外内容。.sr-only 模式在视觉上隐藏文本但保持屏幕阅读器可读——对仅图标按钮至关重要。永远不要在可聚焦元素上使用 aria-hidden,因为这会在键盘和屏幕阅读器导航之间造成脱节。clip 技术是最稳健的视觉隐藏方法。

html
<!-- aria-hidden: visible but hidden from screen readers -->
<div aria-hidden="true">
  <i class="icon-decoration"></i> Decorative only
</div>

<!-- visually hidden but available to screen readers -->
<style>
.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;
}
</style>
<button>
  <span class="sr-only">Delete item</span>
  <i class="icon-trash" aria-hidden="true"></i>
</button>
12

HTML5 API(地理定位、存储、拖放)

地理定位 API

地理定位 API 让你请求用户的物理位置。浏览器始终提示权限——用户必须明确授予访问权限。getCurrentPosition 接受成功和错误回调以及选项对象(enableHighAccuracy、timeout、maximumAge)。对于持续跟踪,使用 watchPosition(),它返回一个 ID,你可以传递给 clearWatch()。始终优雅地处理错误(权限被拒绝、位置不可用、超时)。地理定位在现代浏览器中需要 HTTPS。

html
<!-- Request user location -->
<button onclick="getLocation()">Get My Location</button>
<p id="demo"></p>

<script>
function getLocation() {
  if (navigator.geolocation) {
    navigator.geolocation.getCurrentPosition(
      (pos) => {
        document.getElementById("demo").innerHTML =
          "Lat: " + pos.coords.latitude +
          "<br>Lon: " + pos.coords.longitude;
      },
      (err) => {
        alert("Error: " + err.message);
      },
      { enableHighAccuracy: true, timeout: 5000 }
    );
  } else {
    alert("Geolocation not supported.");
  }
}
</script>

Web 存储(localStorage 与 sessionStorage)

Web 存储在客户端提供键值存储。localStorage 无限期持久化;sessionStorage 在标签关闭时清除。两者都仅存储字符串——对对象使用 JSON.stringify/parse。每个源的存储限制约为 5-10MB。与 cookie 不同,存储数据不随每个 HTTP 请求发送。请注意:存储是同步的并阻塞主线程,且可被同源上的任何脚本访问(不适用于敏感数据)。

html
<!-- localStorage: persists until cleared -->
<script>
// Store data
localStorage.setItem("username", "alice");
localStorage.setItem("prefs", JSON.stringify({theme: "dark", lang: "en"}));

// Retrieve data
const user = localStorage.getItem("username");
const prefs = JSON.parse(localStorage.getItem("prefs") || "{}");

// Remove single item
localStorage.removeItem("username");

// Clear all items
localStorage.clear();

// sessionStorage: cleared when tab closes
sessionStorage.setItem("tempToken", "abc123");
</script>

拖放 API

HTML5 拖放 API 支持原生拖动交互。在源元素上设置 draggable='true'。ondragstart 在拖动开始时触发——使用 dataTransfer.setData() 存储拖动数据。放置目标上的 ondragover 必须调用 preventDefault() 以允许放置。ondrop 处理实际放置——用 dataTransfer.getData() 检索数据。你可以使用 event.dataTransfer.files 从操作系统将文件拖入浏览器。对于复杂应用,考虑 SortableJS 等库以获得更好的跨浏览器支持。

html
<div id="drag1" draggable="true" ondragstart="drag(event)">
  Drag me!
</div>
<div id="dropzone" ondrop="drop(event)" ondragover="allowDrop(event)">
  Drop here
</div>

<script>
function allowDrop(ev) {
  ev.preventDefault(); // necessary to allow dropping
}

function drag(ev) {
  ev.dataTransfer.setData("text", ev.target.id);
}

function drop(ev) {
  ev.preventDefault();
  const data = ev.dataTransfer.getData("text");
  ev.target.appendChild(document.getElementById(data));
}
</script>

页面可见性 API

页面可见性 API 告诉你页面是否对用户可见(未最小化、不在后台标签中)。使用它在用户不看时暂停昂贵的动画、视频播放或轮询——节省电池和 CPU。document.hidden 是布尔值;document.visibilityState 返回 'visible'、'hidden' 或 'prerender'。visibilitychange 事件在转换时触发。这比 blur/focus 更可靠地检测实际可见性。

html
<script>
document.addEventListener("visibilitychange", () => {
  if (document.hidden) {
    console.log("Tab is hidden — pause video/animation");
    video.pause();
  } else {
    console.log("Tab is visible — resume");
    video.play();
  }
});

// Check current state
if (document.visibilityState === "visible") {
  console.log("Page is visible");
}
</script>

全屏 API

全屏 API 让你以全屏模式显示任何元素。requestFullscreen() 必须由用户手势(点击/按键)触发。不同浏览器可能需要供应商前缀(webkit、moz、ms)。fullscreenchange 事件在进入或退出全屏时触发。document.fullscreenElement 引用当前全屏元素(如果不在全屏则为 null)。使用 :fullscreen CSS 伪类以不同方式样式化全屏元素。

html
<button onclick="openFullscreen()">Fullscreen</button>
<button onclick="closeFullscreen()">Exit</button>
<div id="container">Content to fullscreen</div>

<script>
function openFullscreen() {
  const elem = document.getElementById("container");
  if (elem.requestFullscreen) {
    elem.requestFullscreen();
  } else if (elem.webkitRequestFullscreen) {
    elem.webkitRequestFullscreen();
  }
}

function closeFullscreen() {
  if (document.exitFullscreen) {
    document.exitFullscreen();
  }
}

document.addEventListener("fullscreenchange", () => {
  console.log("Fullscreen:", !!document.fullscreenElement);
});
</script>
13

Web 组件

自定义元素

自定义元素让你创建带封装行为的可重用 HTML 标签。类扩展 HTMLElement(或 HTMLElement 子类)。connectedCallback 在元素添加到 DOM 时触发——用于渲染。attributeChangedCallback 响应属性变更,但仅对 observedAttributes 中列出的属性。名称必须包含连字符(例如,'my-greeting')以避免与原生 HTML 冲突。customElements.define() 注册元素。除非需要样式封装,否则避免使用 Shadow DOM。

html
<my-greeting name="World"></my-greeting>

<script>
class MyGreeting extends HTMLElement {
  constructor() {
    super();
    this.name = this.getAttribute("name") || "Guest";
  }
  connectedCallback() {
    this.innerHTML = `<h2>Hello, ${this.name}!</h2>`;
  }
  // Observe attribute changes
  static get observedAttributes() {
    return ["name"];
  }
  attributeChangedCallback(name, oldVal, newVal) {
    if (name === "name") {
      this.name = newVal;
      this.connectedCallback();
    }
  }
}
customElements.define("my-greeting", MyGreeting);
</script>

Shadow DOM

Shadow DOM 提供样式和 DOM 封装——shadow 树内的样式不会泄漏出去,页面样式也不会泄漏进来。attachShadow({mode:'open'}) 创建可通过 element.shadowRoot 访问的 shadow 根。mode:'closed' 拒绝外部访问(很少使用)。<slot> 元素是 light DOM 子元素投影到的占位符。命名插槽(<slot name='title'>)匹配带匹配 slot 属性的子元素。::slotted() 样式化投影内容。Shadow DOM 是构建自包含、可重用组件的关键。

html
<my-card>
  <span slot="title">Card Title</span>
  <p slot="body">Card content here.</p>
</my-card>

<script>
class MyCard extends HTMLElement {
  constructor() {
    super();
    const shadow = this.attachShadow({ mode: "open" });
    shadow.innerHTML = `
      <style>
        .card { border: 1px solid #ccc; padding: 16px; border-radius: 8px; }
        ::slotted([slot="title"]) { font-size: 1.5em; font-weight: bold; }
      </style>
      <div class="card">
        <slot name="title"></slot>
        <slot name="body"></slot>
      </div>
    `;
  }
}
customElements.define("my-card", MyCard);
</script>

HTML 模板

<template> 元素持有在页面加载时不渲染的 HTML——其内容是惰性的(脚本不运行、图像不加载、样式不应用)。通过 .content(一个 DocumentFragment)访问内容并用 cloneNode(true) 克隆以实例化。模板适用于由 JavaScript 生成的重复结构。与 innerHTML 不同,模板被解析一次且可重复克隆而无需重新解析。与自定义元素和 Shadow DOM 结合,它们构成 Web 组件标准。

html
<template id="row-template">
  <tr>
    <td class="name"></td>
    <td class="email"></td>
  </tr>
</template>

<script>
function addRow(name, email) {
  const template = document.getElementById("row-template");
  const clone = template.content.cloneNode(true);
  clone.querySelector(".name").textContent = name;
  clone.querySelector(".email").textContent = email;
  document.querySelector("tbody").appendChild(clone);
}

addRow("Alice", "[email protected]");
addRow("Bob", "[email protected]");
</script>

生命周期回调

自定义元素有四个生命周期回调。constructor() 在元素创建时运行(避免在此进行繁重工作)。connectedCallback() 在添加到 DOM 时触发——适用于设置和渲染。disconnectedCallback() 在移除时触发——用于清理(事件监听器、计时器)。adoptedCallback() 在通过 adoptNode() 移动到新文档时触发(罕见)。attributeChangedCallback() 仅对观察的属性触发。升级顺序始终是:constructor → connectedCallback。属性变更可在 connectedCallback 之前触发。

html
<script>
class LifecycleDemo extends HTMLElement {
  constructor() {
    super();
    console.log("1. constructor: element created");
  }
  connectedCallback() {
    console.log("2. connected: added to DOM");
  }
  disconnectedCallback() {
    console.log("3. disconnected: removed from DOM");
  }
  adoptedCallback() {
    console.log("4. adopted: moved to new document");
  }
  attributeChangedCallback(attr, oldVal, newVal) {
    console.log(`5. attribute '${attr}' changed: ${oldVal} -> ${newVal}`);
  }
  static get observedAttributes() {
    return ["data-status"];
  }
}
customElements.define("lifecycle-demo", LifecycleDemo);
</script>

自定义内置元素

自定义内置元素扩展原生 HTML 元素(如 button、input、div)以继承其内置行为。使用 'is' 属性而非自定义标签名。customElements.define() 的第三个参数指定要扩展的原生元素。当你想增强现有元素(例如,向 <input> 添加验证)而非创建全新元素时,这很有用。注意:Safari 不支持自定义内置元素——考虑使用自主自定义元素以获得跨浏览器兼容性。

html
<!-- Extend a native button -->
<button is="my-button">Click me</button>

<script>
class MyButton extends HTMLButtonElement {
  constructor() {
    super();
    this.addEventListener("click", () => {
      this.textContent = "Clicked!";
      this.style.background = "lightgreen";
    });
  }
  connectedCallback() {
    this.style.fontWeight = "bold";
  }
}
customElements.define("my-button", MyButton,
  { extends: "button" });
</script>
14

媒体元素(音频与视频)

Video 元素

<video> 元素无需插件嵌入视频。controls 属性显示内置播放/暂停/音量控件。poster 指定播放前的预览图像。多个 <source> 标签提供格式回退——浏览器使用它支持的第一个(MP4/H.264 支持最广泛,WebM 开放且高效)。<track> 元素通过 WebVTT 文件添加字幕、说明或描述。始终为非常旧的浏览器包含回退文本。对于自动播放,添加 muted 属性——大多数浏览器阻止带声音的自动播放。

html
<video width="640" height="360" controls poster="preview.jpg">
  <source src="movie.mp4" type="video/mp4">
  <source src="movie.webm" type="video/webm">
  <track kind="subtitles" src="subs_en.vtt"
         srclang="en" label="English" default>
  <track kind="captions" src="caps_en.vtt"
         srclang="en" label="English Captions">
  Your browser does not support the video tag.
</video>

Audio 元素

<audio> 元素嵌入声音。controls 显示内置播放器 UI。没有 controls,元素不可见——通过 JavaScript 控制播放(play()、pause()、volume、currentTime)。preload='auto' 建议浏览器缓冲文件;'metadata' 仅加载时长/信息;'none' 在播放前不加载任何内容。MP3 有通用支持;OGG Vorbis 是开放的但 Safari 不支持。对于游戏或精确计时,使用 Web Audio API 而非 <audio> 以获得更低延迟和效果。

html
<!-- Basic audio player -->
<audio controls>
  <source src="song.mp3" type="audio/mpeg">
  <source src="song.ogg" type="audio/ogg">
  Your browser does not support audio.
</audio>

<!-- Audio controlled by JavaScript -->
<audio id="player" src="song.mp3" preload="auto"></audio>
<button onclick="document.getElementById('player').play()">Play</button>
<button onclick="document.getElementById('player').pause()">Pause</button>
<button onclick="document.getElementById('player').volume += 0.1">Vol+</button>

Picture 与 srcset(响应式图像)

响应式图像通过提供适当尺寸的图像来改善性能。带宽度描述符(480w、800w)的 srcset 加上 sizes 提示让浏览器选择最佳图像——这优先用于分辨率切换。带媒体查询的 <picture> 启用艺术指导(不同屏幕的不同裁剪)。带 type 属性的 <source> 提供现代格式回退(WebP、AVIF),JPG/PNG 作为回退。始终在 <picture> 的最后一个子元素中包含常规 <img> 用于回退和可访问性。

html
<!-- srcset: let browser choose resolution -->
<img src="small.jpg"
     srcset="small.jpg 480w, medium.jpg 800w, large.jpg 1200w"
     sizes="(max-width: 600px) 480px, 800px"
     alt="Responsive image">

<!-- picture: art direction with different images -->
<picture>
  <source media="(max-width: 600px)" srcset="mobile.jpg">
  <source media="(max-width: 1200px)" srcset="tablet.jpg">
  <img src="desktop.jpg" alt="Art-directed image">
</picture>

<!-- Modern format with fallback -->
<picture>
  <source type="image/webp" srcset="photo.webp">
  <source type="image/avif" srcset="photo.avif">
  <img src="photo.jpg" alt="With format fallback">
</picture>

iframe 嵌入

iframe 在当前页面内嵌入另一个文档。title 属性对可访问性至关重要——屏幕阅读器会宣布它。sandbox 属性为安全限制 iframe 的功能:空值阻止一切;添加令牌(allow-scripts、allow-forms、allow-same-origin)以重新启用特定功能。loading='lazy' 延迟到接近视口时加载。allow 指定功能策略(camera、microphone、autoplay)。请谨慎嵌入不受信任的内容——对其进行沙箱处理。跨源 iframe 无法通过 JavaScript 访问。

html
<!-- Basic iframe -->
<iframe src="https://example.com"
        width="600" height="400"
        title="Embedded Content">
</iframe>

<!-- Sandboxed iframe for security -->
<iframe src="untrusted.html"
        sandbox="allow-scripts allow-same-origin"
        loading="lazy">
</iframe>

<!-- YouTube embed -->
<iframe src="https://www.youtube.com/embed/VIDEO_ID"
        allow="accelerometer; autoplay; encrypted-media"
        allowfullscreen
        title="YouTube video">
</iframe>

Embed 与 Object

<embed> 和 <object> 是较旧的嵌入方法,很大程度上已被 <iframe> 和 <video> 取代。<embed> 是自闭合且简单的,但不提供回退内容。<object> 更灵活——标签内的内容在嵌入资源无法显示时作为回退。对需要回退的 PDF 和 SVG 使用 <object>。对于现代 Web 开发,对外部页面优先使用 <iframe>,对媒体使用 <video>/<audio>,对矢量图形使用内联 <svg>。<embed> 主要用于插件内容(Flash,现已弃用)。

html
<!-- embed: simple, self-closing -->
<embed src="animation.svg" type="image/svg+xml"
       width="300" height="200">

<!-- object: more flexible with fallback -->
<object data="document.pdf" type="application/pdf"
        width="100%" height="600px">
  <p>Unable to display PDF.
     <a href="document.pdf">Download it instead.</a>
  </p>
</object>

<!-- embed YouTube without iframe -->
<embed src="https://www.youtube.com/v/VIDEO_ID"
       type="application/x-shockwave-flash"
       width="560" height="315">
15

输入类型深入

日期与时间输入

HTML5 日期/时间输入类型无需 JavaScript 库即可提供原生选择器。type='date' 提供日历;type='time' 提供时间选择器;type='datetime-local' 结合两者。type='month' 和 type='week' 选择月份和周。min/max 约束可选范围。step 定义粒度(例如,step='1800' 表示以秒为单位的 30 分钟间隔)。显示格式因浏览器/区域设置而异,但提交的值始终是 ISO 8601 格式(YYYY-MM-DD)。并非所有浏览器都同样支持所有类型——跨浏览器测试。

html
<label for="birthday">Birthday:</label>
<input type="date" id="birthday" min="1900-01-01" max="2025-12-31">

<label for="appt">Appointment:</label>
<input type="time" id="appt" min="09:00" max="17:00" step="1800">

<label for="meeting">Meeting:</label>
<input type="datetime-local" id="meeting">

<label for="month">Pick a month:</label>
<input type="month" id="month">

<label for="week">Pick a week:</label>
<input type="week" id="week">

数字与范围输入

type='number' 提供带上下箭头的微调控件。min、max 和 step 约束值。对于货币,使用 step='0.01'。type='range' 渲染滑块——始终将其与显示当前值的 <output> 或可见标签配对,因为默认不显示值。数字输入过滤非数字输入但仍允许某些无效字符;在服务器端验证。对于不需要微调的数量,考虑 type='text' 配 inputmode='numeric' 和 pattern 验证。

html
<label for="qty">Quantity (1-100):</label>
<input type="number" id="qty" min="1" max="100"
       step="1" value="10">

<label for="price">Price ($):</label>
<input type="number" id="price" min="0" step="0.01"
       placeholder="0.00">

<label for="volume">Volume:</label>
<input type="range" id="volume" min="0" max="100"
       value="50" oninput="out.value=this.value">
<output id="out">50</output>

颜色与文件输入

type='color' 打开原生颜色选择器并返回十六进制值(#rrggbb)。type='file' 让用户选择文件。accept 属性按 MIME 类型或扩展名过滤(image/*、.pdf、image/png)。multiple 属性允许选择多个文件。通过 JavaScript 中的 files 属性(FileList)访问选定文件。使用 URL.createObjectURL() 为图像创建本地预览 URL。对于大文件上传,考虑分块上传或使用 File API 进行进度跟踪。始终在服务器端验证文件类型和大小。

html
<label for="color">Pick a color:</label>
<input type="color" id="color" value="#ff0000">

<label for="avatar">Upload avatar:</label>
<input type="file" id="avatar" accept="image/*">

<label for="docs">Upload documents:</label>
<input type="file" id="docs" accept=".pdf,.doc,.docx" multiple>

<!-- File input with image preview -->
<input type="file" accept="image/*" onchange="preview(this)">
<img id="preview-img" hidden>
<script>
function preview(input) {
  const file = input.files[0];
  if (file) {
    document.getElementById("preview-img").src =
      URL.createObjectURL(file);
    document.getElementById("preview-img").hidden = false;
  }
}
</script>

datalist(输入建议)

<datalist> 为输入字段提供自动补全建议。通过匹配 datalist id 的 list 属性将其链接到输入。与 <select> 不同,用户仍可输入任何值——建议是可选的。它适用于文本、数字、日期、颜色和范围输入。浏览器在用户输入时显示匹配的建议。这是简单用例下 JavaScript 自动补全库的轻量级替代方案。注意:不支持 datalist 内选项的样式化——它们以浏览器默认值渲染。

html
<label for="browser">Favorite browser:</label>
<input list="browsers" id="browser" name="browser">
<datalist id="browsers">
  <option value="Chrome">
  <option value="Firefox">
  <option value="Safari">
  <option value="Edge">
  <option value="Opera">
</datalist>

<!-- Works with various input types -->
<label for="color">Choose color:</label>
<input type="color" list="preset-colors" id="color">
<datalist id="preset-colors">
  <option value="#ff0000">
  <option value="#00ff00">
  <option value="#0000ff">
</datalist>

表单验证属性

HTML5 通过属性提供内置客户端验证。required 防止空值提交。type='email'/'url' 自动验证格式。minlength/maxlength 约束文本长度。min/max 约束数字和日期。pattern 使用正则表达式进行自定义验证。浏览器显示默认错误气泡。用 setCustomValidity() 自定义消息——但始终在输入时清除它(setCustomValidity(''))以避免粘性错误。客户端验证改善用户体验,但必须与服务器端验证配对以确保安全,因为它可以被绕过。

html
<form>
  <label>Email: <input type="email" required></label>
  <label>Username:
    <input type="text" required minlength="3" maxlength="20"
           pattern="[a-zA-Z0-9_]+">
  </label>
  <label>Age:
    <input type="number" min="18" max="120" required>
  </label>
  <label>Website:
    <input type="url" placeholder="https://example.com">
  </label>
  <button type="submit">Submit</button>
</form>

<!-- Custom validation message -->
<input type="text" required
  oninvalid="this.setCustomValidity('Please enter your name')"
  oninput="this.setCustomValidity('')">
16

Service Worker 与 PWA

Service Worker 注册

Service worker 是在后台运行的 JavaScript 文件,与网页分离,支持离线功能、推送通知和后台同步。从主页面注册它——它必须通过 HTTPS 提供。Service worker 文件的位置决定其作用域(它控制其目录和子目录中的页面)。注册是异步的,每个作用域仅发生一次。首次访问后,SW 在后续页面加载时激活。使用 load 事件延迟注册直到页面可交互之后。

html
<!-- Register a service worker in main page -->
<script>
if ("serviceWorker" in navigator) {
  window.addEventListener("load", () => {
    navigator.serviceWorker
      .register("/sw.js")
      .then((reg) => {
        console.log("SW registered:", reg.scope);
      })
      .catch((err) => {
        console.log("SW registration failed:", err);
      });
  });
}
</script>

Service Worker:缓存

Service worker 生命周期有三个阶段:install(缓存核心资产)、activate(清理旧缓存)和 fetch(拦截网络请求)。此处显示的缓存优先策略立即提供缓存响应,回退到网络。其他策略包括网络优先(尝试网络,回退到缓存)和 stale-while-revalidate(提供缓存,在后台更新)。提升 CACHE_NAME 以触发缓存更新。Cache API 存储 Request/Response 对。Service worker 仅在需要时运行,且可被浏览器终止以节省内存。

html
// sw.js - Cache assets for offline use
const CACHE_NAME = "my-app-v1";
const ASSETS = ["/", "/index.html", "/style.css", "/app.js"];

// Install: cache core assets
self.addEventListener("install", (e) => {
  e.waitUntil(
    caches.open(CACHE_NAME).then((cache) => cache.addAll(ASSETS))
  );
});

// Fetch: serve from cache, fall back to network
self.addEventListener("fetch", (e) => {
  e.respondWith(
    caches.match(e.request).then((cached) => {
      return cached || fetch(e.request);
    })
  );
});

// Activate: clean old caches
self.addEventListener("activate", (e) => {
  e.waitUntil(
    caches.keys().then((keys) =>
      Promise.all(
        keys.filter((k) => k !== CACHE_NAME)
            .map((k) => caches.delete(k))
      )
    )
  );
});

Web 应用清单

Web 应用清单是使 Web 应用可安装(添加到主屏幕)的 JSON 文件。name 是全名;short_name 在主屏幕图标上显示。display:'standalone' 隐藏浏览器 UI,使其看起来像原生应用。theme_color 影响浏览器界面颜色。icons 必须至少包含 192px 和 512px 尺寸。'purpose':'maskable' 让 Android 将图标适应不同形状。有效清单加上已注册的 service worker 是 PWA 的最低要求。用 Lighthouse 测试 PWA 合规性。

html
<!-- Link the manifest in HTML -->
<link rel="manifest" href="manifest.json">

<!-- manifest.json -->
{
  "name": "My PWA App",
  "short_name": "MyApp",
  "start_url": "/",
  "display": "standalone",
  "background_color": "#ffffff",
  "theme_color": "#1976d2",
  "orientation": "portrait",
  "icons": [
    {
      "src": "/icon-192.png",
      "sizes": "192x192",
      "type": "image/png"
    },
    {
      "src": "/icon-512.png",
      "sizes": "512x512",
      "type": "image/png",
      "purpose": "maskable"
    }
  ]
}

推送通知

推送通知让你即使在标签关闭时也能重新吸引用户。首先用 Notification.requestPermission() 请求权限。然后通过 PushManager 订阅——订阅对象包含端点 URL 和密钥。将此订阅发送到你的服务器,服务器使用它通过 Web Push API(带 VAPID 密钥进行身份验证)发送推送消息。Service worker 的 push 事件处理程序显示通知。userVisibleOnly:true 表示每次推送都必须显示通知(无静默推送)。需要 HTTPS 和 service worker。

html
<!-- Request notification permission -->
<button onclick="subscribe()">Enable Notifications</button>

<script>
async function subscribe() {
  const permission = await Notification.requestPermission();
  if (permission !== "granted") return;

  const reg = await navigator.serviceWorker.ready;
  const subscription = await reg.pushManager.subscribe({
    userVisibleOnly: true,
    applicationServerKey: urlBase64ToUint8Array(VAPID_KEY)
  });
  // Send subscription to your server
  await fetch("/api/subscribe", {
    method: "POST",
    body: JSON.stringify(subscription)
  });
}
</script>

<!-- In sw.js: handle push events -->
self.addEventListener("push", (e) => {
  const data = e.data.json();
  e.waitUntil(
    self.registration.showNotification(data.title, {
      body: data.body,
      icon: "/icon-192.png",
      badge: "/badge.png"
    })
  );
});

后台同步

后台同步让你将操作推迟到用户有稳定连接时。当你注册同步事件时,浏览器在连接恢复时触发它——即使用户已关闭标签。同步事件处理程序(在 service worker 中)执行延迟工作。如果工作抛出错误,浏览器自动以指数退避重试。e.tag 标识同步类型——每个标签仅排队一个同步。使用 IndexedDB 存储待处理数据。这适用于消息应用、表单提交和数据同步。定期同步(用于定期更新)在 Chrome 中可用但并非所有浏览器都支持。

html
// Register a sync event from the page
navigator.serviceWorker.ready.then((reg) => {
  return reg.sync.register("send-messages");
});

// Handle sync in sw.js
self.addEventListener("sync", (e) => {
  if (e.tag === "send-messages") {
    e.waitUntil(sendPendingMessages());
  }
});

async function sendPendingMessages() {
  const messages = await getMessagesFromIndexedDB();
  for (const msg of messages) {
    try {
      await fetch("/api/messages", {
        method: "POST",
        body: JSON.stringify(msg)
      });
      await deleteMessageFromIndexedDB(msg.id);
    } catch (err) {
      throw err; // triggers retry
    }
  }
}
17

Meta 标签深入(Open Graph 与 Twitter)

Open Graph 标签(Facebook)

Open Graph(OG)标签控制页面在 Facebook、LinkedIn 和大多数社交平台上分享时的显示方式。og:title 是标题;og:description 是摘要;og:image 是预览图像(推荐 1200x630px)。og:type(website、article、video.movie)影响渲染。对于文章,添加 article:published_time、article:author 和 article:section 以获得更丰富的预览。始终指定 og:image 尺寸以加快渲染。用 Facebook 的 Sharing Debugger 测试。OG 标签对社交媒体营销和点击率至关重要。

html
<head>
  <meta property="og:title" content="My Awesome Article">
  <meta property="og:description" content="A deep dive into modern web development.">
  <meta property="og:type" content="article">
  <meta property="og:url" content="https://example.com/article">
  <meta property="og:image" content="https://example.com/img/og.jpg">
  <meta property="og:image:width" content="1200">
  <meta property="og:image:height" content="630">
  <meta property="og:site_name" content="My Website">
  <meta property="og:locale" content="en_US">
  <meta property="article:published_time" content="2025-01-15T08:00:00Z">
  <meta property="article:author" content="Jane Doe">
</head>

Twitter Card 标签

Twitter Card 标签控制链接在 X(Twitter)上的显示方式。twitter:card 类型可以是 'summary'(小图像)、'summary_large_image'(文本上方大图像)、'player'(视频/音频)或 'app'。twitter:site 是站点的 @handle;twitter:creator 是作者的 @handle。图像应至少为 300x157px(summary)或 600x314px(大图像)。twitter:image:alt 改善可访问性。用 Twitter 的 Card Validator 测试。即使你有 OG 标签,添加 Twitter 特定标签也让你对 Twitter 预览有更多控制。

html
<head>
  <meta name="twitter:card" content="summary_large_image">
  <meta name="twitter:site" content="@mywebsite">
  <meta name="twitter:creator" content="@janedoe">
  <meta name="twitter:title" content="My Awesome Article">
  <meta name="twitter:description" content="A deep dive into modern web dev.">
  <meta name="twitter:image" content="https://example.com/img/twitter.jpg">
  <meta name="twitter:image:alt" content="Article cover image">
</head>

Viewport 与移动 Meta

viewport meta 标签对响应式设计至关重要——没有它,移动浏览器以桌面宽度渲染页面并缩小。width=device-width 匹配设备宽度;initial-scale=1 设置缩放级别。避免禁用用户缩放(user-scalable=no),因为它违反 WCAG 可访问性指南。apple-mobile-web-app-capable 使应用在添加到 iOS 主屏幕时全屏。theme-color 影响移动 Chrome 和 Safari 上的浏览器地址栏颜色。这些移动特定标签显著改善 PWA 和移动体验。

html
<head>
  <!-- Responsive viewport -->
  <meta name="viewport"
        content="width=device-width, initial-scale=1.0">

  <!-- Disable zoom (accessibility concern — avoid) -->
  <meta name="viewport"
        content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">

  <!-- iOS Safari: full-screen web app -->
  <meta name="apple-mobile-web-app-capable" content="yes">
  <meta name="apple-mobile-web-app-status-bar-style"
        content="black-translucent">
  <meta name="apple-mobile-web-app-title" content="MyApp">

  <!-- Android Chrome theme color -->
  <meta name="theme-color" content="#1976d2">
</head>

HTTP-Equiv 与 Charset

http-equiv meta 标签直接在 HTML 中模拟 HTTP 头。charset 必须是 <head> 中的第一个元素且在前 1024 字节内,以防止基于编码的 XSS 攻击。http-equiv='refresh' 在延迟后重定向——为 SEO 优先使用服务器端重定向。通过 meta 的 Content-Security-Policy 受限(无 report-uri、无 frame-ancestors)——优先使用 HTTP 头。通过 meta 的 Cache-Control 不可靠——改用 HTTP 头。X-UA-Compatible 强制 IE 使用其最新渲染引擎。尽可能优先使用真正的 HTTP 头而非 http-equiv。

html
<head>
  <!-- Character encoding (must be first in head) -->
  <meta charset="UTF-8">

  <!-- Refresh/redirect after 5 seconds -->
  <meta http-equiv="refresh" content="5; url=https://example.com">

  <!-- Set content security policy -->
  <meta http-equiv="Content-Security-Policy"
        content="default-src 'self'; script-src 'self'">

  <!-- X-UA-Compatible for IE -->
  <meta http-equiv="X-UA-Compatible" content="IE=edge">

  <!-- Cache control -->
  <meta http-equiv="Cache-Control"
        content="no-cache, no-store, must-revalidate">
</head>

结构化数据(JSON-LD)

JSON-LD(JavaScript Object Notation for Linked Data)是 Google 推荐的结构化数据格式。它帮助搜索引擎理解你的内容,在搜索结果中启用富片段(星级评分、事件日期、面包屑、常见问题手风琴)。常见类型包括 Article、Product、Recipe、Event、LocalBusiness 和 FAQPage。将脚本放在 <head> 中,type='application/ld+json'。用 Google 的 Rich Results Test 验证。结构化数据不保证富片段但使你有资格。它是提高搜索结果点击率的强大 SEO 工具。

html
<head>
  <script type="application/ld+json">
  {
    "@context": "https://schema.org",
    "@type": "Article",
    "headline": "My Awesome Article",
    "author": {
      "@type": "Person",
      "name": "Jane Doe"
    },
    "datePublished": "2025-01-15",
    "image": "https://example.com/img/article.jpg",
    "publisher": {
      "@type": "Organization",
      "name": "My Website",
      "logo": {
        "@type": "ImageObject",
        "url": "https://example.com/logo.png"
      }
    }
  }
  </script>
</head>
18

语义化 HTML 深入

Article 与 Section 与 Div

选择正确的容器元素对可访问性和 SEO 很重要。<article> 用于可以独立分发的内容(博客文章、新闻、评论、产品卡片)。<section> 分组主题相关的内容——它应始终有标题。<div> 是当没有语义元素适合时用于布局/样式化的最后手段。嵌套的 <article> 元素(如文章内的评论)是有效的。经验法则:如果内容可以通过 RSS 联合分发,使用 <article>;如果是主题章节,使用 <section>;如果纯粹用于布局,使用 <div>。

html
<!-- <article>: self-contained, syndicable content -->
<article>
  <h2>Blog Post Title</h2>
  <p>Content that makes sense on its own...</p>
  <article><h3>Comment 1</h3><p>...</p></article>
</article>

<!-- <section>: thematic grouping with a heading -->
<section>
  <h2>Chapter 1: Introduction</h2>
  <p>Related content...</p>
</section>

<!-- <div>: generic container, no semantic meaning -->
<div class="layout-wrapper">
  <div class="grid-item">Styling hook only</div>
</div>

Figure 与 Figcaption

<figure> 表示从主文本引用的自包含内容——图像、图表、代码列表或引用。<figcaption> 提供标题/图例。与普通 <img> 不同,figure/figcaption 在语义上将视觉与其描述关联。屏幕阅读器宣布图形及其标题。图形可以相对于引用它的文本定位在任何位置。对任何有标题并按编号引用的内容(图 1、列表 1)使用 <figure>。对于纯装饰性图像,使用带空 alt 的普通 <img>。

html
<figure>
  <img src="chart.png" alt="Sales growth chart showing 40% increase">
  <figcaption>
    Figure 1: Quarterly sales growth from Q1 to Q4 2025.
    Data source: Internal sales database.
  </figcaption>
</figure>

<!-- Figure can contain code blocks too -->
<figure>
  <pre><code>const x = 42;</code></pre>
  <figcaption>Listing 1: Variable declaration example</figcaption>
</figure>

Details 与 Summary(披露)

<details> 和 <summary> 无需任何 JavaScript 即可创建原生可折叠(手风琴)部分。点击 <summary> 切换内容。open 属性默认展开。toggle 事件在展开/折叠时触发。这非常适合常见问题、设置面板和渐进式披露。屏幕阅读器将其宣布为披露小部件。你可以用 CSS 样式化默认三角标记(summary::-webkit-details-marker 或 list-style)。对于复杂交互,你可能仍需要 JavaScript,但对于简单切换,此原生解决方案是理想的。

html
<!-- Native collapsible without JavaScript -->
<details>
  <summary>Click to expand FAQ</summary>
  <p>Here is the hidden answer that appears when expanded.</p>
</details>

<!-- Open by default -->
<details open>
  <summary>Already expanded</summary>
  <p>This content is visible on page load.</p>
</details>

<!-- Nested disclosures -->
<details>
  <summary>Level 1</summary>
  <details>
    <summary>Level 2</summary>
    <p>Deeply nested content</p>
  </details>
</details>

Time 与 Mark 元素

<time> 包裹人类可读的日期/时间,带 ISO 8601 格式的机器可读 datetime 属性。这帮助搜索引擎、日历和辅助技术正确解析日期。datetime 支持日期(YYYY-MM-DD)、时间(HH:MM)、带时区的日期时间以及持续时间(PT2H30M)。<mark> 表示为相关性高亮的文本,如搜索词匹配。与 <strong> 或 <em>(表示重要性/强调)不同,<mark> 表示上下文相关性。两者都改善语义和 SEO。

html
<!-- <time>: machine-readable dates/times -->
<p>Published on
  <time datetime="2025-01-15">January 15, 2025</time>
</p>
<p>Event at
  <time datetime="2025-03-20T14:30-05:00">2:30 PM EST</time>
</p>
<p>Duration:
  <time datetime="PT2H30M">2 hours 30 minutes</time>
</p>

<!-- <mark>: highlighted/relevant text -->
<p>Search results: the keyword
  <mark>HTML5</mark> appears in 3 documents.
</p>

Dialog 元素

<dialog> 元素无需库即可提供原生模态和非模态对话框。showModal() 以模态方式打开它(带背景、阻止页面交互);show() 以非模态方式打开。close() 关闭它。form method='dialog' 在提交时关闭对话框,按钮的值作为 returnValue。::backdrop 伪元素样式化模态覆盖层。ESC 键自动关闭模态对话框。顶层渲染意味着对话框无论 z-index 如何都出现在所有其他内容之上。close 事件在关闭后触发。这现在得到良好支持并取代了许多 JavaScript 模态库。

html
<!-- Native modal dialog -->
<dialog id="myDialog">
  <h2>Confirm Action</h2>
  <p>Are you sure you want to proceed?</p>
  <form method="dialog">
    <button value="cancel">Cancel</button>
    <button value="confirm">Confirm</button>
  </form>
</dialog>

<button onclick="document.getElementById('myDialog').showModal()">
  Open Modal
</button>

<script>
const dialog = document.getElementById("myDialog");
dialog.addEventListener("close", () => {
  console.log("Dialog closed with:", dialog.returnValue);
});
// Close on backdrop click
dialog.addEventListener("click", (e) => {
  if (e.target === dialog) dialog.close();
});
</script>
19

高级表单与输入

Fieldset 与 Legend

<fieldset> 分组相关表单控件,<legend> 为组提供标题。这对可访问性至关重要——屏幕阅读器在组中每个控件前宣布图例,提供上下文。fieldset 上的 disabled 属性禁用其中的所有控件。Fieldset 还通过默认边框改善视觉组织。对于单选按钮,fieldset/legend 是标记组的推荐方式。避免过深地嵌套 fieldset,因为它可能混淆屏幕阅读器用户。

html
<form>
  <fieldset>
    <legend>Shipping Address</legend>
    <label>Street: <input type="text" name="street" required></label>
    <label>City: <input type="text" name="city" required></label>
    <label>ZIP: <input type="text" name="zip" pattern="[0-9]{5}"></label>
  </fieldset>

  <fieldset disabled>
    <legend>Billing (same as shipping)</legend>
    <label>Card: <input type="text" name="card"></label>
  </fieldset>

  <fieldset>
    <legend>Subscription Plan</legend>
    <label><input type="radio" name="plan" value="free"> Free</label>
    <label><input type="radio" name="plan" value="pro" checked> Pro</label>
  </fieldset>
</form>

Output 与 Progress 元素

<output> 显示计算结果——它与表单输入有实时关系(通过 for 属性)。对于计算值,它比 span 更有语义意义。<progress> 表示任务完成度(value/max);没有 value 时显示不确定的微调器。<meter> 表示已知范围内的标量值(如磁盘空间或测试分数)——low、high 和 optimum 属性定义影响颜色(绿/黄/红)的阈值。progress 和 meter 都有因浏览器而异的内置样式,但可用 CSS 自定义。

html
<form oninput="result.value = parseInt(a.value) + parseInt(b.value)">
  <input type="number" id="a" value="10"> +
  <input type="number" id="b" value="20"> =
  <output name="result" for="a b">30</output>
</form>

<!-- Progress bar -->
<label>Downloading: <progress id="prog" value="70" max="100">70%</progress></label>

<!-- Meter (gauge within a range) -->
<label>Disk usage: <meter value="0.6" min="0" max="1" low="0.5" high="0.8" optimum="0.2">60%</meter></label>
<label>Score: <meter value="85" min="0" max="100" low="40" high="70" optimum="90">85</meter></label>

表单自动补全

autocomplete 属性帮助浏览器使用存储的用户数据填写表单。使用标准化令牌:'name'、'email'、'tel'、'street-address'、'address-level2'(城市)、'postal-code'、'country'。对于信用卡:'cc-name'、'cc-number'、'cc-exp'。autocomplete='off' 禁用自动填充(尽管浏览器可能对非敏感字段忽略此设置)。autocomplete='one-time-code' 在移动设备上触发短信验证码自动填充。正确的 autocomplete 令牌极大地提高表单完成率和用户体验。它们还帮助密码管理器正确识别字段。

html
<form autocomplete="on">
  <!-- Browser can autofill name -->
  <label>Name: <input type="text" name="name" autocomplete="name"></label>

  <!-- Email autofill -->
  <label>Email: <input type="email" name="email" autocomplete="email"></label>

  <!-- Address autofill tokens -->
  <fieldset>
    <legend>Address</legend>
    <input autocomplete="street-address">
    <input autocomplete="address-level2"> <!-- City -->
    <input autocomplete="postal-code">
    <input autocomplete="country">
  </fieldset>

  <!-- Disable autocomplete for sensitive field -->
  <label>SSN: <input type="text" autocomplete="off"></label>

  <!-- One-time code (SMS) -->
  <label>Code: <input type="text" autocomplete="one-time-code"></label>
</form>

表单提交方法

GET 将表单数据附加到 URL(可见、可书签、长度有限)——用于搜索和过滤。POST 在请求体中发送数据(不可见、无长度限制)——用于创建/更新数据。文件上传需要 enctype='multipart/form-data'。按钮的 name/value 对包含在提交中——多个提交按钮可以通过相同 name 和不同值触发不同操作。对于 AJAX 提交,使用 FormData 对象和 fetch()。始终对敏感数据使用 POST,因为 GET 数据出现在浏览器历史和服务器日志中。

html
<!-- GET: data in URL query string -->
<form method="GET" action="/search">
  <input name="q" value="html5">
  <!-- URL: /search?q=html5 -->
</form>

<!-- POST: data in request body -->
<form method="POST" action="/submit" enctype="application/x-www-form-urlencoded">
  <input name="name" value="Alice">
</form>

<!-- File upload: multipart -->
<form method="POST" action="/upload" enctype="multipart/form-data">
  <input type="file" name="document">
</form>

<!-- Form with custom submit button -->
<form method="POST" action="/save">
  <button type="submit" name="action" value="save">Save</button>
  <button type="submit" name="action" value="publish">Publish</button>
</form>

Contenteditable 与 Spellcheck

contenteditable='true' 使任何元素可在浏览器中直接编辑——富文本编辑器的基础。spellcheck='true' 启用浏览器拼写检查(拼写错误的红色下划线)。对于代码片段,设置 spellcheck='false' 以避免误报。contenteditable 属性可以继承——子元素可编辑,除非设置为 'false'。保存可编辑内容需要 JavaScript(例如,在输入时存储到 localStorage)。用 contenteditable 构建完整富文本编辑器很复杂(处理粘贴、格式化、光标位置)——对于生产使用考虑 Quill、TipTap 或 ProseMirror 等库。

html
<!-- Editable div -->
<div contenteditable="true">
  Click to edit this text directly in the browser.
</div>

<!-- Editable with spellcheck -->
<p contenteditable="true" spellcheck="true">
  Typoos will be underlined in red.
</p>

<!-- Turn off spellcheck for code -->
<pre contenteditable="true" spellcheck="false">
  const varible = "code"; // no spellcheck
</pre>

<!-- Entire document editable -->
<body contenteditable="true">

<!-- Save editable content -->
<div id="note" contenteditable="true"
     oninput="localStorage.setItem('note', this.innerHTML)">
  <script>document.getElementById('note').innerHTML =
    localStorage.getItem('note') || '';</script>
</div>
20

SEO Meta 标签

Open Graph 标签

Open Graph 标签(由 Facebook 创建)控制页面在社交媒体上分享时的显示方式。og:image 应为 1200x630px。没有这些标签,平台从页面内容中发明预览,通常效果不佳。

html
<meta property="og:title" content="My Awesome Article">
<meta property="og:description" content="A deep dive into modern web design.">
<meta property="og:image" content="https://example.com/cover.jpg">
<meta property="og:url" content="https://example.com/article">
<meta property="og:type" content="article">
<meta property="og:site_name" content="Example Site">

Twitter Cards

Twitter Card 标签控制链接在 X/Twitter 上的渲染方式。summary_large_image 在标题上方显示突出图像;summary 显示较小的方形缩略图。twitter:site 标签将卡片链接到你的账户。

html
<meta name="twitter:card" content="summary_large_image">
<meta name="twitter:site" content="@yoursite">
<meta name="twitter:title" content="My Article">
<meta name="twitter:description" content="A deep dive into web design.">
<meta name="twitter:image" content="https://example.com/cover.jpg">

Canonical 与 Robots

rel="canonical" 通过指向主 URL 防止重复内容惩罚。robots meta 标签指导爬虫:noindex 从结果中移除页面,nofollow 阻止链接跟随。

html
<!-- Canonical: tells search engines the preferred URL -->
<link rel="canonical" href="https://example.com/article">

<!-- Robots: control indexing -->
<meta name="robots" content="index, follow">
<meta name="robots" content="noindex, nofollow">
<meta name="robots" content="noindex, follow">

结构化数据(JSON-LD)

JSON-LD 结构化数据帮助搜索引擎理解内容并启用富结果(星级评分、面包屑、事件信息)。Schema.org 定义词汇表。将 script 标签放在 head 或 body 内。

html
<script type="application/ld+json">
{
  "@context": "https://schema.org",
  "@type": "Article",
  "headline": "Modern Web Design",
  "author": { "@type": "Person", "name": "Jane Doe" },
  "datePublished": "2025-01-15",
  "image": "https://example.com/cover.jpg"
}
</script>

Meta Description 与 Viewport

description meta 标签影响点击率——保持在 160 个字符以内。viewport 标签对移动端响应式设计是强制性的。theme-color 样式化浏览器 UI 栏。charset 必须是 head 中的第一个元素。

html
<meta name="description" content="A concise 150-160 character summary that appears in search results.">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="theme-color" content="#4285f4">
<meta charset="UTF-8">
21

SVG 基础

基本形状

SVG 用 XML 元素绘图。形状包括 rect、circle、ellipse、line、polyline 和 polygon。与光栅图像不同,SVG 缩放无质量损失且可用 CSS 样式化。fill 设置内部颜色,stroke 设置轮廓。

html
<svg width="200" height="150" xmlns="http://www.w3.org/2000/svg">
  <rect x="10" y="10" width="80" height="50" fill="steelblue"/>
  <circle cx="140" cy="40" r="30" fill="tomato"/>
  <line x1="10" y1="100" x2="180" y2="100" stroke="black" stroke-width="2"/>
  <ellipse cx="100" cy="120" rx="60" ry="20" fill="none" stroke="green"/>
</svg>

路径

path 元素是最强大的 SVG 原语。命令:M(移动)、L(线)、H/V(水平/垂直)、C(三次贝塞尔)、Q(二次)、A(弧)、Z(闭合)。大写 = 绝对坐标,小写 = 相对。

html
<svg width="200" height="200">
  <!-- M=move, L=line, C=cubic bezier, Z=close -->
  <path d="M 10 10 L 100 10 L 100 100 Z" fill="none" stroke="black"/>
  <path d="M 10 150 C 50 50, 150 50, 190 150" stroke="red" fill="none"/>
  <path d="M 10 180 Q 100 120 190 180" stroke="blue" fill="none"/>
</svg>

分组与重用

用 g 分组元素以应用共享属性或一起变换。在 defs 内定义可重用形状并用 use href="#id" 引用它们。这保持 SVG DRY 且更小。

html
<svg width="200" height="200">
  <defs>
    <g id="star">
      <polygon points="50,5 61,39 98,39 68,61 79,95 50,75 21,95 32,61 2,39 39,39"/>
    </g>
  </defs>
  <use href="#star" x="0" y="0" fill="gold"/>
  <use href="#star" x="100" y="100" fill="orange"/>
</svg>

文本与样式

SVG 文本是真实文本——可选、可搜索且在任何比例下清晰。font-family、font-size、font-weight 镜像 CSS。text-anchor 控制水平对齐(start/middle/end)。

html
<svg width="300" height="100">
  <text x="10" y="50" font-family="Arial" font-size="24" fill="navy">
    Hello SVG
  </text>
  <text x="150" y="80" font-size="16" font-weight="bold" text-anchor="middle">
    Centered Bold Text
  </text>
</svg>

渐变与图案

渐变在 defs 中定义并通过 fill="url(#id)" 引用。linearGradient 沿线过渡;radialGradient 从中心点辐射。每个 stop 在百分比偏移处定义颜色。

html
<svg width="200" height="100">
  <defs>
    <linearGradient id="lg" x1="0%" y1="0%" x2="100%" y2="0%">
      <stop offset="0%" stop-color="red"/>
      <stop offset="100%" stop-color="blue"/>
    </linearGradient>
    <radialGradient id="rg">
      <stop offset="0%" stop-color="yellow"/>
      <stop offset="100%" stop-color="green"/>
    </radialGradient>
  </defs>
  <rect width="100" height="100" fill="url(#lg)"/>
  <rect x="100" width="100" height="100" fill="url(#rg)"/>
</svg>
22

Canvas

绘制形状

Canvas API 通过 2D 渲染上下文在 canvas 元素上绘制像素。fillRect/strokeRect 绘制矩形;路径(beginPath、arc、lineTo)在 fill() 或 stroke() 之前构建复杂形状。Canvas 是光栅——缩放会使绘图模糊。

html
const canvas = document.querySelector('canvas');
const ctx = canvas.getContext('2d');

ctx.fillStyle = 'tomato';
ctx.fillRect(10, 10, 100, 50);

ctx.strokeStyle = 'navy';
ctx.lineWidth = 3;
ctx.strokeRect(130, 10, 80, 80);

ctx.beginPath();
ctx.arc(200, 150, 40, 0, Math.PI * 2);
ctx.fill();

路径与线

路径逐点构建形状。moveTo 开始新子路径;lineTo 添加直线段;closePath 连接到起点。bezierCurveTo 和 quadraticCurveTo 用控制点绘制曲线。

html
ctx.beginPath();
ctx.moveTo(50, 50);
ctx.lineTo(150, 50);
ctx.lineTo(100, 150);
ctx.closePath();
ctx.fillStyle = 'gold';
ctx.fill();
ctx.stroke();

// Bezier curves
ctx.beginPath();
ctx.moveTo(0, 200);
ctx.bezierCurveTo(100, 100, 200, 300, 300, 200);
ctx.stroke();

绘制图像

drawImage 绘制图像、视频或另一个 canvas。9 参数形式裁剪源矩形并将其绘制到目标矩形中——适用于精灵表。绘制前等待 onload。

html
const img = new Image();
img.src = 'photo.jpg';
img.onload = () => {
  ctx.drawImage(img, 0, 0);                    // full image
  ctx.drawImage(img, 0, 0, 200, 150);          // scaled
  ctx.drawImage(img, 50, 50, 100, 100, 300, 0, 100, 100); // cropped
};

动画循环

requestAnimationFrame 在每次浏览器重绘时调度 draw() 一次(约 60fps),标签隐藏时暂停。clearRect 每帧清除 canvas 以防止轨迹。对于物理,将移动乘以增量时间以获得一致速度。

html
let x = 0;
function draw() {
  ctx.clearRect(0, 0, canvas.width, canvas.height);
  ctx.fillStyle = 'steelblue';
  ctx.fillRect(x, 50, 40, 40);
  x += 2;
  if (x > canvas.width) x = -40;
  requestAnimationFrame(draw);
}
requestAnimationFrame(draw);

像素操作

getImageData 返回原始 RGBA 像素值的 Uint8ClampedArray(每通道 0-255)。直接像素访问支持滤镜和效果。putImageData 将修改的缓冲区写回。这对大型 canvas 较慢。

html
const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
const data = imageData.data; // RGBA bytes

for (let i = 0; i < data.length; i += 4) {
  const avg = (data[i] + data[i + 1] + data[i + 2]) / 3;
  data[i] = data[i + 1] = data[i + 2] = avg; // grayscale
}

ctx.putImageData(imageData, 0, 0);
23

Iframe 与嵌入

Iframe 基础

iframe 在当前页面内嵌入另一个文档。title 属性对可访问性是必需的。loading="lazy" 延迟到接近视口时加载。始终设置 width 和 height 以防止布局偏移。

html
<iframe
  src="https://example.com/widget"
  width="600"
  height="400"
  title="Example Widget"
  loading="lazy"
  referrerpolicy="no-referrer">
</iframe>

Sandbox 属性

sandbox 属性限制 iframe 功能。空值阻止一切。添加令牌以重新启用功能:allow-scripts、allow-forms、allow-same-origin、allow-popups。永远不要对不受信任的内容将 allow-scripts 与 allow-same-origin 组合使用。

html
<!-- Locks down the iframe completely -->
<iframe src="untrusted.html" sandbox></iframe>

<!-- Selectively re-enable features -->
<iframe
  src="widget.html"
  sandbox="allow-scripts allow-same-origin allow-forms">
</iframe>

Embed 与 Object

embed 和 object 是遗留元素。object 支持资源失败时显示的回退内容。现代 HTML5 元素(video、audio、picture)更受青睐。对 HTML 内容使用 iframe,对视频使用 video。

html
<!-- Embed for plugins/media -->
<embed src="video.mp4" type="video/mp4" width="400" height="300">

<!-- Object with fallback content -->
<object data="report.pdf" type="application/pdf" width="100%" height="600">
  <p>Your browser cannot display PDFs. <a href="report.pdf">Download</a></p>
</object>

<!-- Video with multiple sources -->
<video controls>
  <source src="movie.webm" type="video/webm">
  <source src="movie.mp4" type="video/mp4">
  Your browser does not support video.
</video>

postMessage 通信

postMessage 是跨 iframe 边界(不同源)通信的唯一方式。始终在第三个参数中指定目标源。在接收端,信任消息前验证 e.origin。

html
<!-- Parent page -->
<iframe id="f" src="child.html"></iframe>
<script>
  const frame = document.getElementById('f');
  frame.contentWindow.postMessage({ type: 'greet', text: 'Hi' }, 'https://example.com');

  window.addEventListener('message', (e) => {
    if (e.origin !== 'https://example.com') return;
    console.log('From child:', e.data);
  });
</script>

响应式 Iframe

要使 iframe 响应式(例如,16:9 视频),将它们包裹在容器中,padding-bottom 等于宽高比百分比(16:9 为 56.25%)。将 iframe 绝对定位以填充容器。

html
<div style="position:relative; padding-bottom:56.25%; height:0; overflow:hidden;">
  <iframe
    src="https://youtube.com/embed/abc"
    style="position:absolute; top:0; left:0; width:100%; height:100%; border:0;"
    title="Video"
    allowfullscreen>
  </iframe>
</div>
24

性能优化

资源提示

资源提示告诉浏览器在需要之前准备连接或获取资产。preconnect 预热 DNS/TCP/TLS。preload 早期获取关键当前页面资产。prefetch 在空闲时间获取下一页资源。

html
<!-- Preconnect to a third-party origin -->
<link rel="preconnect" href="https://fonts.googleapis.com">

<!-- DNS prefetch for lower-priority origins -->
<link rel="dns-prefetch" href="https://analytics.example.com">

<!-- Preload critical assets -->
<link rel="preload" href="hero.webp" as="image">
<link rel="preload" href="/fonts/main.woff2" as="font" type="font/woff2" crossorigin>

<!-- Prefetch next page -->
<link rel="prefetch" href="/next-page.html">

延迟加载图像

loading="lazy" 延迟屏幕外图像加载,直到用户滚动到附近。始终设置 width 和 height 以防止布局偏移。picture 元素提供现代格式(WebP/AVIF),JPG 作为回退。

html
<!-- Native lazy loading -->
<img src="photo.jpg" loading="lazy" width="800" height="600" alt="...">

<!-- Picture with responsive sources -->
<picture>
  <source srcset="small.webp" media="(max-width: 600px)" type="image/webp">
  <source srcset="large.webp" type="image/webp">
  <img src="large.jpg" loading="lazy" width="1200" height="800" alt="...">
</picture>

脚本加载策略

没有属性时,脚本阻止 HTML 解析。async 并行下载但就绪时立即执行——不保证顺序。defer 并行下载并在 DOM 解析后按顺序执行——最适合主脚本。

html
<!-- Normal: blocks HTML parsing -->
<script src="app.js"></script>

<!-- Async: downloads in parallel, runs ASAP -->
<script src="analytics.js" async></script>

<!-- Defer: downloads in parallel, runs after HTML parse -->
<script src="app.js" defer></script>

<!-- Module scripts defer by default -->
<script type="module" src="app.mjs"></script>

关键 CSS

内联关键 CSS(首屏内容所需样式)消除渲染阻塞 CSS 请求,加速首次绘制。其余 CSS 通过 preload 技巧异步加载。Critical 等工具自动提取关键 CSS。

html
<!-- Inline above-the-fold CSS in head -->
<head>
  <style>
    body { margin: 0; font-family: sans-serif; }
    .hero { height: 100vh; background: #f0f0f0; }
  </style>
  <!-- Load the rest asynchronously -->
  <link rel="preload" href="full.css" as="style" onload="this.rel='stylesheet'">
  <noscript><link rel="stylesheet" href="full.css"></noscript>
</head>

测量性能

Performance API 测量真实用户计时。关键指标:FCP(首次内容绘制)、LCP(最大内容绘制)、CLS(累积布局偏移)、INP(下次绘制的交互)。使用 Lighthouse 获取实验室数据。

html
// PerformanceObserver for Core Web Vitals
new PerformanceObserver((list) => {
  for (const entry of list.getEntries()) {
    console.log(entry.name, entry.startTime);
  }
}).observe({ entryTypes: ['paint', 'largest-contentful-paint'] });

// Navigation timing
const [nav] = performance.getEntriesByType('navigation');
console.log('DOM Content Loaded:', nav.domContentLoadedEventEnd);
console.log('Load:', nav.loadEventEnd);
25

高级表单

输入类型

HTML5 输入类型提供内置验证和 UI。email/url 验证格式。number/date 提供选择器。color/range 有专用 UI。accept 过滤文件类型。浏览器自动处理验证。

html
<input type="email" required>
<input type="url" placeholder="https://">
<input type="number" min="0" max="100" step="5">
<input type="date" min="2024-01-01">
<input type="color" value="#ff0000">
<input type="range" min="0" max="10">
<input type="file" accept="image/*">

表单验证

required 防止空值提交。minlength/maxlength 限制长度。pattern 使用正则表达式验证。浏览器阻止提交并显示错误。用 setCustomValidity 和 invalid 事件自定义。

html
<form>
  <input type="text" required minlength="3" maxlength="20" pattern="[A-Za-z]+">
  <input type="email" required>
  <input type="submit" value="Submit">
</form>

Fieldset 与 Legend

fieldset 分组相关表单字段。legend 提供标题。fieldset 上的 disabled 禁用所有字段。通过分组相关输入改善可访问性。屏幕阅读器为每个字段宣布图例。

html
<fieldset>
  <legend>Shipping Address</legend>
  <label>Street: <input type="text" name="street"></label>
  <label>City: <input type="text" name="city"></label>
</fieldset>
<fieldset disabled>
  <legend>Billing (disabled)</legend>
  <input type="text" name="billing">
</fieldset>

Datalist

datalist 为输入提供自动补全建议。用户可选择或自由输入。与 select 不同,它允许自定义值。list 属性通过 id 将输入链接到 datalist。适用于搜索和标签。

html
<label>Choose browser:
  <input list="browsers" name="browser">
</label>
<datalist id="browsers">
  <option value="Chrome">
  <option value="Firefox">
  <option value="Safari">
</datalist>

Output 元素

output 显示计算结果。for 属性链接到输入 ID。随表单变更实时更新。简单计算无需 JavaScript。作为实时区域对屏幕阅读器可访问。

html
<form oninput="result.value = parseInt(a.value) + parseInt(b.value)">
  <input type="number" id="a" value="0"> +
  <input type="number" id="b" value="0"> =
  <output name="result" for="a b">0</output>
</form>
26

可访问性(a11y)

ARIA 标签

aria-label 为没有可见文本的元素提供可访问名称。aria-expanded 指示切换状态。aria-controls 链接到受控元素。仅当 HTML 语义不足时使用 ARIA。

html
<button aria-label="Close menu" onclick="closeMenu()">
  <svg>...</svg>
</button>
<nav aria-label="Main navigation">...</nav>
<button aria-expanded="false" aria-controls="menu">Menu</button>

地标角色

地标角色帮助屏幕阅读器用户导航。大多数语义元素有隐式角色。仅在需要时添加显式角色。表单上的 role="search" 创建搜索地标。避免与语义元素的角色冗余。

html
<header role="banner">...</header>
<nav role="navigation">...</nav>
<main role="main">...</main>
<aside role="complementary">...</aside>
<footer role="contentinfo">...</footer>
<form role="search">...</form>

焦点管理

为键盘用户管理焦点。打开模态时,将焦点移到内部。关闭时,将焦点返回到触发器。使用 tabindex="-1" 使元素可编程聚焦。永远不要在没有替代的情况下移除焦点轮廓。

html
<button id="open">Open</button>
<div id="modal" hidden>
  <button id="close">Close</button>
</div>
<script>
document.getElementById('open').onclick = () => {
  modal.hidden = false;
  document.getElementById('close').focus();
};
</script>

跳过链接

跳过链接让键盘用户绕过重复导航。在聚焦前视觉隐藏。href 指向主内容 ID。对可访问性合规(WCAG)至关重要。提高导航效率。

html
<body>
  <a href="#main" class="skip-link">Skip to main content</a>
  <nav>...long navigation...</nav>
  <main id="main">...</main>
</body>
<style>
.skip-link { position: absolute; left: -9999px; }
.skip-link:focus { left: 0; }
</style>

Alt 文本

Alt 文本为屏幕阅读器描述图像。空 alt="" 标记装饰性图像(被忽略)。描述内容和用途,而非外观。对于复杂图像,在其他地方提供更长描述。永远不要将 alt 用于工具提示(使用 title)。

html
<!-- Informative image -->
<img src="chart.png" alt="Bar chart showing 30% increase in sales">
<!-- Decorative image -->
<img src="spacer.gif" alt="">
<!-- Complex image -->
<img src="diagram.png" alt="Network topology" longdesc="diagram-desc.html">
27

常见陷阱

缺少 Alt 文本

缺少 alt 文本破坏可访问性。屏幕阅读器读取文件名。"image" 或 "photo" 毫无帮助。描述内容和用途。空 alt="" 标记装饰性图像。永远不要完全跳过 alt 属性。

html
<!-- BAD: no alt -->
<img src="photo.jpg">
<!-- BAD: unhelpful alt -->
<img src="photo.jpg" alt="image">
<!-- GOOD: descriptive alt -->
<img src="photo.jpg" alt="Team meeting in conference room">
<!-- Decorative: empty alt -->
<img src="border.png" alt="">

Div 汤

对所有内容使用 div 移除语义。屏幕阅读器无法导航。SEO 无法理解内容结构。使用语义元素:header、nav、main、article、section、aside、footer。将 div 保留用于无语义意图的分组。

html
<!-- BAD: div for everything -->
<div class="header">...</div>
<div class="nav">...</div>
<div class="article">...</div>
<!-- GOOD: semantic elements -->
<header>...</header>
<nav>...</nav>
<article>...</article>

内联样式

内联样式混合内容和表现,使维护困难。它们具有高特异性,覆盖样式表。无法缓存或重用。使用类和外部样式表。将内联样式保留用于动态值。

html
<!-- BAD: inline styles -->
<div style="color: red; font-size: 16px;">Text</div>
<!-- GOOD: external CSS -->
<div class="error">Text</div>
<link rel="stylesheet" href="styles.css">

按钮与链接

按钮触发操作(保存、删除、切换)。链接导航到 URL。使用链接进行操作会破坏键盘导航(Space 与 Enter)和语义。屏幕阅读器以不同方式宣布它们。使用 type="button" 防止表单提交。

html
<!-- BAD: link for actions -->
<a href="#" onclick="save()">Save</a>
<!-- GOOD: button for actions -->
<button type="button" onclick="save()">Save</button>
<!-- Link for navigation -->
<a href="/about">About</a>

标题层次结构

标题创建文档大纲。不要跳过级别(h1 到 h3)。每页使用一个 h1(主标题)。屏幕阅读器用户按标题导航。保持逻辑层次结构。使用 CSS 进行视觉样式化,而非标题级别。

html
<!-- BAD: skip levels -->
<h1>Title</h1>
<h3>Subtitle</h3>  <!-- Skipped h2 -->
<!-- BAD: multiple h1 -->
<h1>Title</h1>
<h1>Another</h1>
<!-- GOOD: hierarchical -->
<h1>Main Title</h1>
<h2>Section</h2>
<h3>Subsection</h3>

这篇内容对您有帮助吗?

学习路径

从零开始学习

通过结构化课程从头学习这个语言。