Skip to content

HTML5 速查表

HTML 的第五次修订,新增语义元素和 API。

01

语义元素

文档结构

HTML5 引入了语义元素,向浏览器和开发者描述其含义。header、nav、main、article、aside、footer 定义文档结构。DOCTYPE html 声明触发标准模式,lang 属性提升可访问性和 SEO。

html5
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Page Title</title>
</head>
<body>
  <header>Header content</header>
  <nav>Navigation</nav>
  <main>
    <article>Article content</article>
    <aside>Sidebar</aside>
  </main>
  <footer>Footer</footer>
</body>
</html>

Header 与 Footer

header 表示介绍性内容(徽标、标题、搜索表单),可以出现多次。footer 存放结尾内容,如版权、链接或联系信息。两者都可用于 article 或 section 等分节元素内部,不限于页面级别。

html5
<header>
  <h1>Site Title</h1>
  <p>Tagline</p>
</header>

<footer>
  <nav>
    <a href="/about">About</a>
    <a href="/contact">Contact</a>
  </nav>
  <small>&copy; 2024 Example</small>
</footer>

导航元素

nav 专用于主要导航块。并非每个链接列表都需要 nav——只有对站点导航有意义的链接组才使用。用 aria-label 区分多个 nav 元素(如主导航与面包屑)。aria-current 表示当前页面。

html5
<nav aria-label="Main navigation">
  <ul>
    <li><a href="/" aria-current="page">Home</a></li>
    <li><a href="/blog">Blog</a></li>
    <li><a href="/about">About</a></li>
  </ul>
</nav>

<nav aria-label="Breadcrumb">
  <ol>
    <li><a href="/">Home</a></li>
    <li><a href="/blog">Blog</a></li>
    <li aria-current="page">Post</li>
  </ol>
</nav>

Main 与 Article

main 包裹页面的主要内容,每页只应出现一次(映射到 main ARIA 地标)。article 是独立的组合——博客文章、新闻故事或组件——可以独立分发或复用。section 将主题相关的内容分组,始终应包含标题。

html5
<body>
  <header>Site header</header>
  <main>
    <article>
      <h1>Blog Post Title</h1>
      <p>Body content...</p>
      <section>
        <h2>Subsection</h2>
        <p>More content</p>
      </section>
    </article>
  </main>
</body>

Section 与 Aside

section 将相关内容分组,应始终包含标题。aside 表示与主内容间接相关的内容——侧边栏、引文或广告。两者都改善文档大纲。用 aria-labelledby 为 section 提供无障碍名称。

html5
<section aria-labelledby="features-heading">
  <h2 id="features-heading">Features</h2>
  <p>Feature description</p>
</section>

<aside aria-label="Related links">
  <h2>Related</h2>
  <ul>
    <li><a href="/post-2">Next post</a></li>
  </ul>
</aside>

Figure 与 Figcaption

figure 将从主流中引用的内容(图像、代码清单、引语)分组,figcaption 提供标题。figcaption 必须是第一个或最后一个子元素。figure 不限于图像——也适用于代码片段或引语等任何自包含内容。

html5
<figure>
  <img src="chart.png" alt="Sales chart showing growth">
  <figcaption>Figure 1: Quarterly sales growth in 2024.</figcaption>
</figure>

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

表单增强

新输入类型

HTML5 新增了 email、url、tel、date、time、color、range、number、month、week 和 datetime-local 等输入类型。浏览器提供原生验证和专用 UI(日期选择器、颜色选择器)。type=email/url 在表单提交时触发自动格式验证。

html5
<form>
  <label>Email: <input type="email" required></label>
  <label>URL: <input type="url"></label>
  <label>Phone: <input type="tel"></label>
  <label>Date: <input type="date"></label>
  <label>Time: <input type="time"></label>
  <label>Color: <input type="color"></label>
  <label>Range: <input type="range" min="0" max="100"></label>
  <label>Number: <input type="number" min="0" step="0.01"></label>
  <button>Submit</button>
</form>

Required 与 Pattern 验证

HTML5 通过属性提供内置验证:required、minlength、maxlength、min、max、step 和 pattern(正则表达式)。title 属性提供验证失败时显示的提示。在表单上使用 novalidate 属性可禁用浏览器验证。

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

Datalist 自动补全

datalist 通过 list 属性为输入提供自动补全建议。与 select 不同,用户可以输入自由文本或选择建议。每个 option 的 value 成为一个建议。在不支持的浏览器上 datalist 优雅降级为普通文本输入。

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

Output 元素

output 表示计算或用户操作的结果。它有可通过 JS 访问的 value 属性,以及引用相关输入的 for 属性。它是表单关联元素,其值会随表单提交。适合用于实时计算器。

html5
<label>Uploading:
  <progress value="70" max="100">70%</progress>
</label>

<label>Disk usage:
  <meter value="0.6" min="0" max="1" low="0.4" high="0.8" optimum="0.2">
    60%
  </meter>
</label>

<!-- Indeterminate progress -->
<progress>Processing...</progress>

Progress 与 Meter

progress 显示任务完成度(value/max);省略 value 为不确定状态。meter 显示已知范围内的标量测量,low/high/optimum 阈值影响其颜色。meter 是只读的,不用于进度指示。

html5
<form id="myForm" action="/submit" method="post"></form>

<!-- Input outside the form, but associated via form attribute -->
<label>
  Name:
  <input type="text" name="name" form="myForm" required>
</label>

<fieldset form="myForm" disabled>
  <legend>Shipping Options</legend>
  <label><input type="radio" name="ship" value="standard"> Standard</label>
  <label><input type="radio" name="ship" value="express"> Express</label>
</fieldset>

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

Form 属性与 Fieldset

form 属性通过 id 将 input、fieldset 或 button 与表单关联,即使元素未嵌套在其中。fieldset 将相关控件分组,legend 提供标题。fieldset 上的 disabled 属性可一次禁用所有包含的控件。

html5
<form>
  <label>
    Search:
    <input type="search" name="q"
           placeholder="Type to search..."
           autofocus
           autocomplete="off">
  </label>
  <label>
    Hint text:
    <input type="text" name="hint" title="Helpful tooltip">
  </label>
  <button type="submit">Go</button>
</form>
03

多媒体

Audio 元素

audio 无需插件即可嵌入声音。多个 source 元素让浏览器选择第一个支持的格式。controls 添加播放控件;autoplay 在大多数浏览器中需要 muted;loop 重复播放。回退文本仅在不受支持的浏览器上显示。

html5
<form>
  <label>
    Email (required):
    <input type="email" name="email" required>
  </label>
  <label>
    Optional phone:
    <input type="tel" name="phone">
  </label>
  <button>Submit</button>
</form>

Video 元素

video 嵌入视频,属性包括 poster(预览图)、preload(none/metadata/auto)、width/height(避免布局偏移)、controls、autoplay、loop 和 muted。提供多种 source 格式以实现跨浏览器支持。内部的文本回退仅在 video 不受支持时显示。

html5
<form>
  <label>
    Product code:
    <input type="text" name="code"
           pattern="[A-Z]{3}-\d{4}"
           title="Three letters, hyphen, four digits (e.g., ABC-1234)"
           required>
  </label>
  <label>
    Hex color:
    <input type="text" name="color" pattern="^#[0-9A-Fa-f]{6}$">
  </label>
  <button>Submit</button>
</form>

Source 与 Track

source 指定媒体 URL;浏览器选择第一个可播放的类型。type 属性可包含编解码器提示以加快选择。track 添加文本轨道:subtitles(翻译)、captions(听障)、descriptions(音频叙述)、chapters 或 metadata。default 属性选择一个轨道。

html5
<form>
  <label>
    Quantity:
    <input type="number" name="qty" min="1" max="99" step="1" value="1">
  </label>
  <label>
    Price:
    <input type="number" name="price" min="0" step="0.01">
  </label>
  <label>
    Date range:
    <input type="date" name="start" min="2024-01-01" max="2024-12-31">
  </label>
  <button>Submit</button>
</form>

媒体 API 控制

HTMLMediaElement API 暴露 play()、pause()、load(),以及 currentTime、duration、volume、playbackRate、muted 和 loop 等属性。事件包括 play、pause、ended、timeupdate、loadedmetadata 和 volumechange。play() 返回的 Promise 在自动播放被阻止时会 reject。

html5
<form>
  <label>
    Username (3-20 chars):
    <input type="text" name="user" minlength="3" maxlength="20" required>
  </label>
  <label>
    Bio (max 280 chars):
    <textarea name="bio" maxlength="280"></textarea>
  </label>
  <button>Submit</button>
</form>

Embed 与 Iframe

iframe 嵌入另一个文档。sandbox 限制能力(无表单、脚本、弹窗),除非通过令牌显式允许。allow 授予功能权限(摄像头、地理定位)。referrerpolicy 控制 Referer 头。embed 和 object 是 PDF/插件的传统方式;应谨慎使用。

html5
<form id="f">
  <label>
    Username:
    <input name="u" required minlength="3">
  </label>
  <button>Submit</button>
</form>

<script>
  const input = document.querySelector('#f input[name=u]');
  input.addEventListener('input', () => {
    if (input.validity.valueMissing) {
      input.setCustomValidity('Please pick a username.');
    } else if (input.validity.tooShort) {
      input.setCustomValidity('At least 3 characters.');
    } else {
      input.setCustomValidity('');
    }
  });
</script>

媒体事件

关键媒体事件:loadstart、loadedmetadata、loadeddata、canplay、canplaythrough、playing、waiting、timeupdate、ended、volumechange 和 error。error 属性(mediaError)有错误码:1=中止、2=网络、3=解码、4=不支持。用这些事件构建自定义播放器和加载指示器。

html5
<!-- Browser validation disabled for whole form -->
<form novalidate>
  <label>Email: <input type="email" name="e" required></label>
  <button>Save draft</button>
  <!-- Skip validation only for this button -->
  <button type="submit" formnovalidate>Cancel</button>
</form>
04

Canvas API

Canvas 设置

canvas 是位图绘图表面。width/height 属性设置绘图缓冲区大小(像素);CSS 设置显示大小。要在 Retina 显示器上清晰渲染,需将缓冲区乘以 devicePixelRatio 并缩放上下文。getContext('2d') 返回 2D 渲染上下文。

html5
<canvas id="cv" width="400" height="300">
  Your browser does not support canvas.
</canvas>

<script>
  const canvas = document.getElementById('cv');
  const ctx = canvas.getContext('2d');
  // High-DPI scaling
  const dpr = window.devicePixelRatio || 1;
  canvas.width = 400 * dpr;
  canvas.height = 300 * dpr;
  ctx.scale(dpr, dpr);
</script>

绘制矩形

2D 上下文提供 fillRect(x, y, w, h)、strokeRect 和 clearRect。fillStyle 和 strokeStyle 接受颜色、渐变或图案。clearRect 使像素透明。所有矩形方法都会立即绘制。坐标从左上角开始测量。

html5
<script>
  const ctx = canvas.getContext('2d');
  // Filled rectangle
  ctx.fillStyle = '#3498db';
  ctx.fillRect(10, 10, 100, 50);
  // Outlined rectangle
  ctx.strokeStyle = '#e74c3c';
  ctx.lineWidth = 3;
  ctx.strokeRect(130, 10, 100, 50);
  // Clear a rectangle (erase)
  ctx.clearRect(50, 20, 40, 30);
</script>

绘制路径

路径通过 moveTo、lineTo、arc、arcTo、quadraticCurveTo 和 bezierCurveTo 构建形状。beginPath 开始新的子路径;closePath 连接到起点。arc(x, y, radius, startAngle, endAngle) 绘制弧/圆(角度为弧度)。调用 fill() 或 stroke() 渲染路径。

html5
<script>
  const ctx = canvas.getContext('2d');
  ctx.beginPath();
  ctx.moveTo(20, 20);          // starting point
  ctx.lineTo(180, 20);         // top edge
  ctx.lineTo(100, 140);        // bottom corner
  ctx.closePath();             // back to start
  ctx.fillStyle = 'rgba(46,204,113,0.6)';
  ctx.fill();
  ctx.stroke();
</script>

绘制文本

fillText 和 strokeText 使用当前 font 属性(CSS font 简写)渲染文本。textAlign 和 textBaseline 控制对齐方式。measureText 返回一个 TextMetrics 对象,其 width 属性给出渲染宽度——用于布局和文本命中测试。

html5
<script>
  const ctx = canvas.getContext('2d');
  ctx.font = '600 24px system-ui, sans-serif';
  ctx.textAlign = 'center';
  ctx.textBaseline = 'middle';
  ctx.fillStyle = '#2c3e50';
  ctx.fillText('Hello Canvas', 200, 150);
  ctx.lineWidth = 1;
  ctx.strokeStyle = '#bdc3c7';
  ctx.strokeText('Outlined', 200, 200);
</script>

渐变

createLinearGradient(x0, y0, x1, y1) 和 createRadialGradient(x0, y0, r0, x1, y1, r1) 返回渐变对象。addColorStop(偏移 0-1, 颜色) 定义颜色过渡。将渐变赋给 fillStyle 或 strokeStyle。始终至少定义两个色标才能看到渐变。

html5
<script>
  const img = new Image();
  img.src = 'pic.jpg';
  img.onload = () => {
    ctx.drawImage(img, 0, 0);                      // full size
    ctx.drawImage(img, 0, 0, 100, 75);             // scaled
    // Source crop -> destination
    ctx.drawImage(img, 32, 32, 64, 64, 200, 0, 64, 64);
  };
</script>

图像与变换

drawImage 将图像(或另一个 canvas/video)渲染到画布上。9 参数形式切片源图像的子矩形。save() 和 restore() 压入/弹出整个状态(变换、样式、裁剪)。translate、rotate 和 scale 组成变换矩阵。

html5
<script>
  // Linear gradient
  const lg = ctx.createLinearGradient(0, 0, 200, 0);
  lg.addColorStop(0, '#1e90ff');
  lg.addColorStop(1, '#ffffff');
  ctx.fillStyle = lg;
  ctx.fillRect(0, 0, 200, 80);

  // Radial gradient
  const rg = ctx.createRadialGradient(100, 180, 5, 100, 180, 80);
  rg.addColorStop(0, 'rgba(255,200,0,1)');
  rg.addColorStop(1, 'rgba(255,200,0,0)');
  ctx.fillStyle = rg;
  ctx.fillRect(0, 100, 200, 160);
</script>
05

内联 SVG

内联 SVG

内联 SVG 直接嵌入 HTML,因此它是 DOM 的一部分,可用 CSS 设置样式。viewBox 属性定义坐标系;width/height 设置渲染大小。xmlns 在 HTML5 内部是可选的,但在独立 .svg 文件中是必需的。形状缩放不失真。

html5
<svg width="200" height="120" viewBox="0 0 200 120"
     xmlns="http://www.w3.org/2000/svg">
  <rect x="10" y="10" width="180" height="100"
        fill="#3498db" rx="8" />
  <text x="100" y="65" text-anchor="middle"
        fill="white" font-size="18">SVG</text>
</svg>

SVG 形状

SVG 基本图元:rect、circle、ellipse、line、polygon(闭合)和 polyline(开放)。常用属性:fill、stroke、stroke-width、opacity、rx/ry(圆角)。所有形状都可用 CSS 定位和动画。坐标使用 viewBox 定义的 SVG 用户空间。

html5
<svg width="240" height="120" viewBox="0 0 240 120">
  <rect x="10" y="10" width="80" height="80" fill="#e74c3c" />
  <circle cx="150" cy="50" r="40" fill="#2ecc71" />
  <ellipse cx="200" cy="60" rx="30" ry="18" fill="#f1c40f" />
  <line x1="10" y1="110" x2="230" y2="110" stroke="#333" stroke-width="2" />
  <polyline points="10,100 60,70 110,100 160,70"
            fill="none" stroke="#9b59b6" stroke-width="3" />
</svg>

SVG 文本与路径

text 用 font-family/size 属性渲染文本;tspan 设置子范围样式。path 元素通过 d 属性绘制任何形状:M(移至)、L(线至)、C/S(三次贝塞尔)、Q/T(二次)、A(弧)、Z(闭合)。路径是最强大的 SVG 图元——图标和复杂艺术都由路径构建。

html5
<svg width="200" height="120" viewBox="0 0 200 120">
  <!-- M=moveTo, L=lineTo, C=cubicBezier, Z=closePath -->
  <path d="M 10 80
           C 40 10, 90 10, 120 80
           L 180 80 Z"
        fill="none" stroke="#3498db" stroke-width="3" />
  <!-- A=arc -->
  <path d="M 10 100 A 90 90 0 0 1 190 100"
        fill="none" stroke="#e74c3c" stroke-width="2" />
</svg>

SVG 渐变与图案

defs 存放可复用的定义。linearGradient 和 radialGradient 使用 stop 元素(偏移 0-1、stop-color、stop-opacity)。用 fill='url(#id)' 引用它们。渐变默认使用 objectBoundingBox(0-1)。在 pattern 中定义的图案可以平铺填充以创建纹理。

html5
<svg width="300" height="100" viewBox="0 0 300 100">
  <text x="20" y="40" font-family="system-ui" font-size="20"
        fill="#2c3e50">Plain text</text>
  <text x="20" y="75" font-size="20" fill="#e74c3c"
        text-decoration="underline">Underlined</text>
  <text x="180" y="50">
    <tspan fill="#3498db">Blue</tspan>
    <tspan fill="#27ae60">Green</tspan>
  </text>
</svg>

SVG 变换

transform 属性应用 translate、rotate、scale、skewX、skewY 和 matrix 操作——从右到左应用。g 元素将形状分组并将变换应用于所有子元素。内联 SVG 也支持带 transform-origin 的 CSS transform,对动画很有用。

html5
<svg width="220" height="120" viewBox="0 0 220 120">
  <defs>
    <linearGradient id="g1" x1="0" y1="0" x2="1" y2="0">
      <stop offset="0%" stop-color="#1e90ff" />
      <stop offset="100%" stop-color="#ffffff" />
    </linearGradient>
    <radialGradient id="g2">
      <stop offset="0%" stop-color="#f1c40f" />
      <stop offset="100%" stop-color="#e67e22" />
    </radialGradient>
  </defs>
  <rect x="10" y="10" width="90" height="90" fill="url(#g1)" />
  <circle cx="160" cy="55" r="45" fill="url(#g2)" />
</svg>

SVG 动画

通过 animate、animateTransform 和 animateMotion 的 SMIL 动画以声明方式为属性添加动画。attributeName 指定属性;values 为关键帧;dur/repeatCount 控制时序。对于现代项目,CSS 动画或 Web Animations API 性能更好且支持更广泛。

html5
<!-- SVG: vector, DOM-accessible, scalable -->
<svg width="100" height="100" viewBox="0 0 100 100">
  <circle id="c" cx="50" cy="50" r="40" fill="#3498db" />
</svg>
<button onclick="document.getElementById('c')
  .setAttribute('r','20')">Shrink</button>

<!-- Canvas: bitmap, pixel-based, faster for many objects -->
<canvas id="cv" width="100" height="100"></canvas>
06

地理定位

获取当前位置

getCurrentPosition(success, error, options) 是 Geolocation API 的核心。成功回调接收一个 Position 对象,包含 coords(纬度、经度、精度,以及可选的高度、航向、速度)。首次调用始终会提示用户授权。务必处理错误回调。

html5
<video controls width="640" poster="cover.jpg">
  <source src="movie.mp4" type="video/mp4">
  <source src="movie.webm" type="video/webm">
  Your browser does not support the video tag.
</video>

监视位置

watchPosition 注册一个回调,在设备位置变化时重复触发——非常适合导航应用。它返回一个 id,传给 clearWatch() 可停止更新。每次更新包含时间戳;用 maximumAge 控制缓存,用 timeout 避免挂起。

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

<!-- Simple single-source -->
<audio src="beep.mp3" autoplay></audio>

位置选项

options 对象调整行为:enableHighAccuracy 请求最佳结果(GPS),代价是功耗/时间。timeout 限制 API 等待的最长时间(超时报错码 3)。maximumAge 允许返回早于指定毫秒数的缓存位置——设为 Infinity 则始终使用缓存。

html5
<video controls>
  <source src="clip.webm" type='video/webm; codecs="vp9, opus"'>
  <source src="clip.mp4"  type='video/mp4; codecs="hvc1"'>
  <source src="clip.ogv"  type="video/ogg">
  <p>Download <a href="clip.mp4">clip.mp4</a></p>
</video>

错误处理

PositionError 对象暴露 code 和 message。码 1(PERMISSION_DENIED)在用户拒绝或页面通过 http(不安全上下文)提供服务时触发。码 2(POSITION_UNAVAILABLE)表示设备无法确定位置。码 3(TIMEOUT)表示在超时时间内未获取定位。务必提供优雅的回退方案。

html5
<video controls>
  <source src="talk.mp4" type="video/mp4">
  <track kind="subtitles" src="talk.en.vtt"
         srclang="en" label="English" default>
  <track kind="captions" src="talk.en.cc.vtt"
         srclang="en" label="English CC">
  <track kind="chapters" src="talk.chapters.vtt"
         srclang="en" label="Chapters">
</video>

坐标与距离

coords 提供纬度、经度、精度(米),以及可选的高度(米)、altitudeAccuracy、航向(度)和速度(米/秒)。计算两点间距离可使用 Haversine 公式。accuracy 值告诉你读数的 95% 置信半径。

html5
<video src="bg.mp4"
       autoplay muted loop
       playsinline
       preload="auto"
       width="1280" height="720">
</video>

<video src="preview.mp4"
       controls
       preload="metadata"
       poster="preview.jpg">
</video>

Media API

The HTMLMediaElement API exposes play(), pause(), load(), currentTime, duration, volume, muted, playbackRate, and readyState. Events include play, pause, timeupdate, ended, volumechange, loadedmetadata, and waiting. Build custom players by hiding controls and wiring buttons to these methods.

html5
<video id="v" src="movie.mp4"></video>
<button onclick="v.play()">Play</button>
<button onclick="v.pause()">Pause</button>
<input type="range" min="0" max="100" oninput="v.volume = this.value/100">

<script>
  const v = document.getElementById('v');
  v.addEventListener('timeupdate', () => {
    console.log(v.currentTime, '/', v.duration);
  });
  v.addEventListener('ended', () => alert('Done!'));
  // Jump 10s forward
  function skip() { v.currentTime += 10; }
</script>
07

Web 存储

localStorage 基础

localStorage 持久化存储数据,无过期时间,按源(协议+主机+端口)隔离。值以字符串存储——对象必须序列化。同步 API 在处理大量数据时可能阻塞主线程。每个源的容量通常为 5-10 MB。切勿在此存储敏感数据;它可被源上的任何脚本访问。

html5
<script>
  if ('geolocation' in navigator) {
    navigator.geolocation.getCurrentPosition(
      (pos) => {
        console.log('Latitude:', pos.coords.latitude);
        console.log('Longitude:', pos.coords.longitude);
        console.log('Accuracy:', pos.coords.accuracy, 'meters');
      },
      (err) => console.error(err.message),
      { enableHighAccuracy: true, timeout: 10000 }
    );
  }
</script>

sessionStorage 基础

sessionStorage 与 localStorage 接口相同,但在标签页关闭时清除。关闭标签页会清除数据。在新标签页中打开页面会启动新的会话。适用于不应在浏览器重启后存活的临时状态,如表单草稿或向导进度。

html5
<script>
  navigator.geolocation.getCurrentPosition(success, error, {
    enableHighAccuracy: true,  // use GPS, slower
    timeout: 5000,             // max wait in ms
    maximumAge: 60000          // accept cached fix up to 60s old
  });

  function success(pos) { /* ... */ }
  function error(err) { /* ... */ }
</script>

JSON 存储

由于 Web Storage 只存储字符串,需用 JSON.stringify 序列化对象,用 JSON.parse 解析。务必用 try/catch 包裹 parse,因为存储的值可能已损坏。当键缺失时(null)提供合理的默认值。对于更大或结构化数据,考虑使用 IndexedDB。

html5
<script>
  const watchId = navigator.geolocation.watchPosition(
    (pos) => updateMap(pos.coords),
    (err) => console.warn(err.message),
    { enableHighAccuracy: true }
  );

  // Stop tracking
  document.getElementById('stop').onclick = () => {
    navigator.geolocation.clearWatch(watchId);
  };
</script>

存储事件

storage 事件在 localStorage 或 sessionStorage 变化时,在同一源的其他文档中触发——但关键是不在做出更改的标签页中触发。它实现了跨标签页同步(例如,一个标签页登出则所有标签页登出)。事件包含 key、oldValue、newValue、url 和 storageArea。

html5
<script>
  navigator.geolocation.getCurrentPosition(
    (pos) => console.log(pos),
    (err) => {
      switch (err.code) {
        case err.PERMISSION_DENIED:  // 1
          console.log('User denied permission'); break;
        case err.POSITION_UNAVAILABLE: // 2
          console.log('Position unavailable'); break;
        case err.TIMEOUT:              // 3
          console.log('Timed out'); break;
      }
      console.log(err.message);
    }
  );
</script>

清除存储

removeItem 删除单个键;clear() 清空该源的所有存储。两者都会在其他标签页触发 storage 事件。估算用量需要遍历键(UTF-16 中每个字符 2 字节)。超出配额会抛出 QuotaExceededError;写入大值时应捕获它。

html5
<script>
  navigator.permissions
    .query({ name: 'geolocation' })
    .then((result) => {
      // result.state: 'granted' | 'denied' | 'prompt'
      if (result.state === 'granted') {
        startTracking();
      } else if (result.state === 'prompt') {
        showEnableButton();
      } else {
        showInstructions();
      }
      result.onchange = () => console.log(result.state);
    });
</script>
08

Web Workers

Worker 设置

Web Worker 在后台线程上运行 JavaScript,与 UI 线程分离。用 new Worker(url) 创建。通过 postMessage/onmessage 通信——数据被结构化克隆(或对 ArrayBuffer 使用转移)。Worker 不能访问 DOM 和 window,但可以使用 fetch、IndexedDB 和定时器。

html5
<script>
  // Store a value (strings only)
  localStorage.setItem('theme', 'dark');
  // Read a value
  const theme = localStorage.getItem('theme'); // 'dark'
  // Remove one key
  localStorage.removeItem('theme');
  // Remove everything
  localStorage.clear();

  // Number of stored keys
  console.log(localStorage.length);
</script>

Worker 脚本

在 Worker 内部,self 指向 Worker 的全局作用域。onmessage 接收来自主线程的数据;postMessage 发回数据。importScripts() 同步加载额外脚本。Worker 有自己的事件循环,不能触碰 document 或 window,因此无法直接操作 DOM。

html5
<script>
  // sessionStorage works like localStorage but per-tab
  sessionStorage.setItem('cart', '3 items');
  console.log(sessionStorage.getItem('cart'));
  sessionStorage.removeItem('cart');

  // Survives a page reload but not a tab close
  // Also isolated per tab (not shared)
</script>

Worker 错误

Worker 中未捕获的错误会在父级触发 onerror 事件,包含 message、filename、lineno 和 colno。调用 preventDefault() 可停止默认错误报告。onmessageerror 在消息无法序列化/反序列化时触发(例如,函数等不可克隆的数据)。

html5
<script>
  const settings = { theme: 'dark', fontSize: 16, lang: 'en' };

  // Serialize before saving
  localStorage.setItem('settings', JSON.stringify(settings));

  // Parse when reading
  try {
    const saved = JSON.parse(localStorage.getItem('settings'));
    console.log(saved.theme); // 'dark'
  } catch (e) {
    console.warn('Corrupted data', e);
  }
</script>

终止 Worker

terminate()(从主线程调用)或 close()(从 Worker 内部调用)立即停止 Worker 并释放其资源。终止后 Worker 无法复用——必须创建新的。创建 Worker 的页面关闭或导航离开时,Worker 也会被终止。

html5
<script>
  // Listen in OTHER tabs (same origin) for changes
  window.addEventListener('storage', (e) => {
    console.log('Key changed:', e.key);
    console.log('Old value:', e.oldValue);
    console.log('New value:', e.newValue);
    console.log('URL:', e.url);
  });

  // The tab that calls setItem does NOT receive the event
  localStorage.setItem('count', '42');
</script>

共享 Worker

SharedWorker 可同时被同一源上的多个文档(标签页/iframe)访问,适用于共享状态或单一网络连接。通信通过 port 对象进行。onconnect 在每个新连接时触发。SharedWorkers 浏览器支持有限,且不能从 file:// URL 使用。

html5
<script>
  // Remove a single key
  localStorage.removeItem('temp');

  // Wipe all keys for this origin
  localStorage.clear();

  // Iterate keys
  const keys = [];
  for (let i = 0; i < localStorage.length; i++) {
    keys.push(localStorage.key(i));
  }
  console.log(keys);

  // Indexed access (order is not guaranteed)
  localStorage.key(0);
</script>
09

拖放

Draggable 属性

设置 draggable='true' 可使任何元素可拖动。dragstart 事件在源元素上触发;用 e.dataTransfer 设置拖动的数据和允许的效果(copy、move、link 或 all)。图像和链接默认可拖动。文本选择也可以拖动。

html5
<script>
  const request = indexedDB.open('MyDB', 1);

  request.onupgradeneeded = (e) => {
    const db = e.target.result;
    if (!db.objectStoreNames.contains('users')) {
      const store = db.createObjectStore('users', { keyPath: 'id' });
      store.createIndex('email', 'email', { unique: true });
    }
  };

  request.onsuccess = (e) => {
    const db = e.target.result;
    console.log('DB ready:', db.name, db.version);
  };

  request.onerror = (e) => console.error(e.target.error);
</script>

拖放事件

目标上的完整事件序列:dragenter、dragover、drop(以及退出时的 dragleave)。必须在 dragover(通常还有 dragenter)上调用 preventDefault(),否则 drop 事件不会触发。dropEffect 设置光标反馈。在源元素上会得到 dragstart、drag 和操作完成时的 dragend。

html5
<script>
  request.onupgradeneeded = (e) => {
    const db = e.target.result;

    // With a key path
    const users = db.createObjectStore('users', { keyPath: 'id' });

    // Auto-incrementing integer key
    const logs = db.createObjectStore('logs', { autoIncrement: true });

    // Indexes for fast lookups
    users.createIndex('name', 'name', { unique: false });
    users.createIndex('email', 'email', { unique: true });
  };
</script>

DataTransfer

DataTransfer 携带拖动的数据。setData(type, data) 存储多个 MIME 类型的载荷;getData(type) 检索它们。在 dragstart 期间只能设置数据;在 drop 期间可以读取。types 属性列出可用的 MIME 类型。自定义 MIME 类型可区分自己的拖动源。

html5
<script>
  function addUser(db, user) {
    const tx = db.transaction('users', 'readwrite');
    const store = tx.objectStore('users');
    store.add(user);          // throws on duplicate key
    // store.put(user);       // inserts OR overwrites

    tx.oncomplete = () => console.log('Saved');
    tx.onerror = () => console.error(tx.error);
  }

  addUser(db, { id: 1, name: 'Ada', email: '[email protected]' });
</script>

放置区域

常见模式是在 dragover 时用类高亮放置区域,在 dragleave/drop 时移除。e.dataTransfer.files 可访问从操作系统拖入的文件,实现无需 input 的文件上传。注意在移过子元素时 dragleave 可能触发——使用 relatedTarget 或计数器来防抖。

html5
<script>
  const tx = db.transaction('users', 'readonly');
  const store = tx.objectStore('users');

  // By primary key
  const req = store.get(1);
  req.onsuccess = () => console.log(req.result);

  // By index
  const byEmail = store.index('email');
  byEmail.get('[email protected]').onsuccess = (e) => {
    console.log(e.target.result);
  };

  // All records
  store.getAll().onsuccess = (e) => {
    console.log(e.target.result); // array
  };
</script>

拖动图像

setDragImage(element, x, y) 用自定义元素替换默认拖动幽灵。元素必须已渲染(常用屏幕外定位)。x/y 偏移定位光标相对于图像的位置。稍后移除辅助元素以避免留在 DOM 中。

html5
<script>
  const tx = db.transaction('users', 'readwrite');
  const store = tx.objectStore('users');

  // Iterate and modify
  const req = store.openCursor();
  req.onsuccess = (e) => {
    const cursor = e.target.result;
    if (cursor) {
      if (cursor.value.name === 'Ada') {
        cursor.value.role = 'admin';
        cursor.update(cursor.value);
      }
      cursor.continue();
    }
  };

  // Range query on an index
  const range = IDBKeyRange.bound('A', 'M');
  store.index('name').openCursor(range).onsuccess = (e) => { /* ... */ };
</script>
10

History API

pushState

pushState(state, unused, url) 向会话历史添加新条目并更新地址栏,无需页面加载。state 对象可序列化并附加到条目;第二个参数保留(传空字符串或标题以保持兼容)。url 必须遵守同源策略,否则抛出 SecurityError。

html5
<div id="card" draggable="true">
  Drag me!
</div>

<script>
  const card = document.getElementById('card');
  card.addEventListener('dragstart', (e) => {
    e.dataTransfer.setData('text/plain', card.id);
    e.dataTransfer.effectAllowed = 'move';
    card.classList.add('dragging');
  });
  card.addEventListener('dragend', () => {
    card.classList.remove('dragging');
  });
</script>

replaceState

replaceState 与 pushState 签名相同,但替换当前历史条目而非添加新条目。在当前状态应就地更新时使用——例如,消费后移除查询令牌,或更新 URL 以反映页面内更改而不创建额外的后退步骤。

html5
<script>
  // Events on the SOURCE element:
  //   dragstart, drag, dragend

  // Events on the TARGET element:
  //   dragenter, dragover, dragleave, drop

  const drop = document.getElementById('drop');
  drop.addEventListener('dragover', (e) => {
    e.preventDefault();                 // REQUIRED to allow drop
    e.dataTransfer.dropEffect = 'move';
  });
  drop.addEventListener('drop', (e) => {
    e.preventDefault();
    const id = e.dataTransfer.getData('text/plain');
    drop.appendChild(document.getElementById(id));
  });
</script>

popstate 事件

popstate 在用户使用后退/前进按钮(或通过 history.back/go)导航时触发。事件的 state 属性是目标条目的 state 对象。关键是,pushState 和 replaceState 不会触发 popstate——如果需要响应这些,需在推送后显式调用路由逻辑。

html5
<script>
  card.addEventListener('dragstart', (e) => {
    // Multiple data types
    e.dataTransfer.setData('text/plain', 'Hello');
    e.dataTransfer.setData('text/uri-list', 'https://example.com');
    e.dataTransfer.setData('application/json', JSON.stringify({ a: 1 }));

    // Read in the drop handler
    // e.dataTransfer.getData('text/plain')
  });
</script>

导航方法

back()、forward() 和 go(delta) 导航会话历史。go(0) 重新加载页面。history.length 是会话中的条目数(包括当前条目)。history.state 返回当前条目的 state 对象,未设置则为 null。所有导航都会触发 popstate 事件。

html5
<style>
  .dropzone { border: 2px dashed #bbb; padding: 24px; }
  .dropzone.over { background: #eaf6ff; border-color: #3498db; }
</style>

<div class="dropzone" id="dz">Drop files here</div>

<script>
  const dz = document.getElementById('dz');
  dz.addEventListener('dragenter', () => dz.classList.add('over'));
  dz.addEventListener('dragover',  (e) => e.preventDefault());
  dz.addEventListener('dragleave', () => dz.classList.remove('over'));
  dz.addEventListener('drop', (e) => {
    e.preventDefault();
    dz.classList.remove('over');
    const files = e.dataTransfer.files;   // FileList
    for (const f of files) console.log(f.name, f.size);
  });
</script>

SPA 路由模式

典型的 SPA(单页应用)路由器拦截同源链接点击,调用 pushState 更新 URL,并渲染匹配的视图。popstate 监听器通过读取 e.state 或 location.pathname 处理后退/前进按钮。务必检查链接的源,并让外部链接或修饰键点击通过。

html5
<script>
  card.addEventListener('dragstart', (e) => {
    const ghost = document.createElement('div');
    ghost.textContent = 'Dragging card';
    ghost.style.cssText = 'position:absolute;top:-999px;' +
                          'padding:8px;background:#3498db;color:#fff;';
    document.body.appendChild(ghost);
    e.dataTransfer.setDragImage(ghost, 20, 20);
    setTimeout(() => document.body.removeChild(ghost), 0);
  });
</script>
11

Contenteditable

可编辑元素

设置 contenteditable='true' 使元素(及其子元素)可就地编辑。子元素上的 contenteditable='false' 仅使该节点只读。contenteditable='inherit'(默认)跟随父元素。编辑后的 HTML 是元素的 innerHTML,可以读取或保存。

html5
<!-- main.js -->
<script>
  const worker = new Worker('worker.js');

  worker.postMessage({ cmd: 'sum', nums: [1, 2, 3] });

  worker.onmessage = (e) => {
    console.log('Result:', e.data);   // 6
  };

  worker.onerror = (e) => {
    console.error(e.message, e.filename, e.lineno);
  };
</script>

Input 事件

input 事件在内容变化(输入、删除、粘贴)时触发。与 change 不同,它在每次按键时触发。beforeinput 事件允许拦截和取消编辑。对于粘贴,preventDefault 并手动插入纯文本,以避免 Word 或浏览器产生的杂乱格式化 HTML。

html5
// worker.js
self.onmessage = (e) => {
  const { cmd, nums } = e.data;
  if (cmd === 'sum') {
    let total = 0;
    for (const n of nums) total += n;
    self.postMessage(total);
  } else if (cmd === 'heavy') {
    const result = doExpensiveWork();
    self.postMessage(result);
  }
};

function doExpensiveWork() {
  // ... long-running computation
  return 'done';
}

ExecCommand(传统)

document.execCommand 已弃用,但仍是为 contenteditable 内容设置格式(加粗、斜体、列表、链接)的最简单方式。它正被 Selection API 和 Input Events 取代。现代富文本编辑器(Quill、ProseMirror、Slate)为可靠性避免使用 execCommand,但它仍适用于轻量级编辑器。

html5
<script>
  const w = new Worker('worker.js');

  // Plain object
  w.postMessage({ type: 'start' });

  // Transferable: zero-copy move of a buffer
  const buffer = new ArrayBuffer(1024);
  w.postMessage(buffer, [buffer]);
  // buffer is now "neutered" in main thread

  // Two-way communication
  w.onmessage = (e) => console.log('Worker said:', e.data);
</script>

拼写检查

spellcheck='true' 在可编辑元素上启用浏览器的拼写检查器和波浪下划线。可设置在 contenteditable 元素、textarea 和 input 上。用户可通过上下文菜单覆盖。在代码块或用户名上禁用可避免误报下划线。

html5
<script>
  // Send a canvas ImageBitmap to a worker for processing
  const offscreen = canvas.transferControlToOffscreen();
  const worker = new Worker('paint.js');
  worker.postMessage({ canvas: offscreen }, [offscreen]);

  // Now the worker draws directly to the canvas
  // without round-trips to the main thread.
</script>

<!-- paint.js -->
self.onmessage = (e) => {
  const ctx = e.data.canvas.getContext('2d');
  ctx.fillStyle = '#3498db';
  ctx.fillRect(0, 0, 100, 100);
};

保存内容

通过读取 innerHTML 并存储(例如在 localStorage 中或通过 fetch 到服务器)来持久化编辑的内容。对保存操作进行防抖以避免每次按键都写入。注意 XSS:在保存和重新插入前清理 innerHTML,因为 contenteditable 可能产生任意 HTML。

html5
<!-- main.js -->
<script>
  const sw = new SharedWorker('shared.js');
  sw.port.onmessage = (e) => console.log('Broadcast:', e.data);
  sw.port.start();
  sw.port.postMessage({ from: 'tab-A' });
</script>

<!-- shared.js -->
const ports = new Set();
self.onconnect = (e) => {
  const port = e.ports[0];
  ports.add(port);
  port.onmessage = (ev) => {
    // Broadcast to every connected tab
    for (const p of ports) p.postMessage(ev.data);
  };
  port.start();
};
12

数据属性

data-* 属性

data-* 属性存储页面或应用私有的自定义数据。data- 后面的部分必须小写,不能包含大写字母。值始终是字符串,因此复杂数据需编码为 JSON。它们是有效的 HTML5,可被 CSS 和 JavaScript 访问,便于将配置传递给脚本。

html5
<script>
  const ws = new WebSocket('wss://echo.example.com/chat');

  ws.addEventListener('open', () => {
    console.log('Connected');
    ws.send('Hello server!');
  });

  ws.addEventListener('message', (e) => {
    console.log('Received:', e.data);
  });

  ws.addEventListener('close', (e) => {
    console.log('Closed', e.code, e.reason);
  });
</script>

dataset 属性

dataset 属性将 data-* 属性暴露为 DOMStringMap。属性名转换:data-user-id 变为 dataset.userId(驼峰命名)。值始终是字符串。要存储数字、布尔值或对象,需自行解析。设置 dataset.foo 会创建/更新 data-foo 属性。

html5
<script>
  // Strings
  ws.send('plain text');

  // JSON
  ws.send(JSON.stringify({ type: 'chat', text: 'hi' }));

  // Binary
  const blob = new Blob([new Uint8Array([1,2,3])]);
  ws.send(blob);
  ws.send(new ArrayBuffer(8));

  // Check state before sending
  if (ws.readyState === WebSocket.OPEN) ws.send(data);
</script>

读取数据

除 dataset 外,还可以用 getAttribute 读取 data-* 属性,使用完整的短横线命名。CSS 属性选择器(如 [data-role='admin'])允许按数据查询和设置元素样式。这使得 data-* 非常适合以声明方式将状态绑定到元素。

html5
<script>
  ws.addEventListener('message', (e) => {
    // Text messages: e.data is a string
    if (typeof e.data === 'string') {
      const msg = JSON.parse(e.data);
      console.log(msg.type, msg.text);
    }
    // Binary: e.data is a Blob or ArrayBuffer
    else {
      e.data.arrayBuffer().then((buf) => {
        const view = new DataView(buf);
        console.log(view.getInt32(0));
      });
    }
  });

  // Force binary type
  ws.binaryType = 'arraybuffer';
</script>

修改数据

通过 dataset(赋值更新 DOM)或 setAttribute/removeAttribute 修改数据属性。记住值是字符串,需显式转换。用 delete el.dataset.x 删除属性会移除 data-x 属性。这些更改会实时反映在 DOM 和 CSS 属性选择器中。

html5
<script>
  ws.addEventListener('error', (e) => {
    console.error('Socket error', e);
  });

  ws.addEventListener('close', (e) => {
    console.log('code:', e.code, 'reason:', e.reason, 'clean:', e.wasClean);
  });

  // Close gracefully (1000 = normal closure)
  ws.close(1000, 'goodbye');

  // Common codes:
  //   1000 normal, 1001 going away, 1006 abnormal (no close frame)
  //   4000-4999 app-defined
</script>

CSS 集成

CSS 可以通过数据属性定位元素,甚至通过 attr()(用于 content)读取其值。这与 dataset 配合,可根据状态为组件设置样式。attr() 函数在 content 属性上完全支持;在其他属性(width、color)中使用它的支持仍然有限。

html5
<script>
  let ws;
  let retry = 0;

  function connect() {
    ws = new WebSocket('wss://example.com/live');
    ws.onopen = () => { retry = 0; console.log('open'); };
    ws.onmessage = (e) => console.log(e.data);
    ws.onclose = () => {
      const delay = Math.min(1000 * 2 ** retry, 30000);
      retry++;
      setTimeout(connect, delay);
    };
  }
  connect();

  // Heartbeat to detect dead connections
  setInterval(() => {
    if (ws.readyState === WebSocket.OPEN) ws.send('ping');
  }, 30000);
</script>
13

微数据

itemscope 与 itemtype

itemscope 声明元素是一个微数据项,itemtype 指向词汇表 URL(通常是 schema.org)。带有 itemprop 的子元素描述该项的属性。Google 等搜索引擎读取此结构化数据以构建富媒体结果(如人物、产品、事件摘要)。

html5
<script>
  // Add a new entry WITHOUT reloading
  history.pushState(
    { page: 'about' },      // state object
    'About',                // title (ignored by most browsers)
    '/about'                // URL
  );

  console.log(history.length);     // entries count
  console.log(history.state);      // { page: 'about' }
</script>

itemprop

itemprop 命名当前项的属性。对于值不是可见文本的属性,使用 meta(带 content)或 link(带 href)在不渲染的情况下提供值。属性本身也可以是项(通过 itemscope 嵌套),实现如产品包含报价等丰富结构。

html5
<script>
  // Replace the CURRENT entry (no new history item)
  history.replaceState(
    { page: 'home', tab: 'featured' },
    '',
    '/home?tab=featured'
  );

  // Useful for: cleaning up redirects, updating URL after
  // AJAX filter changes, or normalizing the entry URL.
</script>

嵌套项

要嵌套项,在带有 itemprop 的元素上添加 itemscope。嵌套元素既是外部属性的值,也是具有自身属性的独立项。这建模了如电影拥有导演(人物)等关系。Schema.org 定义了数百种类型和属性。

html5
<script>
  window.addEventListener('popstate', (e) => {
    console.log('Location:', location.pathname);
    console.log('State:', e.state);
    renderRoute(location.pathname);
  });

  function navigate(path) {
    history.pushState({ path }, '', path);
    renderRoute(path);
  }

  // NOTE: pushState/replaceState do NOT fire popstate.
  // Only back/forward (and history.go) do.
</script>

itemid 与 itemref

itemid 为项提供全局标识符(如 ISBN 或 URL)。itemref 允许通过列出元素 id 来包含位于项 DOM 子树之外的属性——避免重构标记。谨慎使用,因为它们使结构更难理解。两者都是 HTML 微数据规范的一部分。

html5
<script>
  history.back();              // go back one entry
  history.forward();           // go forward one entry
  history.go(-2);              // go back two entries
  history.go(2);               // go forward two entries
  history.go(0);               // reload current page

  if (history.length === 1) {
    console.log('First page in this tab');
  }
</script>

JSON-LD 替代方案

JSON-LD 是内联微数据的推荐替代方案,用于结构化数据。嵌入一个 JSON 脚本(type application/ld+json),使用 schema.org 词汇描述项。它将数据与标记分离,更易于服务端生成,是 Google 富媒体结果的首选格式。

html5
<script>
  document.body.addEventListener('click', (e) => {
    const a = e.target.closest('a');
    if (!a || !a.matches('[data-link]')) return;
    if (a.origin !== location.origin) return;

    e.preventDefault();
    history.pushState({ path: a.pathname }, '', a.pathname);
    render(a.pathname);
  });

  window.addEventListener('popstate', () => render(location.pathname));

  function render(path) {
    fetch(path, { headers: { 'X-Ajax': '1' } })
      .then((r) => r.text())
      .then((html) => {
        document.querySelector('#app').innerHTML = html;
      });
  }
</script>
14

ARIA 无障碍

地标角色

ARIA 地标角色(banner、navigation、main、complementary、contentinfo、search、form、region)让屏幕阅读器用户跳转到页面区域。在 HTML5 中许多语义元素具有隐式角色,因此显式添加 role 通常是冗余的——但无害,并帮助较旧的辅助技术。在搜索表单上使用 role='search'。

html5
<p>
  Published on
  <time datetime="2024-07-04">July 4, 2024</time>.
</p>
<p>
  Event starts at
  <time datetime="2024-07-04T19:00-05:00">7 PM EST</time>.
</p>
<p>
  Open
  <time datetime="09:00">9:00 AM</time>-
  <time datetime="17:00">5:00 PM</time> daily.
</p>

aria-label 与 aria-labelledby

aria-label 在没有可见标签时(图标按钮)提供无障碍名称。aria-labelledby 指向命名元素的可见文本 id——当存在可见标签时首选,因为它保持同步。aria-describedby 引用解释性文本。优先使用可见标签;仅在文本缺失时使用 aria-label。

html5
<p>
  The function <code>querySelector()</code> returns the
  first match, while <mark>querySelectorAll()</mark>
  returns a static NodeList.
</p>

<p>
  Search results: <mark>HTML5</mark> is the latest
  version of the Hypertext Markup Language.
</p>

aria-hidden

aria-hidden='true' 将元素及其后代从无障碍树中移除,因此屏幕阅读器会跳过它。用于纯装饰性图标或重复内容。绝不要在可聚焦元素(button、link、input)上设置 aria-hidden——它会创建键盘用户可达但屏幕阅读器不可达的陷阱。与焦点管理配合使用。

html5
<details>
  <summary>What is HTML5?</summary>
  <p>HTML5 is the fifth major revision of the HTML standard,
  introducing semantic elements, new form controls, and APIs
  for graphics, media, and offline apps.</p>
</details>

<details open>
  <summary>System requirements</summary>
  <ul>
    <li>Modern browser (Chrome, Firefox, Safari, Edge)</li>
    <li>JavaScript enabled</li>
  </ul>
</details>

aria-live

aria-live 标记屏幕阅读器会播报其变化的区域。polite 等待暂停;assertive 立即打断(谨慎使用)。role='alert' 意味着 assertive + atomic。aria-atomic(true/false)控制是读取整个区域还是仅读取更改。aria-relevant 过滤哪些变化触发播报。

html5
<button onclick="dlg.showModal()">Open dialog</button>

<dialog id="dlg">
  <form method="dialog">
    <p>Are you sure?</p>
    <menu>
      <button value="cancel">Cancel</button>
      <button value="confirm">OK</button>
    </menu>
  </form>
</dialog>

<script>
  const dlg = document.getElementById('dlg');
  dlg.addEventListener('close', () => {
    console.log('User chose:', dlg.returnValue);
  });
</script>

aria-expanded 与 aria-controls

aria-expanded 告诉辅助技术切换(手风琴、菜单、下拉)是否打开。aria-controls 引用被切换元素的 id,以便屏幕阅读器用户导航到它。切换时保持这些属性与实际可见性同步。使用 hidden 属性来实际隐藏内容。

html5
<p>
  <abbr title="World Health Organization">WHO</abbr>
  was founded in 1948.
</p>

<p>
  As <cite>MDN Web Docs</cite> notes,
  <q>HTML is the standard markup language.</q>
</p>

<p>
  Use <dfn>semantics</dfn> to describe the meaning of
  content, then refer to it normally.
</p>
15

Meta 标签

字符集与 Viewport

charset 声明文档编码——将其放在前 1024 字节内(理想情况下是 head 中的第一个元素)。viewport meta 控制移动端布局:width=device-width 匹配设备宽度,initial-scale=1 设置缩放级别。viewport-fit=cover 将内容延伸到刘海区域。为无障碍起见避免禁用缩放。

html5
<head>
  <meta name="viewport"
        content="width=device-width, initial-scale=1, viewport-fit=cover">
</head>

<!-- Without this tag, mobile browsers render the page at a
     ~980px "desktop" width and shrink it down, making text
     unreadable. -->

<!-- viewport-fit=cover lets content extend into the notch
     area on iPhone X+. -->

描述与关键词

description meta 通常显示在搜索结果中标题下方——保持在约 155 个字符以内,且每页唯一。author 和 robots(index/noindex、follow/nofollow)仍然有用。keywords meta 已被 Google 和大多数引擎忽略;不要在此浪费时间。

html5
<picture>
  <!-- Art direction: different image per viewport -->
  <source media="(min-width: 800px)" srcset="wide.jpg">
  <source media="(orientation: portrait)" srcset="tall.jpg">
  <img src="default.jpg" alt="A responsive photo">
</picture>

<!-- Browser picks the first matching <source>,
     falling back to the <img>. -->

Open Graph

Open Graph(og:*)meta 标签最初来自 Facebook,控制在社交平台分享时页面如何显示——标题、描述、图像和类型。og:image 应至少 1200x630 像素。这些标签被 Facebook、LinkedIn、Slack、Discord 等广泛读取以构建富链接预览。

html5
<img src="small.jpg"
     srcset="small.jpg 480w,
             medium.jpg 800w,
             large.jpg  1200w"
     sizes="(max-width: 600px) 100vw,
            (max-width: 1200px) 50vw,
            33vw"
     alt="A responsive photo">

Twitter Cards

Twitter Card meta 标签控制链接在 X/Twitter 上的显示方式。twitter:card 设置卡片类型(summary 或 summary_large_image)。其他标签镜像 Open Graph;如果 twitter:title 缺失,Twitter 回退到 og:title。许多网站同时设置 og:* 和 twitter:* 以确保各处预览良好。

html5
<style>
  .hero { height: 100vh; }           /* full viewport height */
  .half { width: 50vw; }             /* half the viewport width */

  /* Responsive typography */
  h1 {
    font-size: clamp(1.5rem, 4vw + 1rem, 3rem);
  }

  /* Use dvh/svh/lvh to handle mobile browser UI bars */
  .hero { height: 100dvh; }
</style>

Robots 与 Canonical

robots meta 控制爬虫行为:index/noindex、follow/nofollow,以及 max-image-preview 和 max-snippet 等指令。canonical link 标签告诉搜索引擎存在重复时的首选 URL。hreflang alternate 链接指向翻译版本和未匹配区域的 x-default 回退。

html5
<picture>
  <!-- Modern format first, smaller file size -->
  <source type="image/avif" srcset="photo.avif">
  <source type="image/webp" srcset="photo.webp">
  <img src="photo.jpg" alt="Optimized photo" loading="lazy">
</picture>

<!-- Browser picks the first type it supports.
     AVIF and WebP are ~30-50% smaller than JPEG/PNG. -->
16

链接类型(rel)

stylesheet 与 preload

rel='stylesheet' 加载 CSS(默认渲染阻塞)。rel='preload' 以高优先级提前获取关键资源——始终指定 as(script、style、font、image、fetch 等)以便浏览器正确排序。字体使用 crossorigin。modulepreload 在导入前获取并解析 ES 模块。

html5
<div role="banner">           <!-- like <header> -->
  <h1>Site title</h1>
</div>

<div role="navigation">       <!-- like <nav> -->
  <a href="/">Home</a>
</div>

<div role="main">             <!-- like <main> -->
  <div role="article">        <!-- like <article> -->
    <p>Content</p>
  </div>
</div>

<button role="button" aria-pressed="false">Toggle</button>

preconnect 与 dns-prefetch

preconnect 提前建立到第三方源的 DNS、TCP 和 TLS,减少首次请求的延迟。dns-prefetch 仅做 DNS 解析——对不太关键的源的更轻提示。对肯定会访问的源(API、CDN、字体主机)使用 preconnect,在凭据/CORS 重要时添加 crossorigin。

html5
<button aria-label="Close menu">×</button>

<nav aria-labelledby="main-nav-heading">
  <h2 id="main-nav-heading" class="sr-only">Main navigation</h2>
  <ul>...</ul>
</nav>

<input id="email" type="email">
<label id="email-label" for="email">Email address</label>
<!-- aria-labelledby="email-label" would also work -->

prefetch 与 prerender

rel='prefetch' 在空闲时间获取资源(通常是下一页),以加快后续导航。传统的 rel='prerender' 已弃用;现代 Chrome 使用 Speculation Rules API(JSON 脚本)预渲染页面。这些提示以带宽换取感知速度——用于很可能访问的下一目的地。

html5
<!-- Decorative icon: hide from screen readers -->
<svg aria-hidden="true" focusable="false">
  <use href="#icon-check"></use>
</svg>

<!-- Visually hidden but still announced -->
<span class="sr-only">3 new messages</span>

<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>

icon

rel='icon' 定义网站图标;为不同设备提供多种尺寸和格式(ico、png、svg)。svg 图标缩放清晰且支持暗色模式。apple-touch-icon 在用户将网站添加到 iOS 主屏幕时使用。mask-icon 设置 Safari 固定标签页样式。始终在站点根目录包含回退 /favicon.ico。

html5
<!-- Polite: announce when screen reader is idle -->
<div aria-live="polite" id="status">Saving...</div>

<!-- Assertive: announce immediately, may interrupt -->
<div aria-live="assertive" role="alert" id="error"></div>

<script>
  function show(msg) {
    document.getElementById('status').textContent = msg;
  }
  function fail(msg) {
    document.getElementById('error').textContent = msg;
  }
</script>

manifest

rel='manifest' 链接到 Web App Manifest,一个 JSON 文件,允许网站以自己的图标、名称、主题色和显示模式安装为渐进式 Web 应用(PWA)。结合 Service Worker,可实现离线使用和应用般体验。该文件以 application/manifest+json MIME 类型提供。

html5
<style>
  :focus-visible {
    outline: 3px solid #3498db;
    outline-offset: 2px;
  }
</style>

<script>
  // Move focus into a modal
  function openModal(dlg) {
    dlg.showModal();
    dlg.querySelector('input, button').focus();
  }
  // Trap focus inside the modal (simplified)
  // Restore focus to the trigger on close
  const trigger = document.activeElement;
  dlg.addEventListener('close', () => trigger.focus());
</script>

Skip Links

A skip link is the first focusable element on the page and lets keyboard users jump past repetitive navigation to the main content. It's visually hidden until focused. Essential for accessibility—WCAG 2.1 Success Criterion 2.4.1. Pair it with a <main id='main'> landmark so the target is clear.

html5
<body>
  <a href="#main" class="skip-link">Skip to main content</a>
  <header>...nav with 30 links...</header>
  <main id="main">...</main>
</body>

<style>
  .skip-link {
    position: absolute;
    left: -999px;
    top: 0;
    background: #000; color: #fff;
    padding: 8px 16px;
    z-index: 1000;
  }
  .skip-link:focus {
    left: 0;
  }
</style>
17

图片增强

srcset

srcset 列出带有宽度描述符(如 480w)或像素密度描述符(如 2x)的候选图像。浏览器根据视口和设备像素比选择最佳图像,只下载所需的。始终保留 src 回退以兼容旧浏览器。srcset 单独让浏览器决定;宽度描述符需配合 sizes 使用。

html5
<script>
  class MyButton extends HTMLElement {
    constructor() {
      super();
      this.addEventListener('click', () => {
        console.log('Custom button clicked');
      });
    }
  }
  customElements.define('my-button', MyButton);

  // Autonomous element: extends HTMLElement
  // Customized built-in: extends HTMLButtonElement
  //   <button is="my-button">
</script>

<my-button>Click me</my-button>

sizes

sizes 告诉浏览器在各种视口宽度下图像将显示多宽,使用媒体条件。这在使用 srcset 中的宽度描述符时至关重要,因为浏览器在布局前需要渲染尺寸来选择正确的源。每个条目是一个媒体条件后跟一个 CSS 长度。

html5
<script>
  class FancyCard extends HTMLElement {
    constructor() {
      super();
      const shadow = this.attachShadow({ mode: 'open' });
      shadow.innerHTML = `
        <style>
          :host { display: block; padding: 16px;
                  background: #f4f4f4; border-radius: 8px; }
          ::slotted(*) { color: #333; }
        </style>
        <div class="card">
          <slot></slot>
        </div>
      `;
    }
  }
  customElements.define('fancy-card', FancyCard);
</script>

picture 元素

picture 让你控制使用哪张图像:媒体条件用于艺术指导(不同视口裁剪不同图像),type 用于格式回退(AVIF、WebP,然后 JPEG/PNG)。内部的 img 提供默认 src 和 alt 文本,且是必需的。第一个匹配的 source 生效,因此顺序很重要。

html5
<template id="row-template">
  <tr>
    <td class="name"></td>
    <td class="age"></td>
  </tr>
</template>

<script>
  const tpl = document.getElementById('row-template');
  const tbody = document.querySelector('tbody');

  function addRow(name, age) {
    const clone = tpl.content.cloneNode(true);
    clone.querySelector('.name').textContent = name;
    clone.querySelector('.age').textContent = age;
    tbody.appendChild(clone);
  }
  addRow('Ada', 36);
</script>

懒加载

loading='lazy' 延迟加载屏幕外的图像,直到用户滚动到附近,节省带宽并加快首次绘制。首屏图像使用 loading='eager'(默认)。fetchpriority='high' 提升关键首屏图像的优先级。始终设置 width/height 以避免布局偏移。

html5
<fancy-card>
  <h2 slot="title">Card Title</h2>
  <p>Body text goes here.</p>
</fancy-card>

<!-- Inside the shadow DOM of <fancy-card>: -->
<template>
  <div class="card">
    <slot name="title"></slot>
    <div class="body"><slot></slot></div>
  </div>
</template>

异步解码

decoding='async' 让浏览器在主线程之外解码图像,减少大图像加载时的卡顿。'sync' 内联解码(仅在必须保证立即显示时使用)。'auto' 让浏览器选择。配合 width/height 属性和 loading='lazy' 以获得最流畅的体验。

html5
<script>
  class Observed extends HTMLElement {
    static get observedAttributes() {
      return ['count', 'label'];
    }
    constructor() { super(); }
    connectedCallback() {
      console.log('Added to DOM');
    }
    disconnectedCallback() {
      console.log('Removed from DOM');
    }
    attributeChangedCallback(name, oldVal, newVal) {
      console.log(name, 'changed', oldVal, '->', newVal);
    }
  }
  customElements.define('observed-el', Observed);
</script>
18

模板标签

template 元素

template 元素包含已解析但未渲染的 HTML——其内容是惰性的(脚本不运行、图像不加载、样式不应用),直到克隆到 DOM 中。这使其成为存储可复用标记的理想选择。通过 content 属性访问惰性 DOM(一个 DocumentFragment)。

html5
<script>
  if ('serviceWorker' in navigator) {
    window.addEventListener('load', () => {
      navigator.serviceWorker
        .register('/sw.js', { scope: '/' })
        .then((reg) => console.log('SW registered', reg.scope))
        .catch((err) => console.error('SW failed', err));
    });
  }
</script>

克隆内容

使用 template.content.cloneNode(true) 获取模板内容的深层副本作为一个 DocumentFragment,然后用 JS 填充占位符并将其附加到活动 DOM。由于它是片段,附加只触发一次重排。这是完整模板库的轻量替代方案。

html5
// sw.js
const CACHE = 'app-v1';
const ASSETS = ['/', '/styles.css', '/app.js', '/offline.html'];

self.addEventListener('install', (e) => {
  e.waitUntil(
    caches.open(CACHE).then((c) => c.addAll(ASSETS))
  );
});

self.addEventListener('fetch', (e) => {
  e.respondWith(
    caches.match(e.request).then((r) => r || fetch(e.request))
  );
});

插槽

template 内(在 shadow DOM 中)的 slot 元素充当占位符,消费者从 light DOM 子元素填充。具名插槽(slot='title')匹配具有匹配 slot 属性的子元素;默认插槽捕获未分配的子元素。slot 内的默认内容在未提供插槽子元素时显示。

html5
// sw.js
self.addEventListener('fetch', (e) => {
  e.respondWith(
    fetch(e.request).catch(() =>
      caches.match(e.request).then((r) =>
        r || caches.match('/offline.html')
      )
    )
  );
});

// Activate event: clean up old caches
self.addEventListener('activate', (e) => {
  e.waitUntil(
    caches.keys().then((keys) =>
      Promise.all(
        keys.filter((k) => k !== 'app-v1')
            .map((k) => caches.delete(k))
      )
    )
  );
});

自定义元素

自定义元素允许定义新的 HTML 标签。扩展 HTMLElement(或特定元素类),实现生命周期回调(connectedCallback、disconnectedCallback、attributeChangedCallback),并用 customElements.define('my-tag', Class) 注册。标签名必须包含连字符。与 Shadow DOM 配合实现封装。

html5
<!-- index.html -->
<link rel="manifest" href="manifest.json">

<!-- manifest.json -->
{
  "name": "My App",
  "short_name": "App",
  "start_url": "/",
  "display": "standalone",
  "background_color": "#ffffff",
  "theme_color": "#3498db",
  "icons": [
    { "src": "/icon-192.png", "sizes": "192x192", "type": "image/png" },
    { "src": "/icon-512.png", "sizes": "512x512", "type": "image/png" }
  ]
}

Shadow DOM

Shadow DOM 封装组件的标记和样式,使其不外泄,外部样式也不渗入。attachShadow({ mode: 'open' }) 创建可填充的 shadow root。mode:'open' 允许通过 element.shadowRoot 访问 shadow root;'closed' 返回 null。这是可复用 Web 组件的基础。

html5
// sw.js
self.addEventListener('sync', (e) => {
  if (e.tag === 'send-messages') {
    e.waitUntil(sendQueuedMessages());
  }
});

async function sendQueuedMessages() {
  const queue = await getQueue();
  for (const msg of queue) {
    try { await fetch('/api/send', { method: 'POST', body: msg }); }
    catch (e) { throw e; }   // retry later
  }
}
19

杂项 API

全屏 API

requestFullscreen() 在任何元素上进入全屏模式;document 上的 exitFullscreen() 退出。fullscreenchange 事件在转换时触发;document.fullscreenElement 引用活动元素(或 null)。现代浏览器不再需要前缀。全屏需要用户手势和安全上下文。

html5
<head>
  <title>HTML5 Cheatsheet - Semantic Elements & APIs</title>
  <meta name="description"
        content="A quick reference to HTML5 semantic elements,
                 form types, and browser APIs with examples.">
  <meta name="robots" content="index, follow">
  <link rel="canonical" href="https://example.com/html5">
</head>

剪贴板 API

异步 Clipboard API(navigator.clipboard)取代旧的 document.execCommand('copy')。writeText/readText 处理纯文本;write/read 支持富数据(图像、HTML)。它需要安全上下文(https 或 localhost)和用户手势;读取可能提示权限。始终用 try/catch 包裹。

html5
<head>
  <!-- Open Graph (Facebook, LinkedIn, etc.) -->
  <meta property="og:title" content="HTML5 Cheatsheet">
  <meta property="og:description" content="Quick HTML5 reference.">
  <meta property="og:image" content="https://example.com/og.png">
  <meta property="og:url" content="https://example.com/html5">
  <meta property="og:type" content="article">

  <!-- Twitter Card -->
  <meta name="twitter:card" content="summary_large_image">
  <meta name="twitter:site" content="@example">
</head>

通知 API

Notification.requestPermission() 询问用户一次;granted/denied/default。new Notification(title, options) 显示系统通知。tag 分组/替换通知。通知需要安全上下文和用户手势来请求权限。对于 Service Worker 通知(推送),使用 registration.showNotification()。

html5
<head>
  <!-- Canonical: the master URL for duplicate content -->
  <link rel="canonical" href="https://example.com/article">

  <!-- hreflang: language/region alternatives -->
  <link rel="alternate" hreflang="en" href="https://example.com/en/article">
  <link rel="alternate" hreflang="es" href="https://example.com/es/article">
  <link rel="alternate" hreflang="x-default" href="https://example.com/article">
</head>

页面可见性

Page Visibility API 在标签页隐藏或显示时触发 visibilitychange。document.hidden 是布尔值;document.visibilityState 是 'visible'、'hidden' 或 'prerender'。用于在标签页不可见时暂停视频、动画、轮询或分析——节省 CPU、电池和带宽。

html5
<script type="application/ld+json">
{
  "@context": "https://schema.org",
  "@type": "Article",
  "headline": "HTML5 Cheatsheet",
  "author": { "@type": "Person", "name": "Ada" },
  "datePublished": "2024-07-04",
  "image": "https://example.com/cover.png",
  "publisher": {
    "@type": "Organization",
    "name": "Example"
  }
}
</script>

requestAnimationFrame

requestAnimationFrame 在下次重绘前调度回调,与显示器刷新率对齐(约 60 fps)。回调接收高分辨率时间戳。标签页隐藏时自动暂停,节省资源。停止时始终用 cancelAnimationFrame 取消。动画优先使用它而非 setInterval。

html5
<article>
  <h1>Article Title (only one h1 per page)</h1>
  <p>Intro paragraph.</p>

  <section>
    <h2>First major section</h2>
    <p>Content...</p>
    <h3>Subsection</h3>
    <p>Detail...</p>
  </section>

  <section>
    <h2>Second major section</h2>
    <p>Content...</p>
  </section>
</article>
20

文档与 Head

DOCTYPE 与 html

<!DOCTYPE html> 是 HTML5 文档类型声明——在所有浏览器中触发标准模式,且不区分大小写。html 元素是根;lang 设置文档语言(对屏幕阅读器和搜索引擎至关重要),dir 设置文本方向(ltr 或 rtl)。省略 DOCTYPE 会触发怪异模式,破坏布局。

html5
<head>
  <!-- Preload critical resources for the current page -->
  <link rel="preload" href="/fonts/main.woff2"
        as="font" type="font/woff2" crossorigin>
  <link rel="preload" href="/css/critical.css" as="style">
  <link rel="preload" href="/js/app.js" as="script">

  <!-- Preload a hero image -->
  <link rel="preload" href="/hero.webp" as="image">
</head>

head 元素

head 包含机器可读的元数据:charset、viewport、title(显示在标签页和搜索结果中)、description、样式表、图标和脚本。title 元素是必需的。charset 应是第一个元素,以便解析器在读取其他内容前知道编码。head 本身不渲染。

html5
<head>
  <!-- preconnect: early handshake to a third-party origin -->
  <link rel="preconnect" href="https://cdn.example.com">
  <link rel="preconnect" href="https://api.example.com" crossorigin>

  <!-- dns-prefetch: just DNS lookup, lighter -->
  <link rel="dns-prefetch" href="https://cdn.example.com">

  <!-- prefetch: fetch a resource for the NEXT navigation -->
  <link rel="prefetch" href="/next-page.html">
</head>

script async 与 defer

普通 script 在下载和运行时阻塞 HTML 解析。async 并行下载并在就绪后立即运行——不保证顺序;用于独立脚本。defer 并行下载但等待按文档顺序运行,在 DOMContentLoaded 之前——用于依赖 DOM 的脚本。ES 模块默认延迟。

html5
<!-- Native lazy loading (no JS needed) -->
<img src="photo.jpg" loading="lazy" decoding="async" alt="...">
<iframe src="embed.html" loading="lazy"></iframe>

<!-- Eager (default) loads immediately -->
<img src="hero.jpg" loading="eager" fetchpriority="high" alt="Hero">

<!-- Defer non-critical images until they near the viewport -->
<img src="below-fold.jpg" loading="lazy" width="800" height="600" alt="">

base 元素

base 为文档中所有相对 URL 设置默认基 URL 和 target。只能有一个 base 元素,且必须在使用任何相对 URL 之前放在 head 中。实践中 base 很少使用,因为它可能让开发者困惑并破坏框架;绝对 URL 或构建步骤通常更清晰。

html5
<head>
  <!-- async: download in parallel, run ASAP (order not guaranteed) -->
  <script src="analytics.js" async></script>

  <!-- defer: download in parallel, run after parse, in order -->
  <script src="app.js" defer></script>
  <script src="page.js" defer></script>

  <!-- Classic: block parsing to download and run -->
  <script src="blocking.js"></script>
</head>

noscript

noscript 仅在 JavaScript 被禁用或不受支持时渲染其内容。在 body 中显示回退标记;在 head 中只能包含 link、style 和 meta 元素。现代应用通常显示 noscript 消息引导用户启用 JS。它不是渐进增强的替代品。

html5
<head>
  <link rel="preload"    href="/critical.woff2" as="font" crossorigin>
  <link rel="preconnect" href="https://cdn.example.com">
  <link rel="dns-prefetch" href="https://cdn.example.com">
  <link rel="prefetch"   href="/next-page.html">

  <!-- Speculation Rules API (Chrome) -->
  <script type="speculationrules">
  { "prerender": [{ "where": { "href_matches": "/next/*" } }] }
  </script>
</head>

Critical Rendering

Inlining critical CSS for above-the-fold content removes the render-blocking request and improves First Contentful Paint. Load the rest via preload+swap. content-visibility: auto lets the browser skip rendering off-screen sections, dramatically improving scroll performance for long pages—pair with contain-intrinsic-size to reserve space and avoid scrollbar jumpiness.

html5
<head>
  <!-- Inline critical CSS for above-the-fold -->
  <style>
    body { margin: 0; font: system-ui; }
    .hero { height: 60vh; background: #3498db; }
  </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>
<body>
  <!-- Use content-visibility to skip off-screen rendering -->
  <section style="content-visibility: auto; contain-intrinsic-size: 500px;">
    Heavy content here
  </section>
</body>

这篇内容对您有帮助吗?

学习路径

从零开始学习

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