KeyboardEvent.keyCode - Web API 接口参考

KeyboardEvent.keyCode
=====================

**已弃用:** 不再推荐使用该特性。虽然一些浏览器仍然支持它，但也许已从相关的 web 标准中移除，也许正准备移除或出于兼容性而保留。请尽量不要使用该特性，并更新现有的代码；参见本页面底部的兼容性表格以指导你作出决定。请注意，该特性随时可能无法正常工作。

这个只读的属性 **`KeyboardEvent.keyCode`** 代表着一个唯一标识的所按下的键的未修改值，它依据于一个系统和实现相关的数字代码。这通常是与密钥对应的二进制的 ASCII (RFC 20) 或 Windows 1252 码。如果这个键不能被标志，这个值为 0。

你应该尽量避免使用它；它已经被弃用了一段时间。相反的，如果它在你的浏览器中被实现了的话，你应该使用`KeyboardEvent.code`。不幸的是，有一些浏览器还是没有实现它，所以你在使用之前必须要小心，确认你所使用的那个被所有目标浏览器所支持。

**备注：** 在处理 keydown 和 keyup 事件时，Web 开发人员不应使用可打印字符的 keycode 属性。如上所述，keycode 属性对可打印字符不有用，尤其是那些按下 shift 或 alt 键的输入。在实现快捷键处理程序时，事件（“keypress”）事件通常更好（至少当 gecko 是正在使用的运行时）。详情请参见 Gecko 按键事件。

Example
-------

```js
window.addEventListener(
  "keydown",
  function (event) {
    if (event.defaultPrevented) {
      return; // 如果已取消默认操作，则不应执行任何操作
    }

    var handled = false;
    if (event.key !== undefined) {
      // 使用 KeyboardEvent.key 处理事件，并将 handled 设置为 true。
    } else if (event.keyCode !== undefined) {
      //使用 KeyboardEvent.keyCode 处理事件并将 handled 设置为 true。
    }

    if (handled) {
      // 如果事件已处理，则禁止“双重操作”
      event.preventDefault();
    }
  },
  true,
);
```

规范
--

| Specification |
| --- |
| UI Events # dom-keyboardevent-keycode |

浏览器兼容性
------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.

键码值
---

### 标准位置的可打印键

 在标准位置按下或释放可打印键导致的按键事件值在浏览器之间不兼容。
 IE 只将本机虚拟密钥代码值公开为 keyboardvent.keycode。
 Google Chrome、Chromium 和 Safari 必须根据输入字符确定值。如果输入字符可以用 US 键盘布局输入，则使用 US 键盘布局上的 keycode 值。
 从 gecko 15 geckore lease（“15.0”）开始，gecko 从一个可由键输入的 ASCII 字符（即使具有移位修饰符或支持 ASCII 的键盘布局）决定键码值。有关详细信息，请参见以下规则：

1. 如果系统是 Windows，并且按下键的本机键代码指示键是 A-Z 或 0-9，请使用 keycode。
2. 如果系统是 Mac，并且按下键的本机键码指示键为 0-9，则使用 keycode。
3. 如果按下键输入一个 ASCII 字母或数字，没有修改键，请使用 keycode。
4. 如果按下键输入带 SHIFT 键的 ASCII 字母或数字，请使用 keycode。
5. 如果按下键输入另一个没有修改键的 ASCII 字符，请使用 keycode。
6. 如果按下键输入另一个带 SHIFT 键的 ASCII 字符，请使用 keycode。
7. 否则，即按下键输入一个 Unicode 字符：

* 如果键盘布局是支持 ASCII 的键盘布局（即，可以输入 ASCII 字母），则使用 0 或者根据下面的附加规则计算。
* 否则，即键盘布局不支持 ASCII，使用环境中安装的具有最高优先级的支持 ASCII 的键盘布局：
    + 如果按备用键盘布局上的键输入一个 ASCII 字母或数字，请使用 keycode。
    + 否则，使用 0 或者根据下面的附加规则计算。

从 Firefox 60 开始，Gecko 会尽可能的根据以下规则额设置标点符号的 `keyCode` 值（当满足上述 7.1 或者 7.2 的时候）:

**警告：** 这些附加规则的目的是为了使键盘布局映射 unicode 字符映射到美国键盘标点符号的用户可以使用只支持 ASCII 的键盘或者美国键盘布局的 Firefox 的 web 应用。否则，新映射的 `keyCode` 值可能会和其他按键冲突。例如，如果当前键盘布局是俄语，`"Period"` 键 和 `"Slash"` 键的 `keyCode` 都会是 `190`（`KeyEvent.DOM_VK_PERIOD`）。如果你需要区分这些按键但是你自己又不想支持世界上所有的键盘布局，你可能应该使用 `KeyboardEvent.code`。

1. 如果运行 macOS 或者 Linux:
    1. 如果你当前的键盘布局不支持 ASCII 并且候选支持 ASCII 键盘布局可用。
        1. 如果候选支持 ASCII 的键盘布局仅通过未修改的键产生 ASCII 字符，请对该字符使用`keyCode`。
        2. 如果候选支持 ASCII 的键盘布局产生带有 Shift 键修饰符的 ASCII 字符，请对该字符使用`keyCode`。
        3. 否则，在美国键盘布局激活时，使用使用`keyCode`表示由按键产生的 ASCII 字符。
    2. 否则，在美国键盘布局激活时，使用使用`keyCode`表示由按键产生的 ASCII 字符。
2. 如果运行 Windows：
    1. 当美国键盘布局激活时，使用映射到 Windows 的相同虚拟键代码的按键产生的 ASCII 字符的`keyCode`值。

由标准位置的可打印键引起的每个浏览器的 keydown 事件的 keycode 值

| `KeyboardEvent.code` | Internet Explorer 11 | Google Chrome 34 | Chromium 34 | Safari 7 | Gecko 29 |
| --- | --- | --- | --- | --- | --- |
| Windows | Windows | Mac (10.9) | Linux (Ubuntu 14.04) | Mac (10.9) | Windows | Mac (10.9) | Linux (Ubuntu 14.04) |
| US | Japanese | Greek | US | Japanese | Greek | US | Japanese | Greek | US | Japanese | Greek | US | Japanese | Greek | US | Japanese | Greek | US | Japanese | Greek | US | Japanese | Greek |
| `KeyboardEvent.code` | US | Japanese | Greek | US | Japanese | Greek | US | Japanese | Greek | US | Japanese | Greek | US | Japanese | Greek | US | Japanese | Greek | US | Japanese | Greek | US | Japanese | Greek |
| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |
| Windows | Windows | Mac (10.9) | Linux (Ubuntu 14.04) | Mac (10.9) | Windows | Mac (10.9) | Linux (Ubuntu 14.04) |
| Internet Explorer 11 | Google Chrome 34 | Chromium 34 | Safari 7 | Gecko 29 |
| `"Digit1"` | `0x31 (49)` | `0x31 (49)` | `0x31 (49)` | `0x31 (49)` | `0x31 (49)` | `0x31 (49)` | `0x31 (49)` | `0x31 (49)` |
| `"Digit2"` | `0x32 (50)` | `0x32 (50)` | `0x32 (50)` | `0x32 (50)` | `0x32 (50)` | `0x32 (50)` | `0x32 (50)` | `0x32 (50)` |
| `"Digit3"` | `0x33 (51)` | `0x33 (51)` | `0x33 (51)` | `0x33 (51)` | `0x33 (51)` | `0x33 (51)` | `0x33 (51)` | `0x33 (51)` |
| `"Digit4"` | `0x34 (52)` | `0x34 (52)` | `0x34 (52)` | `0x34 (52)` | `0x34 (52)` | `0x34 (52)` | `0x34 (52)` | `0x34 (52)` |
| `"Digit5"` | `0x35 (53)` | `0x35 (53)` | `0x35 (53)` | `0x35 (53)` | `0x35 (53)` | `0x35 (53)` | `0x35 (53)` | `0x35 (53)` |
| `"Digit6"` | `0x36 (54)` | `0x36 (54)` | `0x36 (54)` | `0x36 (54)` | `0x36 (54)` | `0x36 (54)` | `0x36 (54)` | `0x36 (54)` |
| `"Digit7"` | `0x37 (55)` | `0x37 (55)` | `0x37 (55)` | `0x37 (55)` | `0x37 (55)` | `0x37 (55)` | `0x37 (55)` | `0x37 (55)` |
| `"Digit8"` | `0x38 (56)` | `0x38 (56)` | `0x38 (56)` | `0x38 (56)` | `0x38 (56)` | `0x38 (56)` | `0x38 (56)` | `0x38 (56)` |
| `"Digit9"` | `0x39 (57)` | `0x39 (57)` | `0x39 (57)` | `0x39 (57)` | `0x39 (57)` | `0x39 (57)` | `0x39 (57)` | `0x39 (57)` |
| `"Digit0"` | `0x30 (48)` | `0x30 (48)` | `0x30 (48)` | `0x30 (48)` | `0x30 (48)` | `0x30 (48)` | `0x30 (48)` | `0x30 (48)` |
| `"KeyA"` | `0x41 (65)` | `0x41 (65)` | `0x41 (65)` | `0x41 (65)` | `0x41 (65)` | `0x41 (65)` | `0x41 (65)` | `0x41 (65)` |
| `"KeyB"` | `0x42 (66)` | `0x42 (66)` | `0x42 (66)` | `0x42 (66)` | `0x42 (66)` | `0x42 (66)` | `0x42 (66)` | `0x42 (66)` |
| `"KeyC"` | `0x43 (67)` | `0x43 (67)` | `0x43 (67)` | `0x43 (67)` | `0x43 (67)` | `0x43 (67)` | `0x43 (67)` | `0x43 (67)` |
| `"KeyD"` | `0x44 (68)` | `0x44 (68)` | `0x44 (68)` | `0x44 (68)` | `0x44 (68)` | `0x44 (68)` | `0x44 (68)` | `0x44 (68)` |
| `"KeyE"` | `0x45 (69)` | `0x45 (69)` | `0x45 (69)` | `0x45 (69)` | `0x45 (69)` | `0x45 (69)` | `0x45 (69)` | `0x45 (69)` |
| `"KeyF"` | `0x46 (70)` | `0x46 (70)` | `0x46 (70)` | `0x46 (70)` | `0x46 (70)` | `0x46 (70)` | `0x46 (70)` | `0x46 (70)` |
| `"KeyG"` | `0x47 (71)` | `0x47 (71)` | `0x47 (71)` | `0x47 (71)` | `0x47 (71)` | `0x47 (71)` | `0x47 (71)` | `0x47 (71)` |
| `"KeyH"` | `0x48 (72)` | `0x48 (72)` | `0x48 (72)` | `0x48 (72)` | `0x48 (72)` | `0x48 (72)` | `0x48 (72)` | `0x48 (72)` |
| `"KeyI"` | `0x49 (73)` | `0x49 (73)` | `0x49 (73)` | `0x49 (73)` | `0x49 (73)` | `0x49 (73)` | `0x49 (73)` | `0x49 (73)` |
| `"KeyJ"` | `0x4A (74)` | `0x4A (74)` | `0x4A (74)` | `0x4A (74)` | `0x4A (74)` | `0x4A (74)` | `0x4A (74)` | `0x4A (74)` |
| `"KeyK"` | `0x4B (75)` | `0x4B (75)` | `0x4B (75)` | `0x4B (75)` | `0x4B (75)` | `0x4B (75)` | `0x4B (75)` | `0x4B (75)` |
| `"KeyL"` | `0x4C (76)` | `0x4C (76)` | `0x4C (76)` | `0x4C (76)` | `0x4C (76)` | `0x4C (76)` | `0x4C (76)` | `0x4C (76)` |
| `"KeyM"` | `0x4D (77)` | `0x4D (77)` | `0x4D (77)` | `0x4D (77)` | `0x4D (77)` | `0x4D (77)` | `0x4D (77)` | `0x4D (77)` |
| `"KeyN"` | `0x4E (78)` | `0x4E (78)` | `0x4E (78)` | `0x4E (78)` | `0x4E (78)` | `0x4E (78)` | `0x4E (78)` | `0x4E (78)` |
| `"KeyO"` | `0x4F (79)` | `0x4F (79)` | `0x4F (79)` | `0x4F (79)` | `0x4F (79)` | `0x4F (79)` | `0x4F (79)` | `0x4F (79)` |
| `"KeyP"` | `0x50 (80)` | `0x50 (80)` | `0x50 (80)` | `0x50 (80)` | `0x50 (80)` | `0x50 (80)` | `0x50 (80)` | `0x50 (80)` |
| `"KeyQ"` | `0x51 (81)` | `0x51 (81)` | `0x51 (81)` | `0x51 (81)` | `0xBA (186)` | `0x51 (81)` | `0x51 (81)` | `0xBA (186)` | `0x51 (81)` | `0x51 (81)` | `0xBA (186)` | `0x51 (81)` | `0x51 (81)` | `0x51 (81)` | `0xBA (186)` | `0x51 (81)` |
| `"KeyR"` | `0x52 (82)` | `0x52 (82)` | `0x52 (82)` | `0x52 (82)` | `0x52 (82)` | `0x52 (82)` | `0x52 (82)` | `0x52 (82)` |
| `"KeyS"` | `0x53 (83)` | `0x53 (83)` | `0x53 (83)` | `0x53 (83)` | `0x53 (83)` | `0x53 (83)` | `0x53 (83)` | `0x53 (83)` |
| `"KeyT"` | `0x54 (84)` | `0x54 (84)` | `0x54 (84)` | `0x54 (84)` | `0x54 (84)` | `0x54 (84)` | `0x54 (84)` | `0x54 (84)` |
| `"KeyU"` | `0x55 (85)` | `0x55 (85)` | `0x55 (85)` | `0x55 (85)` | `0x55 (85)` | `0x55 (85)` | `0x55 (85)` | `0x55 (85)` |
| `"KeyV"` | `0x56 (86)` | `0x56 (86)` | `0x56 (86)` | `0x56 (86)` | `0x56 (86)` | `0x56 (86)` | `0x56 (86)` | `0x56 (86)` |
| `"KeyW"` | `0x57 (87)` | `0x57 (87)` | `0x57 (87)` | `0x57 (87)` | `0x57 (87)` | `0x57 (87)` | `0x57 (87)` | `0x57 (87)` |
| `"KeyX"` | `0x58 (88)` | `0x58 (88)` | `0x58 (88)` | `0x58 (88)` | `0x58 (88)` | `0x58 (88)` | `0x58 (88)` | `0x58 (88)` |
| `"KeyY"` | `0x59 (89)` | `0x59 (89)` | `0x59 (89)` | `0x59 (89)` | `0x59 (89)` | `0x59 (89)` | `0x59 (89)` | `0x59 (89)` |
| `"KeyZ"` | `0x5A (90)` | `0x5A (90)` | `0x5A (90)` | `0x5A (90)` | `0x5A (90)` | `0x5A (90)` | `0x5A (90)` | `0x5A (90)` |

由标准位置的可打印键（US 布局中的标点符号）引起的每个浏览器的 keydown 事件的 keycode 值：

| `KeyboardEvent.code` | Internet Explorer 11 | Google Chrome 34 | Chromium 34 | Safari 7 | Gecko 29 |
| --- | --- | --- | --- | --- | --- |
| Windows | Windows | Mac (10.9) | Linux (Ubuntu 14.04) | Mac (10.9) | Windows (10.9) | Mac (10.9) | Linux (Ubuntu 14.04) |
| US | Japanese | Greek | US | Japanese | Greek | US | Japanese | Greek | US | Japanese | Greek | US | Japanese | Greek | US | Japanese | Greek | US | Japanese | Greek | US | Japanese | Greek |
| `KeyboardEvent.code` | US | Japanese | Greek | US | Japanese | Greek | US | Japanese | Greek | US | Japanese | Greek | US | Japanese | Greek | US | Japanese | Greek | US | Japanese | Greek | US | Japanese | Greek |
| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |
| Windows | Windows | Mac (10.9) | Linux (Ubuntu 14.04) | Mac (10.9) | Windows | Mac (10.9) | Linux (Ubuntu 14.04) |
| Internet Explorer 11 | Google Chrome 34 | Chromium 34 | Safari 7 | Gecko 29 |
| `"Comma"` | `0xBC (188)` | `0xBC (188)` | `0xBC (188)` | `0xBC (188)` | `0xBC (188)` | `0xBC (188)` | `0xBC (188)` | `0xBC (188)` |
| `"Comma"` with `Shift` |
| `"Period"` | `0xBE (190)` | `0xBE (190)` | `0xBE (190)` | `0xBE (190)` | `0xBE (190)` | `0xBE (190)` | `0xBE (190)` | `0xBE (190)` |
| `"Period"` with `Shift` |
| `"Semicolon"` | `0xBA (186)` | `0xBB (187)` | `0xBA (186)` | `0xBA (186)` | `0xBB (187)` | `0xBA (186)` | `0xBA (186)` | `0xBA (186)` \*1 | `0xE5 (229) *2` | `0xBA (186)` | `0xBA (186)` | `0xE5 (229) *3` | `0xBA (186)` | `0xBA (186)` \*1 | `0xE5 (229) *2` | `0x3B (59)` | `0x3B (59)` | `0x00 (0)` | `0x3B (59)` | `0x3B (59)` \*1 | `0x00 (0)` | `0x3B (59)` | `0x3B (59)` | `0x00 (0)` |
| `"Semicolon"` with `Shift` | `0xBB (187)` \*1 | `0xBB (187)` | `0xBB (187)` \*1 |
| `"Quote"` | `0xDE (222)` | `0xBA (186)` | `0xDE (222)` | `0xDE (222)` | `0xBA (186)` | `0xDE (222)` | `0xDE (222)` | `0xBA (186) *1` | `0xDE (222)` | `0xDE (222)` | `0xBA (186)` | `0xDE (222)` | `0xDE (222)` | `0xBA (186)` \*1 | `0xDE (222)` | `0xDE (222)` | `0x3A (58)` | `0xDE (222)` | `0xDE (222)` | `0x3A (58)` \*1 | `0xDE (222)` | `0xDE (222)` | `0x3A (58)` | `0xDE (222)` |
| `"Quote"` with Shift | `0xDE (222)` \*1 | `0x38 (56)` | `0xDE (222)` \*1 |
| `"BracketLeft"` | `0xDB (219)` | `0xC0(192)` | `0xDB (219)` | `0xDB (219)` | `0xC0(192)` | `0xDB (219)` | `0xDB (219)` | `0xDB (219)` \*1 | `0xDB (219)` | `0xDB (219)` | `0x32 (50)` | `0xDB (219)` | `0xDB (219)` | `0xDB (219) *1` | `0xDB (219)` | `0xDB (219)` | `0x40 (64)` | `0xDB (219)` | `0xDB (219)` | `0x40 (64)` \*1 | `0xDB (219)` | `0xDB (219)` | `0x40 (64)` | `0xDB (219)` |
| `"BracketLeft"` with `Shift` | `0xC0 (192) *1` | `0xC0 (192)` | `0xC0 (192) *1` |
| `"BracketRight"` | `0xDD (221)` | `0xDB (219)` | `0xDD (221)` | `0xDD (221)` | `0xDB (219)` | `0xDD (221)` | `0xDD (221)` | `0xDB (219) *1` | `0xDD (221)` | `0xDD (221)` | `0xDB (219)` | `0xDD (221)` | `0xDD (221)` | `0xDB (219) *1` | `0xDD (221)` | `0xDD (221)` | `0xDB (219)` | `0xDD (221)` | `0xDD (221)` | `0xDB (219)` \*1 | `0xDD (221)` | `0xDD (221)` | `0xDB (219)` | `0xDD (221)` |
| `"BracketRight"` with `Shift` |
| `"Backquote"` | `0xC0 (192)` | `N/A` | `0xC0 (192)` | `0xC0 (192)` | `N/A` | `0xC0 (192)` | `0xC0 (192)` | `0xC0 (192)` | `0xF4 (244)` | `0xC0 (192)` | `0xC0 (192)` | `0xC0 (192)` | `N/A` | `0xC0 (192)` | `0xC0 (192)` | `0xC0 (192)` | `0x00 (0)` | `0xC0 (192)` |
| `"Backquote"` with `Shift` |
| `"Backslash"` | `0xDC (220)` | `0xDD (221)` | `0xDC (220)` | `0xDC (220)` | `0xDD (221)` | `0xDC (220)` | `0xDC (220)` | `0xDC (220)` | `0xDD (221)` | `0xDC (220)` | `0xDC (220)` | `0xDC (220)` | `0xDD (221)` | `0xDC (220)` | `0xDC (220)` | `0xDC (220)` | `0xDD (221)` | `0xDC (220)` |
| `"Backslash"` with `Shift` |
| `"Minus"` | `0xBD (189)` | `0xBD (189)` | `0xBD (189)` | `0xBD (189)` \*1 | `0xBD (189)` | `0xBD (189)` | `0xBD (189)` | `0xBD (189)` | `0xBD (189)` | `0xBD (189) *1` | `0xBD (189)` | `0xAD (173)` | `0xAD (173)` | `0xAD (173) *1` | `0xAD (173)` | `0xAD (173)` |
| `"Minus"` with `Shift` | `0xBB (187)` \*1 | `0xBB (187)` | `0xBD (189)` | `0xBB (187) *1` | `0xBD (189)` |
| `"Equal"` | `0xBB (187)` | `0xDE (222)` | `0xBB (187)` | `0xBB (187)` | `0xDE (222)` | `0xBB (187)` | `0xBB (187)` | `0xBB (187) *1` | `0xBB (187)` | `0xBB (187)` | `0x36 (54)` | `0xBB (187)` | `0xBB (187)` | `0xBB (187) *1` | `0xBB (187)` | `0x3D (61)` | `0xA0 (160)` | `0x3D (61)` | `0x3D (61)` | `0xA0 (160)` \*1 | `0x3D (61)` | `0x3D (61)` | `0xA0 (160)` | `0x3D (61)` |
| `"Equal"` with `Shift` | `0xC0 (192) *1` | `0xC0 (192)` | `0xBB (187)` | `0xC0 (192) *1` | `0xBB (187)` |
| `"IntlRo"` | `0xC1 (193)` | `0xE2 (226)` | `0xC1 (193)` | `0xC1 (193)` | `0xE2 (226)` | `0xC1 (193)` | `0xBD (189)` | `0xBD (189)` | `0x00 (0)` | \*4 | `0xDC (220)` | \*4 | `0xBD (189)` | `0xBD (189)` | `0xE5` (229) \*5 | `0x00 (0)` | `0xDC (220)` | `0x00 (0)` | `0xA7 (167)` | `0xA7 (167)` | `0x00 (0)` | `0x00 (0)` | `0xDC (220)` | `0x00 (0)` |
| `"IntlRo"` with `Shift` |
| `"IntlYen"` | `0xFF (255)` | `0xDC (220)` | `0xFF (255)` | `0xFF (255)` | `0xDC (220)` | `0xFF (255)` | `0x00 (0)` | `0x00 (0)` | `0x00 (0)` | \*4 | `0xDC (220)` | \*4 | `0x00 (0)` | `0x00 (0)` | `0xE5` (229) \*5 | `0x00 (0)` | `0xDC` (220) | `0x00 (0)` | `0xDC` (220) | `0xDC` (220) | `0x00 (0)` | `0x00 (0)` | `0xDC (220)` | `0x00 (0)` |
| `"IntlYen"` with `Shift` | `0xDC (220)` | `0xDC (220)` | `0xBD (189)` | `0xDC (220)` | `0xDC (220)` |

1. 该值是从 JIS 键盘输入的。使用 ANSI 键盘时，键代码值和输入字符是从美国键盘布局中选择的。
2. 按键是一个死键。keyup 事件的值是 0xba（186）。
3. 按键是一个死键。keyup 事件的值为 0x10（16）。
4. 没有触发任何按键事件。
5. 该键在希腊键盘布局中不可用（不输入任何字符）。keyup 事件的值为 0x00（0）。
### 不可打印键（功能键）

由修改键引起的每个浏览器的 keydown 事件的 keycode 值：| `KeyboardEvent.code` | Internet Explorer 11 | Google Chrome 34 | Chromium 34 | Safari 7 | Gecko 29 |
| --- | --- | --- | --- | --- | --- |
| Windows | Windows | Mac (10.9) | Linux (Ubuntu 14.04) | Mac (10.9) | Windows | Mac (10.9) | Linux (Ubuntu 14.04) |
| `KeyboardEvent.code` | Windows | Windows | Mac (10.9) | Linux (Ubuntu 14.04) | Mac (10.9) | Windows | Mac (10.9) | Linux (Ubuntu 14.04) |
| --- | --- | --- | --- | --- | --- | --- | --- | --- |
| Internet Explorer 11 | Google Chrome 34 | Chromium 34 | Safari 7 | Gecko 29 |
| `"AltLeft"` | `0x12 (18)` | `0x12 (18)` | `0x12 (18)` | `0x12 (18)` | `0x12 (18)` | `0x12 (18)` | `0x12 (18)` | `0x12 (18)` |
| `"AltRight"` | `0x12 (18)` | `0x12 (18)` | `0x12 (18)` | `0x12 (18)` | `0x12 (18)` | `0x12 (18)` | `0x12 (18)` | `0x12 (18)` |
| `"AltRight"` when it's `"AltGraph"` key | \*1 | \*1 | N/A | `0xE1 (225)` | N/A | \*1 | N/A | `0xE1 (225)` |
| `"CapsLock"` | `0x14 (20)` \*2 | `0x14 (20)` \*2 | `0x14 (20)` | `0x14 (20)` | `0x14 (20)` | `0x14 (20)` \*2 | `0x14 (20)` | `0x14 (20)` \*3 |
| `"ControlLeft"` | `0x11 (17)` | `0x11 (17)` | `0x11 (17)` | `0x11 (17)` | `0x11 (17)` | `0x11 (17)` | `0x11 (17)` | `0x11 (17)` |
| `"ControlRight"` | `0x11 (17)` | `0x11 (17)` | `0x11 (17)` | `0x11 (17)` | `0x11 (17)` | `0x11 (17)` | `0x11 (17)` | `0x11 (17)` |
| `"OSLeft"` | `0x5B (91)` | `0x5B (91)` | `0x5B (91)` | `0x5B (91)` | `0x5B (91)` | `0x5B (91)` | `0xE0 (224)` | `0x5B (91)` |
| `"OSRight"` | `0x5C (92)` | `0x5C (92)` | `0x5D (93)` | `0x5C (92)` | `0x5D (93)` | `0x5B (91)` | `0xE0 (224)` | `0x5B (91)` |
| `"ShiftLeft"` | `0x10 (16)` | `0x10 (16)` | `0x10 (16)` | `0x10 (16)` | `0x10 (16)` | `0x10 (16)` | `0x10 (16)` | `0x10 (16)` |
| `"ShiftRight"` | `0x10 (16)` | `0x10 (16)` | `0x10 (16)` | `0x10 (16)` | `0x10 (16)` | `0x10 (16)` | `0x10 (16)` | `0x10 (16)` |

1. 在 Windows 上，“altgraph”键导致“controlLeft”键事件和“altRight”键事件。
2. 当日语键盘布局处于活动状态时，“capslock”键没有 `Shift` 会导致 0xf0（240）。该键作为“`Alphanumeric`”键工作，其标签为“英数”。
3. 当日语键盘布局处于活动状态时，“capslock”键没有 `Shift` 会导致 0x00（0）。该键作为“`Alphanumeric`”键工作，其标签为“英数”。

由不可打印的键引起的每个浏览器的 keydown 事件的 keycode 值：

| `KeyboardEvent.code` | Internet Explorer 11 | Google Chrome 34 | Chromium 34 | Safari 7 | Gecko 29 |
| --- | --- | --- | --- | --- | --- |
| Windows | Windows | Mac (10.9) | Linux (Ubuntu 14.04) | Mac (10.9) | Windows | Mac (10.9) | Linux (Ubuntu 14.04) |
| `KeyboardEvent.code` | Windows | Windows | Mac (10.9) | Linux (Ubuntu 14.04) | Mac (10.9) | Windows | Mac (10.9) | Linux (Ubuntu 14.04) |
| --- | --- | --- | --- | --- | --- | --- | --- | --- |
| Internet Explorer 11 | Google Chrome 34 | Chromium 34 | Safari 7 | Gecko 29 |
| `"ContextMenu"` | `0x5D (93)` | `0x5D (93)` | `0x00 (0)` \*1 | `0x5D (93)` | `0x00 (0)` \*1 | `0x5D (93)` | `0x5D (93)` | `0x5D (93)` |
| `"Enter"` | `0x0D (13)` | `0x0D (13)` | `0x0D (13)` | `0x0D (13)` | `0x0D (13)` | `0x0D (13)` | `0x0D (13)` | `0x0D (13)` |
| `"Space"` | `0x20 (32)` | `0x20 (32)` | `0x20 (32)` | `0x20 (32)` | `0x20 (32)` | `0x20 (32)` | `0x20 (32)` | `0x20 (32)` |
| `"Tab"` | `0x09 (9)` | `0x09 (9)` | `0x09 (9)` | `0x09 (9)` | `0x09 (9)` | `0x09 (9)` | `0x09 (9)` | `0x09 (9)` |
| `"Delete"` | `0x2E (46)` | `0x2E (46)` | `0x2E (46)` | `0x2E (46)` | `0x2E (46)` | `0x2E (46)` | `0x2E (46)` | `0x2E (46)` |
| `"End"` | `0x23 (35)` | `0x23 (35)` | `0x23 (35)` | `0x23 (35)` | `0x23 (35)` | `0x23 (35)` | `0x23 (35)` | `0x23 (35)` |
| `"Help"` | N/A | N/A | `0x2D (45)` \*2 | `0x2F (47)` \*3 | `0x2D (45)` \*2 | N/A | `0x2D (45)` \*2 | `0x06 (6)` \*3 |
| `"Home"` | `0x24 (36)` | `0x24 (36)` | `0x24 (36)` | `0x24 (36)` | `0x24 (36)` | `0x24 (36)` | `0x24 (36)` | `0x24 (36)` |
| `"Insert"` | `0x2D (45)` | `0x2D (45)` | `0x2D (45)` | `0x2D (45)` | `0x2D (45)` | `0x2D (45)` | `0x2D (45)` | `0x2D (45)` |
| `"PageDown"` | `0x22 (34)` | `0x22 (34)` | `0x22 (34)` | `0x22 (34)` | `0x22 (34)` | `0x22 (34)` | `0x22 (34)` | `0x22 (34)` |
| `"PageUp"` | `0x21 (33)` | `0x21 (33)` | `0x21 (33)` | `0x21 (33)` | `0x21 (33)` | `0x21 (33)` | `0x21 (33)` | `0x21 (33)` |
| `"ArrowDown"` | `0x28 (40)` | `0x28 (40)` | `0x28 (40)` | `0x28 (40)` | `0x28 (40)` | `0x28 (40)` | `0x28 (40)` | `0x28 (40)` |
| `"ArrowLeft"` | `0x25 (37)` | `0x25 (37)` | `0x25 (37)` | `0x25 (37)` | `0x25 (37)` | `0x25 (37)` | `0x25 (37)` | `0x25 (37)` |
| `"ArrowRight"` | `0x27 (39)` | `0x27 (39)` | `0x27 (39)` | `0x27 (39)` | `0x27 (39)` | `0x27 (39)` | `0x27 (39)` | `0x27 (39)` |
| `"ArrowUp"` | `0x26 (38)` | `0x26 (38)` | `0x26 (38)` | `0x26 (38)` | `0x26 (38)` | `0x26 (38)` | `0x26 (38)` | `0x26 (38)` |
| `"Escape"` | `0x1B (27)` | `0x1B (27)` | `0x1B (27)` | `0x1B (27)` | `0x1B (27)` | `0x1B (27)` | `0x1B (27)` | `0x1B (27)` |
| `"PrintScreen"` | `0x2C (44)` \*4 | `0x2C (44)` \*4 | `0x7C (124)`\*5 | `0x2A (42)` | `0x7C (124)`\*5 | `0x2C (44)` \*4 | `0x2C (44)` | `0x2A (42)` |
| `"ScrollLock"` | `0x91 (145)` | `0x91 (145)` | `0x7D (125)`\*5 | `0x91 (145)` | `0x7D (125)`\*5 | `0x91 (145)` | `0x91 (145)` | `0x91 (145)` |
| `"Pause"` | `0x13 (19)` \*6 | `0x13 (19)` \*6 | `0x7E (126)`\*5 | `0x13 (19)` | `0x7E (126)`\*5 | `0x13 (19)` \*6 | `0x13 (19)` | `0x13 (19)` |

1. keypress 事件被激发，其 keypcode 和 charcode 为 0x10（16），但文本未输入编辑器。
2. 在 Mac 电脑上，“`Help`”键映射到电脑键盘的“`Insert`”键。这些`keyCode` 值与“`Insert`”键的`keyCode`值相同。
3. 在 Fedora 20 上测试。
4. 仅激发 keyup 事件。
5. PC 的“`PrintScreen`”、“`ScrollLock`”和“`Pause`”映射到 Mac 的“F13”、“F14”和“F15”。Chrome 和 Safari 用 Mac 的键映射相同的`keyCode`值。
6. “`Pause`”键加上 `Control` 导致 0x03（3）。

由功能键引起的每个浏览器的 keydown 事件的 keycode 值：

| `KeyboardEvent.code` | Internet Explorer 11 | Google Chrome 34 | Chromium 34 | Safari 7 | Gecko 29 |
| --- | --- | --- | --- | --- | --- |
| Windows | Windows | Mac (10.9) | Linux (Ubuntu 14.04) | Mac (10.9) | Windows | Mac (10.9) | Linux (Ubuntu 14.04) |
| `KeyboardEvent.code` | Windows | Windows | Mac (10.9) | Linux (Ubuntu 14.04) | Mac (10.9) | Windows | Mac (10.9) | Linux (Ubuntu 14.04) |
| --- | --- | --- | --- | --- | --- | --- | --- | --- |
| Internet Explorer 11 | Google Chrome 34 | Chromium 34 | Safari 7 | Gecko 29 |
| `"F1"` | `0x70 (112)` | `0x70 (112)` | `0x70 (112)` | `0x70 (112)` | `0x70 (112)` | `0x70 (112)` | `0x70 (112)` | `0x70 (112)` |
| `"F2"` | `0x71 (113)` | `0x71 (113)` | `0x71 (113)` | `0x71 (113)` | `0x71 (113)` | `0x71 (113)` | `0x71 (113)` | `0x71 (113)` |
| `"F3"` | `0x72 (114)` | `0x72 (114)` | `0x72 (114)` | `0x72 (114)` | `0x72 (114)` | `0x72 (114)` | `0x72 (114)` | `0x72 (114)` |
| `"F4"` | `0x73 (115)` | `0x73 (115)` | `0x73 (115)` | `0x73 (115)` | `0x73 (115)` | `0x73 (115)` | `0x73 (115)` | `0x73 (115)` |
| `"F5"` | `0x74 (116)` | `0x74 (116)` | `0x74 (116)` | `0x74 (116)` | `0x74 (116)` | `0x74 (116)` | `0x74 (116)` | `0x74 (116)` |
| `"F6"` | `0x75 (117)` | `0x75 (117)` | `0x75 (117)` | `0x75 (117)` | `0x75 (117)` | `0x75 (117)` | `0x75 (117)` | `0x75 (117)` |
| `"F7"` | `0x76 (118)` | `0x76 (118)` | `0x76 (118)` | `0x76 (118)` | `0x76 (118)` | `0x76 (118)` | `0x76 (118)` | `0x76 (118)` |
| `"F8"` | `0x77 (119)` | `0x77 (119)` | `0x77 (119)` | `0x77 (119)` | `0x77 (119)` | `0x77 (119)` | `0x77 (119)` | `0x77 (119)` |
| `"F9"` | `0x78 (120)` | `0x78 (120)` | `0x78 (120)` | `0x78 (120)` | `0x78 (120)` | `0x78 (120)` | `0x78 (120)` | `0x78 (120)` |
| `"F10"` | `0x79 (121)` | `0x79 (121)` | `0x79 (121)` | `0x79 (121)` | `0x79 (121)` | `0x79 (121)` | `0x79 (121)` | `0x79 (121)` |
| `"F11"` | `0x7A (122)` | `0x7A (122)` | `0x7A (122)` | `0x7A (122)` | `0x7A (122)` | `0x7A (122)` | `0x7A (122)` | `0x7A (122)` |
| `"F12"` | `0x7B (123)` | `0x7B (123)` | `0x7B (123)` | `0x7B (123)` | `0x7B (123)` | `0x7B (123)` | `0x7B (123)` | `0x7B (123)` |
| `"F13"` | `0x7C (124)` | `0x7C (124)` | `0x7C (124)` | `0x7C (124)` \*1 | `0x7C (124)` | `0x7C (124)` | `0x2C (44)` \*2 | `0x00 (0)` \*3 |
| `"F14"` | `0x7D (125)` | `0x7D (125)` | `0x7D (125)` | `0x7D (125)` \*1 | `0x7D (125)` | `0x7D (125)` | `0x91 (145)` \*2 | `0x00 (0)` \*3 |
| `"F15"` | `0x7E (126)` | `0x7E (126)` | `0x7E (126)` | `0x7E (126)` \*1 | `0x7E (126)` | `0x7E (126)` | `0x13 (19)` \*2 | `0x00 (0)` \*3 |
| `"F16"` | `0x7F (127)` | `0x7F (127)` | `0x7F (127)` | `0x7F (127)` \*1 | `0x7F (127)` | `0x7F (127)` | `0x7F (127)` | `0x00 (0)` \*3 |
| `"F17"` | `0x80 (128)` | `0x80 (128)` | `0x80 (128)` | `0x80 (128)` \*1 | `0x80 (128)` | `0x80 (128)` | `0x80 (128)` | `0x00 (0)` \*3 |
| `"F18"` | `0x81 (129)` | `0x81 (129)` | `0x81 (129)` | `0x81 (129)` \*1 | `0x81 (129)` | `0x81 (129)` | `0x81 (129)` | `0x00 (0)` \*3 |
| `"F19"` | `0x82 (130)` | `0x82 (130)` | `0x82 (130)` | `N/A` \*4 | `0x82 (130)` | `0x82 (130)` | `0x82 (130)` | `0x00 (0)` \*3 |
| `"F20"` | `0x83 (131)` | `0x83 (131)` | `0x83 (131)` | `N/A` \*4 | `0xE5 (229)` \*5 | `0x83 (131)` | `0x00 (0)` | `N/A` \*6 |
| `"F21"` | `0x84 (132)` | `0x84 (132)` | `0x00 (0)` \*7 | `N/A` \*4 | `0x00 (0)` \*7 | `0x84 (132)` | `N/A` \*8 | `N/A` \*6 |
| `"F22"` | `0x85 (133)` | `0x85 (133)` | `0x00 (0)` \*7 | `N/A` \*4 | `0x00 (0)` \*7 | `0x85 (133)` | `N/A` \*8 | `N/A` \*6 |
| `"F23"` | `0x86 (134)` | `0x86 (134)` | `0x00 (0)` \*7 | `N/A` \*4 | `0x00 (0)` \*7 | `0x86 (134)` | `N/A` \*8 | `N/A` \*6 |
| `"F24"` | `0x87 (135)` | `0x87 (135)` | `0x00 (0)` \*7 | `N/A` \*4 | `0x00 (0)` \*7 | `0x87 (135)` | `N/A` \*8 | `0x00 (0)` \*3 |

1. 在 Fedora 20 上测试。
2. PC 的“`PrintScreen`”、“`ScrollLock`”和“`Pause`”映射到 Mac 的“F13”、“F14”和“F15”。火狐映射到和 PC 相同的`keyCode`。
3. 在 Fedora 20 上测试。这些键不会导致`GDK_Fxx` 按键符号。如果键产生正确的按键符号，这些值必须与 IE 相同。
4. 在 Fedora 20 上测试。这些键不会在 chromium 上引起 dom 键事件。
5. keyup 事件的 keycode 值为 0x83（131）。
6. 在 Fedora 20 上测试。这些密钥不会在 Firefox 上引起 DOM 密钥事件。
7. 仅激发 keydown 事件。
8. 在 Firefox 上不会触发任何 DOM 密钥事件。
### 小键盘键

由 numPad 中的键在 numLock 状态下导致的每个浏览器的 keyDown 事件的 keycode 值

| `KeyboardEvent.code` | Internet Explorer 11 | Google Chrome 34 | Chromium 34 | Safari 7 | Gecko 29 |
| --- | --- | --- | --- | --- | --- |
| Windows | Windows | Mac (10.9) | Linux (Ubuntu 14.04) | Mac (10.9) | Windows | Mac (10.9) | Linux (Ubuntu 14.04) |
| `KeyboardEvent.code` | Windows | Windows | Mac (10.9) | Linux (Ubuntu 14.04) | Mac (10.9) | Windows | Mac (10.9) | Linux (Ubuntu 14.04) |
| --- | --- | --- | --- | --- | --- | --- | --- | --- |
| Internet Explorer 11 | Google Chrome 34 | Chromium 34 | Safari 7 | Gecko 29 |
| `"NumLock"` | `0x90 (144)` | `0x90 (144)` | `0x0C (12)` \*1 | `0x90 (144)` | `0x0C (12)` \*1 | `0x90 (144)` | `0x0C (12)` \*1 | `0x90 (144)` |
| `"Numpad0"` | `0x60 (96)` | `0x60 (96)` | `0x60 (96)` | `0x60 (96)` | `0x60 (96)` | `0x60 (96)` | `0x60 (96)` | `0x60 (96)` |
| `"Numpad1"` | `0x61 (97)` | `0x61 (97)` | `0x61 (97)` | `0x61 (97)` | `0x61 (97)` | `0x61 (97)` | `0x61 (97)` | `0x61 (97)` |
| `"Numpad2"` | `0x62 (98)` | `0x62 (98)` | `0x62 (98)` | `0x62 (98)` | `0x62 (98)` | `0x62 (98)` | `0x62 (98)` | `0x62 (98)` |
| `"Numpad3"` | `0x63 (99)` | `0x63 (99)` | `0x63 (99)` | `0x63 (99)` | `0x63 (99)` | `0x63 (99)` | `0x63 (99)` | `0x63 (99)` |
| `"Numpad4"` | `0x64 (100)` | `0x64 (100)` | `0x64 (100)` | `0x64 (100)` | `0x64 (100)` | `0x64 (100)` | `0x64 (100)` | `0x64 (100)` |
| `"Numpad5"` | `0x65 (101)` | `0x65 (101)` | `0x65 (101)` | `0x65 (101)` | `0x65 (101)` | `0x65 (101)` | `0x65 (101)` | `0x65 (101)` |
| `"Numpad6"` | `0x66 (102)` | `0x66 (102)` | `0x66 (102)` | `0x66 (102)` | `0x66 (102)` | `0x66 (102)` | `0x66 (102)` | `0x66 (102)` |
| `"Numpad7"` | `0x67 (103)` | `0x67 (103)` | `0x67 (103)` | `0x67 (103)` | `0x67 (103)` | `0x67 (103)` | `0x67 (103)` | `0x67 (103)` |
| `"Numpad8"` | `0x68 (104)` | `0x68 (104)` | `0x68 (104)` | `0x68 (104)` | `0x68 (104)` | `0x68 (104)` | `0x68 (104)` | `0x68 (104)` |
| `"Numpad9"` | `0x69 (105)` | `0x69 (105)` | `0x69 (105)` | `0x69 (105)` | `0x69 (105)` | `0x69 (105)` | `0x69 (105)` | `0x69 (105)` |
| `"NumpadAdd"` | `0x6B (107)` | `0x6B (107)` | `0x6B (107)` | `0x6B (107)` | `0x6B (107)` | `0x6B (107)` | `0x6B (107)` | `0x6B (107)` |
| `"NumpadComma"` inputting `","` | `0xC2 (194)` | `0xC2 (194)` | `0xBC (188)` | `Always inputs` "." | `0xBC (188)` | `0xC2 (194)` | `0x6C (108)` | `Always inputs` "." |
| `"NumpadComma"` inputting `"."` or empty string | `0xC2 (194)` | `0xC2 (194)` | `0xBE (190)` | `0x6E (110)` | `0xBE (190)` | `0xC2 (194)` | `0x6C (108)` | `0x6E (110)` |
| `"NumpadDecimal"` inputting `"."` | `0x6E (110)` | `0x6E (110)` | `0x6E (110)` | `0x6E (110)` | `0x6E (110)` | `0x6E (110)` | `0x6E (110)` | `0x6E (110)` |
| `"NumpadDecimal"` inputting `","` | `0x6E (110)` | `0x6E (110)` | `0x6E (110)` | `0x6C (108)` | `0x6E (110)` | `0x6E (110)` | `0x6E (110)` | `0x6C (108)` |
| `"NumpadDivide"` | `0x6F (111)` | `0x6F (111)` | `0x6F (111)` | `0x6F (111)` | `0x6F (111)` | `0x6F (111)` | `0x6F (111)` | `0x6F (111)` |
| `"NumpadEnter"` | `0x0D (13)` | `0x0D (13)` | `0x0D (13)` | `0x0D (13)` | `0x0D (13)` | `0x0D (13)` | `0x0D (13)` | `0x0D (13)` |
| `"NumpadEqual"` | `0x0C (12)` | `0x0C (12)` | `0xBB (187)` | `0xBB (187)` | `0xBB (187)` | `0x0C (12)` | `0x3D (61)` | `0x3D (61)` |
| `"NumpadMultiply"` | `0x6A (106)` | `0x6A (106)` | `0x6A (106)` | `0x6A (106)` | `0x6A (106)` | `0x6A (106)` | `0x6A (106)` | `0x6A (106)` |
| `"NumpadSubtract"` | `0x6D (109)` | `0x6D (109)` | `0x6D (109)` | `0x6D (109)` | `0x6D (109)` | `0x6D (109)` | `0x6D (109)` | `0x6D (109)` |

1. “numlock”键在 Mac 上作为“clear”键工作。

由处于无 numlock 状态的 numpad 中的键引起的每个浏览器的 keydown 事件的 keycode 值

| `KeyboardEvent.code` | Internet Explorer 11 | Google Chrome 34 | Chromium 34 | Gecko 29 |
| --- | --- | --- | --- | --- |
| Windows | Windows | Linux (Ubuntu 14.04) | Windows | Linux (Ubuntu 14.04) |
| `KeyboardEvent.code` | Windows | Windows | Linux (Ubuntu 14.04) | Windows | Linux (Ubuntu 14.04) |
| --- | --- | --- | --- | --- | --- |
| Internet Explorer 11 | Google Chrome 34 | Chromium 34 | Gecko 29 |
| `"Numpad0"` (`"Insert"`) | `0x2D (45)` | `0x2D (45)` | `0x2D (45)` | `0x2D (45)` | `0x2D (45)` |
| `"Numpad1"` (`"End"`) | `0x23 (35)` | `0x23 (35)` | `0x23 (35)` | `0x23 (35)` | `0x23 (35)` |
| `"Numpad2"` (`"ArrowDown"`) | `0x28 (40)` | `0x28 (40)` | `0x28 (40)` | `0x28 (40)` | `0x28 (40)` |
| `"Numpad3"` (`"PageDown"`) | `0x22 (34)` | `0x22 (34)` | `0x22 (34)` | `0x22 (34)` | `0x22 (34)` |
| `"Numpad4"` (`"ArrowLeft"`) | `0x25 (37)` | `0x25 (37)` | `0x25 (37)` | `0x25 (37)` | `0x25 (37)` |
| `"Numpad5"` | `0x0C (12)` | `0x0C (12)` | `0x0C (12)` | `0x0C (12)` | `0x0C (12)` |
| `"Numpad6"` (`"ArrowRight"`) | `0x27 (39)` | `0x27 (39)` | `0x27 (39)` | `0x27 (39)` | `0x27 (39)` |
| `"Numpad7"` (`"Home"`) | `0x24 (36)` | `0x24 (36)` | `0x24 (36)` | `0x24 (36)` | `0x24 (36)` |
| `"Numpad8"` (`"ArrowUp"`) | `0x26 (38)` | `0x26 (38)` | `0x26 (38)` | `0x26 (38)` | `0x26 (38)` |
| `"Numpad9"` (`"PageUp"`) | `0x21 (33)` | `0x21 (33)` | `0x21 (33)` | `0x21 (33)` | `0x21 (33)` |
| `"NumpadDecimal"` (`"Delete"`) | `0x2E (46)` | `0x2E (46)` | `0x2E (46)` | `0x2E (46)` | `0x2E (46)` |

最近的 Mac 没有“numlock”键和状态。因此，未锁定状态不可用。

常数值的键码
------

gecko 在 keyboardvent 中定义了许多 keycode 值，用于显式地生成映射表。这些值对 Firefox 的附加开发人员很有用，但在公共网页中却没有那么有用。

| 常数 | 值 | 描述 |
| --- | --- | --- |
| `DOM_VK_CANCEL` | 0x03 (3) | Cancel key. |
| `DOM_VK_HELP` | 0x06 (6) | Help key. |
| `DOM_VK_BACK_SPACE` | 0x08 (8) | Backspace key. |
| `DOM_VK_TAB` | 0x09 (9) | Tab key. |
| `DOM_VK_CLEAR` | 0x0C (12) | "5" key on Numpad when NumLock is unlocked. Or on Mac, clear key which is positioned at NumLock key. |
| `DOM_VK_RETURN` | 0x0D (13) | Return/enter key on the main keyboard. |
| `DOM_VK_ENTER` | 0x0E (14) | Reserved, but not used.
已弃用
 (Dropped, see Firefox bug 969247.) |
| `DOM_VK_SHIFT` | 0x10 (16) | Shift key. |
| `DOM_VK_CONTROL` | 0x11 (17) | Control key. |
| `DOM_VK_ALT` | 0x12 (18) | Alt (Option on Mac) key. |
| `DOM_VK_PAUSE` | 0x13 (19) | Pause key. |
| `DOM_VK_CAPS_LOCK` | 0x14 (20) | Caps lock. |
| `DOM_VK_KANA` | 0x15 (21) | Linux support for this keycode was added in Gecko 4.0. |
| `DOM_VK_HANGUL` | 0x15 (21) | Linux support for this keycode was added in Gecko 4.0. |
| `DOM_VK_EISU` | 0x 16 (22) | "英数" key on Japanese Mac keyboard. |
| `DOM_VK_JUNJA` | 0x17 (23) | Linux support for this keycode was added in Gecko 4.0. |
| `DOM_VK_FINAL` | 0x18 (24) | Linux support for this keycode was added in Gecko 4.0. |
| `DOM_VK_HANJA` | 0x19 (25) | Linux support for this keycode was added in Gecko 4.0. |
| `DOM_VK_KANJI` | 0x19 (25) | Linux support for this keycode was added in Gecko 4.0. |
| `DOM_VK_ESCAPE` | 0x1B (27) | Escape key. |
| `DOM_VK_CONVERT` | 0x1C (28) | Linux support for this keycode was added in Gecko 4.0. |
| `DOM_VK_NONCONVERT` | 0x1D (29) | Linux support for this keycode was added in Gecko 4.0. |
| `DOM_VK_ACCEPT` | 0x1E (30) | Linux support for this keycode was added in Gecko 4.0. |
| `DOM_VK_MODECHANGE` | 0x1F (31) | Linux support for this keycode was added in Gecko 4.0. |
| `DOM_VK_SPACE` | 0x20 (32) | Space bar. |
| `DOM_VK_PAGE_UP` | 0x21 (33) | Page Up key. |
| `DOM_VK_PAGE_DOWN` | 0x22 (34) | Page Down key. |
| `DOM_VK_END` | 0x23 (35) | End key. |
| `DOM_VK_HOME` | 0x24 (36) | Home key. |
| `DOM_VK_LEFT` | 0x25 (37) | Left arrow. |
| `DOM_VK_UP` | 0x26 (38) | Up arrow. |
| `DOM_VK_RIGHT` | 0x27 (39) | Right arrow. |
| `DOM_VK_DOWN` | 0x28 (40) | Down arrow. |
| `DOM_VK_SELECT` | 0x29 (41) | Linux support for this keycode was added in Gecko 4.0. |
| `DOM_VK_PRINT` | 0x2A (42) | Linux support for this keycode was added in Gecko 4.0. |
| `DOM_VK_EXECUTE` | 0x2B (43) | Linux support for this keycode was added in Gecko 4.0. |
| `DOM_VK_PRINTSCREEN` | 0x2C (44) | Print Screen key. |
| `DOM_VK_INSERT` | 0x2D (45) | Ins(ert) key. |
| `DOM_VK_DELETE` | 0x2E (46) | Del(ete) key. |
| `DOM_VK_0` | 0x30 (48) | "0" key in standard key location. |
| `DOM_VK_1` | 0x31 (49) | "1" key in standard key location. |
| `DOM_VK_2` | 0x32 (50) | "2" key in standard key location. |
| `DOM_VK_3` | 0x33 (51) | "3" key in standard key location. |
| `DOM_VK_4` | 0x34 (52) | "4" key in standard key location. |
| `DOM_VK_5` | 0x35 (53) | "5" key in standard key location. |
| `DOM_VK_6` | 0x36 (54) | "6" key in standard key location. |
| `DOM_VK_7` | 0x37 (55) | "7" key in standard key location. |
| `DOM_VK_8` | 0x38 (56) | "8" key in standard key location. |
| `DOM_VK_9` | 0x39 (57) | "9" key in standard key location. |
| `DOM_VK_COLON` | 0x3A (58) | Colon (":") key. |
| `DOM_VK_SEMICOLON` | 0x3B (59) | Semicolon (";") key. |
| `DOM_VK_LESS_THAN` | 0x3C (60) | Less-than ("<") key. |
| `DOM_VK_EQUALS` | 0x3D (61) | Equals ("=") key. |
| `DOM_VK_GREATER_THAN` | 0x3E (62) | Greater-than (">") key. |
| `DOM_VK_QUESTION_MARK` | 0x3F (63) | Question mark ("?") key. |
| `DOM_VK_AT` | 0x40 (64) | Atmark ("@") key. |
| `DOM_VK_A` | 0x41 (65) | "A" key. |
| `DOM_VK_B` | 0x42 (66) | "B" key. |
| `DOM_VK_C` | 0x43 (67) | "C" key. |
| `DOM_VK_D` | 0x44 (68) | "D" key. |
| `DOM_VK_E` | 0x45 (69) | "E" key. |
| `DOM_VK_F` | 0x46 (70) | "F" key. |
| `DOM_VK_G` | 0x47 (71) | "G" key. |
| `DOM_VK_H` | 0x48 (72) | "H" key. |
| `DOM_VK_I` | 0x49 (73) | "I" key. |
| `DOM_VK_J` | 0x4A (74) | "J" key. |
| `DOM_VK_K` | 0x4B (75) | "K" key. |
| `DOM_VK_L` | 0x4C (76) | "L" key. |
| `DOM_VK_M` | 0x4D (77) | "M" key. |
| `DOM_VK_N` | 0x4E (78) | "N" key. |
| `DOM_VK_O` | 0x4F (79) | "O" key. |
| `DOM_VK_P` | 0x50 (80) | "P" key. |
| `DOM_VK_Q` | 0x51 (81) | "Q" key. |
| `DOM_VK_R` | 0x52 (82) | "R" key. |
| `DOM_VK_S` | 0x53 (83) | "S" key. |
| `DOM_VK_T` | 0x54 (84) | "T" key. |
| `DOM_VK_U` | 0x55 (85) | "U" key. |
| `DOM_VK_V` | 0x56 (86) | "V" key. |
| `DOM_VK_W` | 0x57 (87) | "W" key. |
| `DOM_VK_X` | 0x58 (88) | "X" key. |
| `DOM_VK_Y` | 0x59 (89) | "Y" key. |
| `DOM_VK_Z` | 0x5A (90) | "Z" key. |
| `DOM_VK_WIN` | 0x5B (91) | Windows logo key on Windows. Or Super or Hyper key on Linux. |
| `DOM_VK_CONTEXT_MENU` | 0x5D (93) | Opening context menu key. |
| `DOM_VK_SLEEP` | 0x5F (95) | Linux support for this keycode was added in Gecko 4.0. |
| `DOM_VK_NUMPAD0` | 0x60 (96) | "0" on the numeric keypad. |
| `DOM_VK_NUMPAD1` | 0x61 (97) | "1" on the numeric keypad. |
| `DOM_VK_NUMPAD2` | 0x62 (98) | "2" on the numeric keypad. |
| `DOM_VK_NUMPAD3` | 0x63 (99) | "3" on the numeric keypad. |
| `DOM_VK_NUMPAD4` | 0x64 (100) | "4" on the numeric keypad. |
| `DOM_VK_NUMPAD5` | 0x65 (101) | "5" on the numeric keypad. |
| `DOM_VK_NUMPAD6` | 0x66 (102) | "6" on the numeric keypad. |
| `DOM_VK_NUMPAD7` | 0x67 (103) | "7" on the numeric keypad. |
| `DOM_VK_NUMPAD8` | 0x68 (104) | "8" on the numeric keypad. |
| `DOM_VK_NUMPAD9` | 0x69 (105) | "9" on the numeric keypad. |
| `DOM_VK_MULTIPLY` | 0x6A (106) | "\*" on the numeric keypad. |
| `DOM_VK_ADD` | 0x6B (107) | "+" on the numeric keypad. |
| `DOM_VK_SEPARATOR` | 0x6C (108) |  |
| `DOM_VK_SUBTRACT` | 0x6D (109) | "-" on the numeric keypad. |
| `DOM_VK_DECIMAL` | 0x6E (110) | Decimal point on the numeric keypad. |
| `DOM_VK_DIVIDE` | 0x6F (111) | "/" on the numeric keypad. |
| `DOM_VK_F1` | 0x70 (112) | F1 key. |
| `DOM_VK_F2` | 0x71 (113) | F2 key. |
| `DOM_VK_F3` | 0x72 (114) | F3 key. |
| `DOM_VK_F4` | 0x73 (115) | F4 key. |
| `DOM_VK_F5` | 0x74 (116) | F5 key. |
| `DOM_VK_F6` | 0x75 (117) | F6 key. |
| `DOM_VK_F7` | 0x76 (118) | F7 key. |
| `DOM_VK_F8` | 0x77 (119) | F8 key. |
| `DOM_VK_F9` | 0x78 (120) | F9 key. |
| `DOM_VK_F10` | 0x79 (121) | F10 key. |
| `DOM_VK_F11` | 0x7A (122) | F11 key. |
| `DOM_VK_F12` | 0x7B (123) | F12 key. |
| `DOM_VK_F13` | 0x7C (124) | F13 key. |
| `DOM_VK_F14` | 0x7D (125) | F14 key. |
| `DOM_VK_F15` | 0x7E (126) | F15 key. |
| `DOM_VK_F16` | 0x7F (127) | F16 key. |
| `DOM_VK_F17` | 0x80 (128) | F17 key. |
| `DOM_VK_F18` | 0x81 (129) | F18 key. |
| `DOM_VK_F19` | 0x82 (130) | F19 key. |
| `DOM_VK_F20` | 0x83 (131) | F20 key. |
| `DOM_VK_F21` | 0x84 (132) | F21 key. |
| `DOM_VK_F22` | 0x85 (133) | F22 key. |
| `DOM_VK_F23` | 0x86 (134) | F23 key. |
| `DOM_VK_F24` | 0x87 (135) | F24 key. |
| `DOM_VK_NUM_LOCK` | 0x90 (144) | Num Lock key. |
| `DOM_VK_SCROLL_LOCK` | 0x91 (145) | Scroll Lock key. |
| `DOM_VK_WIN_OEM_FJ_JISHO` | 0x92 (146) | An OEM specific key on Windows. This was used for "Dictionary" key on Fujitsu OASYS. |
| `DOM_VK_WIN_OEM_FJ_MASSHOU` | 0x93 (147) | An OEM specific key on Windows. This was used for "Unregister word" key on Fujitsu OASYS. |
| `DOM_VK_WIN_OEM_FJ_TOUROKU` | 0x94 (148) | An OEM specific key on Windows. This was used for "Register word" key on Fujitsu OASYS. |
| `DOM_VK_WIN_OEM_FJ_LOYA` | 0x95 (149) | An OEM specific key on Windows. This was used for "Left OYAYUBI" key on Fujitsu OASYS. |
| `DOM_VK_WIN_OEM_FJ_ROYA` | 0x96 (150) | An OEM specific key on Windows. This was used for "Right OYAYUBI" key on Fujitsu OASYS. |
| `DOM_VK_CIRCUMFLEX` | 0xA0 (160) | Circumflex ("^") key. |
| `DOM_VK_EXCLAMATION` | 0xA1 (161) | Exclamation ("!") key. |
| `DOM_VK_DOUBLE_QUOTE` | 0xA3 (162) | Double quote (""") key. |
| `DOM_VK_HASH` | 0xA3 (163) | Hash ("#") key. |
| `DOM_VK_DOLLAR` | 0xA4 (164) | Dollar sign ("$") key. |
| `DOM_VK_PERCENT` | 0xA5 (165) | Percent ("%") key. |
| `DOM_VK_AMPERSAND` | 0xA6 (166) | Ampersand ("&") key. |
| `DOM_VK_UNDERSCORE` | 0xA7 (167) | Underscore ("\_") key. |
| `DOM_VK_OPEN_PAREN` | 0xA8 (168) | Open parenthesis ("(") key. |
| `DOM_VK_CLOSE_PAREN` | 0xA9 (169) | Close parenthesis (")") key. |
| `DOM_VK_ASTERISK` | 0xAA (170) | Asterisk ("\*") key. |
| `DOM_VK_PLUS` | 0xAB (171) | Plus ("+") key. |
| `DOM_VK_PIPE` | 0xAC (172) | Pipe ("|") key. |
| `DOM_VK_HYPHEN_MINUS` | 0xAD (173) | Hyphen-US/docs/Minus ("-") key. |
| `DOM_VK_OPEN_CURLY_BRACKET` | 0xAE (174) | Open curly bracket ("{") key. |
| `DOM_VK_CLOSE_CURLY_BRACKET` | 0xAF (175) | Close curly bracket ("}") key. |
| `DOM_VK_TILDE` | 0xB0 (176) | Tilde ("~") key. |
| `DOM_VK_VOLUME_MUTE` | 0xB5 (181) | Audio mute key. |
| `DOM_VK_VOLUME_DOWN` | 0xB6 (182) | Audio volume down key |
| `DOM_VK_VOLUME_UP` | 0xB7 (183) | Audio volume up key |
| `DOM_VK_COMMA` | 0xBC (188) | Comma (",") key. |
| `DOM_VK_PERIOD` | 0xBE (190) | Period (".") key. |
| `DOM_VK_SLASH` | 0xBF (191) | Slash ("/") key. |
| `DOM_VK_BACK_QUOTE` | 0xC0 (192) | Back tick ("`") key. |
| `DOM_VK_OPEN_BRACKET` | 0xDB (219) | Open square bracket ("[") key. |
| `DOM_VK_BACK_SLASH` | 0xDC (220) | Back slash ("\") key. |
| `DOM_VK_CLOSE_BRACKET` | 0xDD (221) | Close square bracket ("]") key. |
| `DOM_VK_QUOTE` | 0xDE (222) | Quote (''') key. |
| `DOM_VK_META` | 0xE0 (224) | Meta key on Linux, Command key on Mac. |
| `DOM_VK_ALTGR` | 0xE1 (225) | AltGr key (Level 3 Shift key or Level 5 Shift key) on Linux. |
| `DOM_VK_WIN_ICO_HELP` | 0xE3 (227) | An OEM specific key on Windows. This is (was?) used for Olivetti ICO keyboard. |
| `DOM_VK_WIN_ICO_00` | 0xE4 (228) | An OEM specific key on Windows. This is (was?) used for Olivetti ICO keyboard. |
| `DOM_VK_WIN_ICO_CLEAR` | 0xE6 (230) | An OEM specific key on Windows. This is (was?) used for Olivetti ICO keyboard. |
| `DOM_VK_WIN_OEM_RESET` | 0xE9 (233) | An OEM specific key on Windows. This was used for Nokia/Ericsson's device. |
| `DOM_VK_WIN_OEM_JUMP` | 0xEA (234) | An OEM specific key on Windows. This was used for Nokia/Ericsson's device. |
| `DOM_VK_WIN_OEM_PA1` | 0xEB (235) | An OEM specific key on Windows. This was used for Nokia/Ericsson's device. |
| `DOM_VK_WIN_OEM_PA2` | 0xEC (236) | An OEM specific key on Windows. This was used for Nokia/Ericsson's device. |
| `DOM_VK_WIN_OEM_PA3` | 0xED (237) | An OEM specific key on Windows. This was used for Nokia/Ericsson's device. |
| `DOM_VK_WIN_OEM_WSCTRL` | 0xEE (238) | An OEM specific key on Windows. This was used for Nokia/Ericsson's device. |
| `DOM_VK_WIN_OEM_CUSEL` | 0xEF (239) | An OEM specific key on Windows. This was used for Nokia/Ericsson's device. |
| `DOM_VK_WIN_OEM_ATTN` | 0xF0 (240) | An OEM specific key on Windows. This was used for Nokia/Ericsson's device. |
| `DOM_VK_WIN_OEM_FINISH` | 0xF1 (241) | An OEM specific key on Windows. This was used for Nokia/Ericsson's device. |
| `DOM_VK_WIN_OEM_COPY` | 0xF2 (242) | An OEM specific key on Windows. This was used for Nokia/Ericsson's device. |
| `DOM_VK_WIN_OEM_AUTO` | 0xF3 (243) | An OEM specific key on Windows. This was used for Nokia/Ericsson's device. |
| `DOM_VK_WIN_OEM_ENLW` | 0xF4 (244) | An OEM specific key on Windows. This was used for Nokia/Ericsson's device. |
| `DOM_VK_WIN_OEM_BACKTAB` | 0xF5 (245) | An OEM specific key on Windows. This was used for Nokia/Ericsson's device. |
| `DOM_VK_ATTN` | 0xF6 (246) | Attn (Attention) key of IBM midrange computers, e.g., AS/400. |
| `DOM_VK_CRSEL` | 0xF7 (247) | CrSel (Cursor Selection) key of IBM 3270 keyboard layout. |
| `DOM_VK_EXSEL` | 0xF8 (248) | ExSel (Extend Selection) key of IBM 3270 keyboard layout. |
| `DOM_VK_EREOF` | 0xF9 (249) | Erase EOF key of IBM 3270 keyboard layout. |
| `DOM_VK_PLAY` | 0xFA (250) | Play key of IBM 3270 keyboard layout. |
| `DOM_VK_ZOOM` | 0xFB (251) | Zoom key. |
| `DOM_VK_PA1` | 0xFD (253) | PA1 key of IBM 3270 keyboard layout. |
| `DOM_VK_WIN_OEM_CLEAR` | 0xFE (254) | Clear key, but we're not sure the meaning difference from `DOM_VK_CLEAR`. |

### Windows 上的 OEM 特定密钥

在 Windows 上，虚拟密钥代码的某些值是为特定于 OEM 的密钥定义（保留）的。它们可用于非标准键盘上的特殊键。换句话说，一些值被两个或多个供应商（或硬件）用于不同的含义。

从 gecko 21（并且早于 15）开始，仅在 Windows 上的 keycode 属性上提供 OEM 特定的键值。因此，它们对于通常的 Web 应用程序不有用。它们仅对内部网应用程序或类似情况有用。

查看 MSDN 上的"Manufacturer-specific Virtual-Key Codes (Windows CE 5.0)"了解更多


CSSNumericValue：div() 方法 - Web API 接口参考

CSSNumericValue：div() 方法
========================

`CSSNumericValue` (en-US) 接口的 **`div()`** 方法将 `CSSNumericValue` 除以提供的值。

语法
--

```js
div(number)
```

### 参数

`number`

可以是一个数字或者一个 `CSSNumericValue` (en-US)。

### 返回值

一个 `CSSMathProduct` (en-US)。

### 异常

* `TypeError`
    + ：如果传递给方法的类型无效，则抛出错误。
示例
--

```js
let mathProduct = CSS.px("24").div(CSS.percent("4"));
// 输出 "calc(24px / 4%)"
mathProduct.toString();
```

规范
--

| Specification |
| --- |
| CSS Typed OM Level 1 # dom-cssnumericvalue-div |

浏览器兼容性
------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.


SpeechRecognitionResult.isFinal - Web API 接口参考

SpeechRecognitionResult.isFinal
===============================

**实验性:** **这是一项实验性技术**
在将其用于生产之前，请仔细检查浏览器兼容性表格。

`SpeechRecognitionResult` (en-US) 接口的 **`isFinal`** 只读属性是一个布尔值，如果值是`true`, 则表示这是最后一次返回的结果 (语音识别结束)。如果为`false`, 表示识别尚未结束，这只是一个临时的数据，有可能会在稍后更新。

语法
--
```

var myIsFinal = speechRecognitionResultInstance.isFinal;
```

### 返回值

`Boolean` (en-US)示例
--

```js
recognition.onresult = function (event) {
  // The SpeechRecognitionEvent results property returns a SpeechRecognitionResultList object
  // SpeechRecognitionResultList 对象包含 SpeechRecognitionResult 对象。
  // 它有一个 getter，所以它可以像数组一样被访问
  // 第一个 [0] 返回 SpeechRecognitionResult 的第 0 个下标。
  // Each SpeechRecognitionResult object contains SpeechRecognitionAlternative objects that contain individual results.
  // 这些也有 getter，因此可以像数组一样访问它们。
  // 第二个 [0] 返回 SpeechRecognitionAlternative 所在的第 0 个下标。
  // 然后我们返回的记录属性 SpeechRecognitionAlternative 对象
  var color = event.results[0][0].transcript;
  diagnostic.textContent = "Result received: " + color + ".";
  bg.style.backgroundColor = color;

  console.log(event.results[0].isFinal);
};
```

规范
--

| Specification |
| --- |
| Web Speech API # dom-speechrecognitionresult-isfinal |

浏览器兼容性
------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.

相关链接
----

* Web Speech API


使用 Fetch - Web API 接口参考

使用 Fetch
========

Fetch API 提供了一个 JavaScript 接口，用于访问和操纵 HTTP 管道的一些具体部分，例如请求和响应。它还提供了一个全局 `fetch()` 方法，该方法提供了一种简单，合理的方式来跨网络异步获取资源。

这种功能以前是使用 `XMLHttpRequest` 实现的。Fetch 提供了一个更理想的替代方案，可以很容易地被其他技术使用，例如 `Service Workers`。Fetch 还提供了专门的逻辑空间来定义其他与 HTTP 相关的概念，例如 CORS 和 HTTP 的扩展。

请注意，`fetch` 规范与 `jQuery.ajax()` 主要有以下的不同：

* 当接收到一个代表错误的 HTTP 状态码时，从 `fetch()` 返回的 Promise **不会被标记为 reject**，即使响应的 HTTP 状态码是 404 或 500。相反，它会将 Promise 状态标记为 resolve（如果响应的 HTTP 状态码不在 200 - 299 的范围内，则设置 resolve 返回值的 `ok` 属性为 false），仅当网络故障时或请求被阻止时，才会标记为 reject。
* `fetch` **不会发送跨域 cookie**，除非你使用了 *credentials* 的初始化选项。（自 2018 年 8 月以后，默认的 credentials 政策变更为 `same-origin`。Firefox 也在 61.0b13 版本中进行了修改）

一个基本的 fetch 请求设置起来很简单。看看下面的代码：

```js
fetch("http://example.com/movies.json")
  .then((response) => response.json())
  .then((data) => console.log(data));
```

这里我们通过网络获取一个 JSON 文件并将其打印到控制台。最简单的用法是只提供一个参数用来指明想 `fetch()` 到的资源路径，然后返回一个包含响应结果的 promise（一个 `Response` 对象）。

当然它只是一个 HTTP 响应，而不是真的 JSON。为了获取 JSON 的内容，我们需要使用 `json()` 方法（该方法返回一个将响应 body 解析成 JSON 的 promise）。

**备注：** Body 还有其他相似的方法，用于获取其他类型的内容。

最好使用符合内容安全策略 (CSP)的链接而不是使用直接指向资源地址的方式来进行 fetch 的请求。

### 支持的请求参数

`fetch()` 接受第二个可选参数，一个可以控制不同配置的 `init` 对象：

参考 `fetch()`，查看所有可选的配置和更多描述。

```js
// Example POST method implementation:
async function postData(url = "", data = {}) {
  // Default options are marked with \*
  const response = await fetch(url, {
    method: "POST", // \*GET, POST, PUT, DELETE, etc.
    mode: "cors", // no-cors, \*cors, same-origin
    cache: "no-cache", // \*default, no-cache, reload, force-cache, only-if-cached
    credentials: "same-origin", // include, \*same-origin, omit
    headers: {
      "Content-Type": "application/json",
      // 'Content-Type': 'application/x-www-form-urlencoded',
    },
    redirect: "follow", // manual, \*follow, error
    referrerPolicy: "no-referrer", // no-referrer, \*no-referrer-when-downgrade, origin, origin-when-cross-origin, same-origin, strict-origin, strict-origin-when-cross-origin, unsafe-url
    body: JSON.stringify(data), // body data type must match "Content-Type" header
  });
  return response.json(); // parses JSON response into native JavaScript objects
}

postData("https://example.com/answer", { answer: 42 }).then((data) => {
  console.log(data); // JSON data parsed by `data.json()` call
});
```

注意：`mode: "no-cors"` 仅允许使用一组有限的 HTTP 请求头：

* `Accept`
* `Accept-Language`
* `Content-Language`
* `Content-Type` 允许使用的值为：`application/x-www-form-urlencoded`、`multipart/form-data` 或 `text/plain`
### 发送带凭据的请求

为了让浏览器发送包含凭据的请求（即使是跨域源），要将 `credentials: 'include'` 添加到传递给 `fetch()` 方法的 `init` 对象。

```js
fetch("https://example.com", {
  credentials: "include",
});
```

**备注：** 当请求使用 `credentials: 'include'` 时，响应的 `Access-Control-Allow-Origin` 不能使用通配符 "`*`"。在这种情况下，`Access-Control-Allow-Origin` 必须是当前请求的源，在使用 CORS Unblock 插件的情况下请求仍会失败。

**备注：** 无论怎么设置，浏览器都不应在 *预检请求* 中发送凭据。了解更多：跨域资源共享 > 附带身份凭证的请求

如果你只想在请求 URL 与调用脚本位于同一起源处时发送凭据，请添加 `credentials: 'same-origin'`。

```js
// The calling script is on the origin 'https://example.com'

fetch("https://example.com", {
  credentials: "same-origin",
});
```

要改为确保浏览器不在请求中包含凭据，请使用 `credentials: 'omit'`。

```js
fetch("https://example.com", {
  credentials: "omit",
});
```

### 上传 JSON 数据

使用 `fetch()` POST JSON 数据

```js
const data = { username: "example" };

fetch("https://example.com/profile", {
  method: "POST", // or 'PUT'
  headers: {
    "Content-Type": "application/json",
  },
  body: JSON.stringify(data),
})
  .then((response) => response.json())
  .then((data) => {
    console.log("Success:", data);
  })
  .catch((error) => {
    console.error("Error:", error);
  });
```

### 上传文件

可以通过 HTML `<input type="file" />` 元素，`FormData()` 和 `fetch()` 上传文件。

```js
const formData = new FormData();
const fileField = document.querySelector('input[type="file"]');

formData.append("username", "abc123");
formData.append("avatar", fileField.files[0]);

fetch("https://example.com/profile/avatar", {
  method: "PUT",
  body: formData,
})
  .then((response) => response.json())
  .then((result) => {
    console.log("Success:", result);
  })
  .catch((error) => {
    console.error("Error:", error);
  });
```

### 上传多个文件

可以通过 HTML `<input type="file" multiple />` 元素，`FormData()` 和 `fetch()` 上传文件。

```js
const formData = new FormData();
const photos = document.querySelector('input[type="file"][multiple]');

formData.append("title", "My Vegas Vacation");
for (let i = 0; i < photos.files.length; i++) {
  formData.append(`photos\_${i}`, photos.files[i]);
}

fetch("https://example.com/posts", {
  method: "POST",
  body: formData,
})
  .then((response) => response.json())
  .then((result) => {
    console.log("Success:", result);
  })
  .catch((error) => {
    console.error("Error:", error);
  });
```

### 逐行处理文本文件

从响应中读取的分块不是按行分割的，并且是 `Uint8Array` 数组类型（不是字符串类型）。如果你想通过 `fetch()` 获取一个文本文件并逐行处理它，那需要自行处理这些复杂情况。以下示例展示了一种创建行迭代器来处理的方法（简单起见，假设文本是 UTF-8 编码的，且不处理 `fetch()` 的错误）。

```js
async function\* makeTextFileLineIterator(fileURL) {
  const utf8Decoder = new TextDecoder("utf-8");
  const response = await fetch(fileURL);
  const reader = response.body.getReader();
  let { value: chunk, done: readerDone } = await reader.read();
  chunk = chunk ? utf8Decoder.decode(chunk) : "";

  const re = /\n|\r|\r\n/gm;
  let startIndex = 0;
  let result;

  for (;;) {
    let result = re.exec(chunk);
    if (!result) {
      if (readerDone) {
        break;
      }
      let remainder = chunk.substr(startIndex);
      ({ value: chunk, done: readerDone } = await reader.read());
      chunk = remainder + (chunk ? utf8Decoder.decode(chunk) : "");
      startIndex = re.lastIndex = 0;
      continue;
    }
    yield chunk.substring(startIndex, result.index);
    startIndex = re.lastIndex;
  }
  if (startIndex < chunk.length) {
    // last line didn't end in a newline char
    yield chunk.substr(startIndex);
  }
}

async function run() {
  for await (let line of makeTextFileLineIterator(urlOfFile)) {
    processLine(line);
  }
}

run();
```

### 检测请求是否成功

如果遇到网络故障或服务端的 CORS 配置错误时，`fetch()` promise 将会 reject，带上一个 `TypeError` 对象。虽然这个情况经常是遇到了权限问题或类似问题——比如 404 不是一个网络故障。想要精确的判断 `fetch()` 是否成功，需要包含 promise resolved 的情况，此时再判断 `Response.ok` 是否为 true。类似以下代码：

```js
fetch("flowers.jpg")
  .then((response) => {
    if (!response.ok) {
      throw new Error("Network response was not OK");
    }
    return response.blob();
  })
  .then((myBlob) => {
    myImage.src = URL.createObjectURL(myBlob);
  })
  .catch((error) => {
    console.error("There has been a problem with your fetch operation:", error);
  });
```

### 自定义请求对象

除了传给 `fetch()` 一个资源的地址，你还可以通过使用 `Request()` 构造函数来创建一个 request 对象，然后再作为参数传给 `fetch()`：

```js
const myHeaders = new Headers();

const myRequest = new Request("flowers.jpg", {
  method: "GET",
  headers: myHeaders,
  mode: "cors",
  cache: "default",
});

fetch(myRequest)
  .then((response) => response.blob())
  .then((myBlob) => {
    myImage.src = URL.createObjectURL(myBlob);
  });
```

`Request()` 和 `fetch()` 接受同样的参数。你甚至可以传入一个已存在的 request 对象来创造一个拷贝：

```js
const anotherRequest = new Request(myRequest, myInit);
```

这个很有用，因为 request 和 response bodies 只能被使用一次（译者注：这里的意思是因为设计成了 stream 的方式，所以它们只能被读取一次）。创建一个拷贝就可以再次使用 request/response 了，当然也可以使用不同的 `init` 参数。创建拷贝必须在读取 body 之前进行，而且读取拷贝的 body 也会将原始请求的 body 标记为已读。

**备注：** `clone()` 方法也可以用于创建一个拷贝。它和上述方法一样，如果 request 或 response 的 body 已经被读取过，那么将执行失败。区别在于， `clone()` 出的 body 被读取不会导致原 body 被标记为已读取。

Headers
-------

使用 `Headers` 的接口，你可以通过 `Headers()` 构造函数来创建一个你自己的 headers 对象。一个 headers 对象是一个简单的多键值对：

```js
const content = "Hello World";
const myHeaders = new Headers();
myHeaders.append("Content-Type", "text/plain");
myHeaders.append("Content-Length", content.length.toString());
myHeaders.append("X-Custom-Header", "ProcessThisImmediately");
```

也可以传入一个多维数组或者对象字面量：

```js
const myHeaders = new Headers({
  "Content-Type": "text/plain",
  "Content-Length": content.length.toString(),
  "X-Custom-Header": "ProcessThisImmediately",
});
```

它的内容可以被获取：

```js
console.log(myHeaders.has("Content-Type")); // true
console.log(myHeaders.has("Set-Cookie")); // false
myHeaders.set("Content-Type", "text/html");
myHeaders.append("X-Custom-Header", "AnotherValue");

console.log(myHeaders.get("Content-Length")); // 11
console.log(myHeaders.get("X-Custom-Header")); // ['ProcessThisImmediately', 'AnotherValue']

myHeaders.delete("X-Custom-Header");
console.log(myHeaders.get("X-Custom-Header")); // null
```

虽然一些操作只能在 `ServiceWorkers` 中使用，但是它提供了更方便的操作 Headers 的 API。

如果使用了一个不合法的 HTTP Header 属性名，那么 Headers 的方法通常都抛出 TypeError 异常。如果不小心写入了一个不可写的属性（见下方），也会抛出一个 TypeError 异常。除此以外的情况，失败了并不抛出异常。例如：

```js
const myResponse = Response.error();
try {
  myResponse.headers.set("Origin", "http://mybank.com");
} catch (e) {
  console.log("Cannot pretend to be a bank!");
}
```

最好在在使用之前检查内容类型 `content-type` 是否正确，比如：

```js
fetch(myRequest)
  .then((response) => {
    const contentType = response.headers.get("content-type");
    if (!contentType || !contentType.includes("application/json")) {
      throw new TypeError("Oops, we haven't got JSON!");
    }
    return response.json();
  })
  .then((data) => {
    /\* process your data further \*/
  })
  .catch((error) => console.error(error));
```

### Guard

由于 Headers 可以在 request 中被发送或者在 response 中被接收，并且规定了哪些参数是可写的，Headers 对象有一个特殊的 guard 属性。这个属性没有暴露给 Web，但是它影响到哪些内容可以在 Headers 对象中被操作。

可能的值如下：

* `none`：默认的。
* `request`：从 request 中获得的 headers（`Request.headers`）只读。
* `request-no-cors`：从不同域（`Request.mode` `no-cors`）的 request 中获得的 headers 只读。
* `response`：从 response 中获得的 headers（`Response.headers`）只读。
* `immutable`：在 ServiceWorkers 中最常用的，所有的 headers 都只读。

**备注：** 你不可以添加或者修改一个 guard 属性是 `request` 的 Request Header 的 `Content-Length` 属性。同样地，插入 `Set-Cookie` 属性到一个 response header 是不允许的，因此，Service Worker 中，不能给合成的 Response 设置 cookie。

Response 对象
-----------

如上所述，`Response` 实例是在 `fetch()` 处理完 promise 之后返回的。

你会用到的最常见的 response 属性有：

* `Response.status` — 整数（默认值为 200）为 response 的状态码。
* `Response.statusText` (en-US) — 字符串（默认值为 ""），该值与 HTTP 状态码消息对应。注意：HTTP/2 不支持状态消息
* `Response.ok` — 如上所示，该属性是来检查 response 的状态是否在 200 - 299（包括 200 和 299）这个范围内。该属性返回一个布尔值。

它的实例也可用通过 JavaScript 来创建，但只有在 `ServiceWorkers` 中使用 `respondWith()` 方法并提供了一个自定义的 response 来接受 request 时才真正有用：

```js
const myBody = new Blob();

addEventListener("fetch", (event) => {
  // ServiceWorker intercepting a fetch
  event.respondWith(
    new Response(myBody, {
      headers: { "Content-Type": "text/plain" },
    }),
  );
});
```

`Response()` 构造方法接受两个可选参数——response 的 body 和一个初始化对象（与`Request()` 所接受的 init 参数类似）。

**备注：** 静态方法 `error()` 只是返回了错误的 response。与此类似地，`redirect()` 只是返回了一个可以重定向至某 URL 的 response。这些也只与 Service Worker 有关。

Body
----

不管是请求还是响应都能够包含 body 对象。body 也可以是以下任意类型的实例。

* `ArrayBuffer`
* `ArrayBufferView` (Uint8Array 等)
* `Blob`/File
* string
* `URLSearchParams`
* `FormData`

Body 类定义了以下方法（这些方法都被 `Request` 和 `Response`所实现）以获取 body 内容。这些方法都会返回一个被解析后的 Promise 对象和数据。

* `Request.arrayBuffer()` / `Response.arrayBuffer()`
* `Request.blob()` / `Response.blob()`
* `Request.formData()` / `Response.formData()`
* `Request.json()` / `Response.json()`
* `Request.text()` / `Response.text()`

相比于 XHR，这些方法让非文本化数据的使用更加简单。

请求体可以由传入 body 参数来进行设置：

```js
const form = new FormData(document.getElementById("login-form"));
fetch("/login", {
  method: "POST",
  body: form,
});
```

request 和 response（包括 `fetch()` 方法）都会试着自动设置 `Content-Type`。如果没有设置 `Content-Type` 值，发送的请求也会自动设值。

特性检测
----

Fetch API 的支持情况，可以通过检测 `Headers`, `Request`, `Response` 或 `fetch()` 是否在 `Window` 或 `Worker` 域中来判断。例如：

```js
if (window.fetch) {
  // run my fetch request here
} else {
  // do something with XMLHttpRequest?
}
```

参见
--

* `ServiceWorker API`
* HTTP access control (CORS)
* HTTP
* Fetch polyfill
* Fetch examples on GitHub


Fetch 基本概念 - Web API 接口参考

Fetch 基本概念
==========

Fetch 是一个现代的概念，等同于 XMLHttpRequest。它提供了许多与 XMLHttpRequest 相同的功能，但被设计成更具可扩展性和高效性。本文介绍了 Fetch API 的一些基本概念。

**备注：** 这篇文章可能还需要修改。如果你觉得有的概念可以解释的更好，让人们在MDN 论坛上知道，或 Mozilla IRC (#mdn room)。

简而言之
----

Fetch 的核心在于对 HTTP 接口的抽象，包括 `Request`，`Response`，`Headers`，`Body`，以及用于初始化异步请求的 `global fetch`。得益于 JavaScript 实现的这些抽象好的 HTTP 模块，其他接口能够很方便的使用这些功能。

Service Workers (en-US) 是大量使用 Fetch 的 API 的一个示例。

除此之外，Fetch 还利用到了请求的异步特性——它是基于 `Promise` 的。

Guard
-----

Guard 是 `Headers` 对象的特性，基于不同的情况，它可以有以下取值：`immutable`、`request`、`request-no-cors`、`response` 或 `none`。

当使用 `Headers()` constructor 创建一个新的 `Headers` 对象的时候，它的 guard 被设置成 `none（默认值）。当创建``Request` 或 `Response` 对象的时候，它将拥有一个按照以下规则实现的与之关联的 `Headers` 对象：

| 新对象的类型 | 创建时的构造函数 | 关联的 `Headers` 对象的 guard |
| --- | --- | --- |
| `Request` | `Request()` | `request` |
| `Request()`，`mode`
 设置成 `no-cors` | `request-no-cors` |
| `Response` | `Response()` | `response` |
| `error()` 或
 `redirect()` 方法
  | `immutable` |

头信息的 guard 会影响 `set()`、`delete()` 和 `append()` 方法。如果你试图修改 `guard` 是 `immutable` 的`Headers` 对象，那么会抛出一个 `TypeError`。以下情况则不会抛出错误：

* guard 是 `request` 并且头信息中的 *name* 不是 forbidden header name
* guard 是 `request-no-cors` 并且头信息中的 *name*/*value* 是 simple header
* guard 是 `response` 并且头信息中的 *name* 不是 forbidden response header name


跨全局的 Fetch 使用方法 - Web API 接口参考

跨全局的 Fetch 使用方法
===============

本文解释了在 fetch 时发生的边缘情况（以及潜在展示相同类型的资源检索行为的其他 API）。当从 `<iframe>` 发起涉及相对 URL 的跨源 Fetch 时，相对 URL 会被解析为当前的全局位置，而不是 iframe 的位置。

边缘情况
----

大多数网站几乎不会遇到这种边缘情况。如下：

* 需要一个同源的 iframe
* 该同源 iframe 需要具有不同根 URL 位置
* 必须使用跨全局的 fetch 函数，例如 `frame.contentWindow.fetch()`
* 传递给 fetch 函数的是相对 URL
遇到的问题
-----

以前，我们会根据当前的全局对象来解析相对的 URL，比如说：

```js
let absolute = new URL(relative, window.location.href);
```

这样做不是什么大问题，只是表现出这种行为的不同 API 与规范中定义的行为的不一致可能导致问题的进一步发展。

解决方案
----

在 Firefox 60 及以后版本中，Mozilla 对相对 URL 的解析是相对于拥有 `fetch()` 函数的全局的（见 Firefox bug 1432272）。因此在上述情形中，URL 是相对于 iframe 的地址进行解析的：

```js
let absolute = new URL(relative, frame.contentWindow.location.href);
```

对于如何使得新规范与此行为变化保持一致，以缓解未来可能出现的问题，有很多讨论正在进行中。


使用自定义元素 - Web API 接口参考

使用自定义元素
=======

Web 组件的一个关键特性是创建*自定义元素*：即由 Web 开发人员定义行为的 HTML 元素，扩展了浏览器中可用的元素集。

这篇文章介绍了自定义元素，并通过一些示例进行了详细说明。

自定义元素的类型
--------

有两种类型的自定义元素：

* **自定义内置元素**（Customized built-in element）继承自标准的 HTML 元素，例如 `HTMLImageElement` 或 `HTMLParagraphElement`。它们的实现定义了标准元素的行为。
* **独立自定义元素**（Autonomous custom element）继承自 HTML 元素基类 `HTMLElement`。你必须从头开始实现它们的行为。
实现自定义元素
-------

自定义元素作为一个类来实现，该类可以扩展 `HTMLElement`（在独立元素的情况下）或者你想要定制的接口（在自定义内置元素的情况下）。

以下是一个最小自定义元素的实现示例，该元素定制了 `<p>` 元素：

```js
class WordCount extends HTMLParagraphElement {
  constructor() {
    super();
  }
  // 此处编写元素功能
}
```

以下是一个独立自定义元素的最小实现：

```js
class PopupInfo extends HTMLElement {
  constructor() {
    super();
  }
  // 此处编写元素功能
}
```

在类的构造函数中，你可以设置初始状态和默认值，注册事件监听器，甚至创建一个影子根（shadow root）。在此处，你不应检查元素的属性或子元素，也不应添加新的属性或子元素。有关完整的要求集，请参阅自定义元素构造函数和交互行为的要求。

### 自定义元素生命周期回调

一旦你的自定义元素被注册，当页面中的代码以特定方式与你的自定义元素交互时，浏览器将调用你的类的某些方法。通过提供这些方法的实现，规范称之为*生命周期回调*，你可以运行代码来响应这些事件。

自定义元素生命周期回调包括：

* `connectedCallback()`：每当元素添加到文档中时调用。规范建议开发人员尽可能在此回调中实现自定义元素的设定，而不是在构造函数中实现。
* `disconnectedCallback()`：每当元素从文档中移除时调用。
* `adoptedCallback()`：每当元素被移动到新文档中时调用。
* `attributeChangedCallback()`：在属性更改、添加、移除或替换时调用。有关此回调的更多详细信息，请参见响应属性变化。

以下是一个记录这些生命周期事件的最小自定义元素示例：

```js
// 为这个元素创建类
class MyCustomElement extends HTMLElement {
  static observedAttributes = ["color", "size"];

  constructor() {
    // 必须首先调用 super 方法
    super();
  }

  connectedCallback() {
    console.log("自定义元素添加至页面。");
  }

  disconnectedCallback() {
    console.log("自定义元素从页面中移除。");
  }

  adoptedCallback() {
    console.log("自定义元素移动至新页面。");
  }

  attributeChangedCallback(name, oldValue, newValue) {
    console.log(`属性 ${name} 已变更。`);
  }
}

customElements.define("my-custom-element", MyCustomElement);
```

注册自定义元素
-------

要使自定义元素在页面中可用，请调用 `Window.customElements` 的 `define()` 方法。

`define()` 方法接受以下参数：

`name`

元素的名称。必须以小写字母开头，包含一个连字符，并符合规范中有效名称的定义中列出的一些其他规则。

`constructor`

自定义元素的构造函数。

`options`

仅对于自定义内置元素，这是一个包含单个属性 `extends` 的对象，该属性是一个字符串，命名了要扩展的内置元素。

例如，以下代码注册了名为 `WordCount` 的自定义内置元素：

```js
customElements.define("word-count", WordCount, { extends: "p" });
```

以下代码注册了名为 `PopupInfo` 的独立自定义元素：

```js
customElements.define("popup-info", PopupInfo);
```

使用自定义元素
-------

一旦你定义并注册了自定义元素，就可以在代码中使用它。

要使用自定义内置元素，请使用内置元素，但将自定义名称作为 `is` 属性的值：

```html
<p is="word-count"></p>
```

要使用独立自定义元素，就像使用内置的 HTML 元素一样，使用自定义名称即可：

```html
<popup-info>
  <!-- 元素的内容 -->
</popup-info>
```

响应属性变化
------

与内置元素一样，自定义元素可以使用 HTML 属性来配置元素的行为。为了有效地使用属性，元素必须能够响应属性值的变化。为此，自定义元素需要将以下成员添加到实现自定义元素的类中：

* 一个名为 `observedAttributes` 的静态属性。这必须是一个包含元素需要变更通知的所有属性名称的数组。
* `attributeChangedCallback()` 生命周期回调的实现。

`attributeChangedCallback()` 回调在列在元素的 `observedAttributes` 属性中的属性被添加、修改、移除或替换时调用。

回调接受三个参数：

* 发生变化的属性的名称。
* 属性的旧值。
* 属性的新值。

例如，下面这个独立自定义元素将观察一个 `size` 属性，并在它们发生变化时记录旧值和新值：

```js
// 为这个元素创建类
class MyCustomElement extends HTMLElement {
  static observedAttributes = ["size"];

  constructor() {
    super();
  }

  attributeChangedCallback(name, oldValue, newValue) {
    console.log(`属性 ${name} 已由 ${oldValue} 变更为 ${newValue}。`);
  }
}

customElements.define("my-custom-element", MyCustomElement);
```

请注意，如果元素的 `HTML` 声明包含一个被观察的属性，那么在属性被初始化后，`attributeChangedCallback()` 将在元素的声明首次解析时被调用。因此，在以下示例中，即使属性再也没有被更改，当 DOM 被解析时，`attributeChangedCallback()` 也会被调用：

```html
<my-custom-element size="100"></my-custom-element>
```

有关使用 `attributeChangedCallback()` 的完整示例，请参阅本页面中的生命周期回调。

示例
--

在本指南的其余部分，我们将看一些示例自定义元素。你可以在 web-components-examples 仓库中找到所有这些示例的源代码，以及更多示例，并且你可以在 https://mdn.github.io/web-components-examples/ 上实时查看它们。

### 一个独立自定义元素

首先，我们来看一个独立自定义元素。`<popup-info>` 自定义元素接受图像图标和文本字符串作为属性，并将图标嵌入到页面中。当焦点在图标上时，它会在弹出的信息框中显示文本，以提供更多上下文信息。

* 查看在线示例
* 查看源代码

首先，JavaScript 文件定义了一个名为 `PopupInfo` 的类，该类扩展了 `HTMLElement` 类。

```js
// 为当这个元素创建一个类
class PopupInfo extends HTMLElement {
  constructor() {
    // 必须首先调用 super 方法
    super();
  }

  connectedCallback() {
    // 创建影子根
    const shadow = this.attachShadow({ mode: "open" });

    // 创建几个 span
    const wrapper = document.createElement("span");
    wrapper.setAttribute("class", "wrapper");

    const icon = document.createElement("span");
    icon.setAttribute("class", "icon");
    icon.setAttribute("tabindex", 0);

    const info = document.createElement("span");
    info.setAttribute("class", "info");

    // 获取属性内容然后将其放入 info 这个 span 内
    const text = this.getAttribute("data-text");
    info.textContent = text;

    // 插入图标
    let imgUrl;
    if (this.hasAttribute("img")) {
      imgUrl = this.getAttribute("img");
    } else {
      imgUrl = "img/default.png";
    }

    const img = document.createElement("img");
    img.src = imgUrl;
    icon.appendChild(img);

    // 创建一些 CSS 应用于影子 DOM
    const style = document.createElement("style");
    console.log(style.isConnected);

    style.textContent = `
 .wrapper {
 position: relative;
 }

 .info {
 font-size: 0.8rem;
 width: 200px;
 display: inline-block;
 border: 1px solid black;
 padding: 10px;
 background: white;
 border-radius: 10px;
 opacity: 0;
 transition: 0.6s all;
 position: absolute;
 bottom: 20px;
 left: 10px;
 z-index: 3;
 }

 img {
 width: 1.2rem;
 }

 .icon:hover + .info, .icon:focus + .info {
 opacity: 1;
 }
 `;

    // 将创建好的元素附加到影子 DOM 上
    shadow.appendChild(style);
    console.log(style.isConnected);
    shadow.appendChild(wrapper);
    wrapper.appendChild(icon);
    wrapper.appendChild(info);
  }
}
```

类定义包含类的 `constructor()` 方法，该方法始终以调用 `super()` 开始，以便正确建立原型链。

在 `connectedCallback()` 方法内部，我们定义了元素连接到 DOM 时元素将具有的所有功能。在这种情况下，我们将一个影子根附加到自定义元素，使用一些 DOM 操作来创建元素的影子 DOM 结构——然后将其附加到影子根——最后将一些 CSS 附加到影子根以进行样式设置。我们不在构造函数中执行这项工作，因为在连接到 DOM 之前，元素的属性是不可用的。

最后，我们使用前面提到的 `define()` 方法在 `CustomElementRegistry` 中注册我们的自定义元素——在参数中，我们指定元素名称，然后定义其功能的类名称：

```js
customElements.define("popup-info", PopupInfo);
```

现在，它已经可以在我们的页面上使用了。在我们的 HTML 中，我们可以像这样使用它：

```html
<popup-info
 img="img/alt.png"
 data-text="Your card validation code (CVC)
 is an extra security feature — it is the last 3 or 4 numbers on the
 back of your card."></popup-info>
```

### 引用外部样式

在上面的示例中，我们使用 `<style>` 元素为影子 DOM 应用样式，但你也可以从 `<link>` 元素引用外部样式表。在此示例中，我们将修改 `<popup-info>` 自定义元素以使用外部样式表。

* 查看在线示例
* 查看源代码

下面是类的定义：

```js
// 为这个元素创建类
class PopupInfo extends HTMLElement {
  constructor() {
    // 必须首先调用 super 方法
    super();
  }

  connectedCallback() {
    // 创建影子根
    const shadow = this.attachShadow({ mode: "open" });

    // 创建几个 span
    const wrapper = document.createElement("span");
    wrapper.setAttribute("class", "wrapper");

    const icon = document.createElement("span");
    icon.setAttribute("class", "icon");
    icon.setAttribute("tabindex", 0);

    const info = document.createElement("span");
    info.setAttribute("class", "info");

    // 获取属性内容然后将其放入 info 这个 span 内
    const text = this.getAttribute("data-text");
    info.textContent = text;

    // 插入图标
    let imgUrl;
    if (this.hasAttribute("img")) {
      imgUrl = this.getAttribute("img");
    } else {
      imgUrl = "img/default.png";
    }

    const img = document.createElement("img");
    img.src = imgUrl;
    icon.appendChild(img);

    // 将外部样式添加至影子 DOM
    const linkElem = document.createElement("link");
    linkElem.setAttribute("rel", "stylesheet");
    linkElem.setAttribute("href", "style.css");

    // 将创建好的元素附加到影子 DOM 上
    shadow.appendChild(linkElem);
    shadow.appendChild(wrapper);
    wrapper.appendChild(icon);
    wrapper.appendChild(info);
  }
}
```

这与原始的 `<popup-info>` 示例相同，只是我们使用 `<link>` 元素链接到外部样式表，然后将其添加到影子 DOM 中。

请注意，`<link>` 元素不会阻止影子根的绘制，因此在样式表加载时可能会出现未经样式化的内容（FOUC）。

许多现代浏览器对于从公共节点克隆的 `<style>` 标签或具有相同文本的标签都实现了一种优化，使它们可以共享单个后备样式表。通过这种优化，外部和内部样式的性能应该是相似的。

### 自定义内置元素

现在让我们看一个自定义内置元素的例子。这个例子扩展了内置的 `<ul>` 元素，以支持展开和折叠列表项。

* 查看在线示例
* 查看源代码

首先，我们定义了这个元素的类：

```js
// 为这个元素创建类
class ExpandingList extends HTMLUListElement {
  constructor() {
    // 必须首先调用 super 方法
    // super() 的返回值是对当前元素的引用
    self = super();
  }

  connectedCallback() {
    // 获取当前自定义 ul 元素的 ul 和 li 子元素
    // 包含 ul 的 li 元素可以成为容器
    const uls = Array.from(self.querySelectorAll("ul"));
    const lis = Array.from(self.querySelectorAll("li"));
    // 隐藏所有子 ul
    // 当用户点击更高级别的容器时，这些列表就会显示出来
    uls.forEach((ul) => {
      ul.style.display = "none";
    });

    // 仔细观察每个在 ul 中的 li 元素
    lis.forEach((li) => {
      // 如果这个 li 有一个 ul 作为子元素，则对其进行装饰并添加一个点击处理程序
      if (li.querySelectorAll("ul").length > 0) {
        // 添加一个属性，以便通过样式使用
        // 来显示打开或关闭的图标
        li.setAttribute("class", "closed");
        // 将 li 元素的文本包裹在一个新的 span 元素中
        // 这样我们就可以将样式和事件处理程序分配给 span
        const childText = li.childNodes[0];
        const newSpan = document.createElement("span");

        // 从 li 复制文本到 span，设置光标样式
        newSpan.textContent = childText.textContent;
        newSpan.style.cursor = "pointer";

        // 为这个 span 添加事件处理程序
        newSpan.addEventListener("click", (e) => {
          // span 的下一个兄弟元素应该是 ul
          const nextul = e.target.nextElementSibling;

          // 切换可见状态并更新 ul 的 class 属性
          if (nextul.style.display == "block") {
            nextul.style.display = "none";
            nextul.parentNode.setAttribute("class", "closed");
          } else {
            nextul.style.display = "block";
            nextul.parentNode.setAttribute("class", "open");
          }
        });
        // 添加 span 并从 li 中移除纯文本节点
        childText.parentNode.insertBefore(newSpan, childText);
        childText.parentNode.removeChild(childText);
      }
    });
  }
}
```

请注意，这次我们扩展的是 `HTMLUListElement` (en-US)，而不是 `HTMLElement`。这意味着我们获得了列表的默认行为，只需实现自己的定制。

与之前一样，大部分代码都在 `connectedCallback()` 生命周期回调中。

接下来，我们使用与之前相同的 `define()` 方法注册元素，只是这次它还包括一个选项对象，详细说明了我们的自定义元素从哪个元素继承：

```js
customElements.define("expanding-list", ExpandingList, { extends: "ul" });
```

在 Web 文档中使用内置元素看起来也有些不同：

```html
<ul is="expanding-list">
  …
</ul>
```

你使用 `<ul>` 元素与往常一样，但在 `is` 属性内指定自定义元素的名称。

请注意，在这种情况下，我们必须确保定义自定义元素的脚本在 DOM 完全解析后执行，因为 `connectedCallback()` 在展开的列表被添加到 DOM 时就会被调用，而此时其子元素尚未添加，因此 `querySelectorAll()` 调用将找不到任何项。确保这一点的一种方法是在包含脚本的行上添加 defer 属性：

```html
<script src="main.js" defer></script>
```

### 生命周期回调

到目前为止，我们只看到了一个生命周期回调的实际应用：`connectedCallback()`。在最后一个示例——`<custom-square>`——中，我们将看到一些其他的回调。`<custom-square>` 独立自定义元素绘制一个正方形，其大小和颜色由两个名为 `"size"` 和 `"color"` 的属性确定。

* 查看在线示例
* 查看源代码

在类的构造函数中，我们将影子 DOM 附加到元素上，然后再附加空的 `<div>` 和 `<style>` 元素到影子根上：

```js
constructor() {
  // 必须首先调用 super 方法
  super();

  const shadow = this.attachShadow({ mode: "open" });

  const div = document.createElement("div");
  const style = document.createElement("style");
  shadow.appendChild(style);
  shadow.appendChild(div);
}
```

这个示例的关键函数是 `updateStyle()`——它接受一个元素，获取其影子根，找到它的 `<style>` 元素，然后添加 `width`、`height` 和 `background-color` 到样式上。

```js
function updateStyle(elem) {
  const shadow = elem.shadowRoot;
  shadow.querySelector("style").textContent = `
 div {
 width: ${elem.getAttribute("size")}px;
 height: ${elem.getAttribute("size")}px;
 background-color: ${elem.getAttribute("color")};
 }
 `;
}
```

实际的更新都是由生命周期回调处理的。`connectedCallback()` 每次将元素添加到 DOM 时都会运行一次——在这里，我们运行 `updateStyle()` 函数，以确保正方形的样式与其属性中定义的一致：

```js
connectedCallback() {
  console.log("自定义正方形元素添加至页面。");
  updateStyle(this);
}
```

`disconnectedCallback()` 和 `adoptedCallback()` 回调在元素从 DOM 中移除或移动到不同页面时记录消息到控制台，以通知我们：

```js
disconnectedCallback() {
  console.log("自定义正方形元素从页面中移除。");
}

adoptedCallback() {
  console.log("自定义正方形元素移动至新页面。");
}
```

`attributeChangedCallback()` 回调在元素的属性以某种方式更改时运行。正如你从其参数中看到的那样，可以单独处理属性，查看它们的名称以及旧的和新的属性值。不过在这种情况下，我们只是再次运行 `updateStyle()` 函数，以确保正方形的样式根据新的值更新：

```js
attributeChangedCallback(name, oldValue, newValue) {
  console.log("自定义正方形元素的属性已变更。");
  updateStyle(this);
}
```

请注意，要在属性更改时触发 `attributeChangedCallback()` 回调，必须观察这些属性。这通过在自定义元素类内指定一个 `static get observedAttributes()` 方法来实现——该方法应返回一个包含要观察的属性名称的数组：

```js
static get observedAttributes() {
  return ["color", "size"];
}
```


使用影子 DOM - Web API 接口参考

使用影子 DOM
========

自定义元素的一个重要方面是封装，因为自定义元素从定义上来说是一种可重用功能：它可以被放置在任何网页中，并且期望它能够正常工作。因此，很重要的一点是，运行在页面中的代码不应该能够通过修改自定义元素的内部实现而意外地破坏它。影子 DOM（Shadow DOM）允许你将一个 DOM 树附加到一个元素上，并且使该树的内部对于在页面中运行的 JavaScript 和 CSS 是隐藏的。

本文介绍了使用影子 DOM 的基础知识。

高层次概览
-----

本文假设你已经熟悉 DOM（文档对象模型）的概念——一种连接节点的树状结构，代表标记文档（通常是在 web 文档中的 HTML 文档）中出现的不同元素和文本字符串。作为示例，请考虑以下 HTML 片段：

```html
<html lang="zh-CN">
  <head>
    <meta charset="utf-8" />
    <title>DOM 示例</title>
  </head>
  <body>
    <section>
      <img src="dinosaur.png" alt="一个红色的霸王龙。" />
      <p>
        这里我们将添加一个到
        <a href="https://www.mozilla.org/">Mozilla 主页</a>的链接
      </p>
    </section>
  </body>
</html>
```

这个片段生成了以下的 DOM 结构（不包括仅包含空格的文本节点）：
```

- HTML
    - HEAD
        - META charset="utf-8"
        - TITLE
            - #text: DOM 示例
    - BODY
        - SECTION
            - IMG src="dinosaur.png" alt="一个红色的霸王龙。"
            - P
                - #text: 这里我们将添加一个到
                - A href="https://www.mozilla.org/"
                - #text: Mozilla 主页
                - #text: 的链接
```

*影子* DOM 允许将隐藏的 DOM 树附加到常规 DOM 树中的元素上——这个影子 DOM 始于一个影子根，在其之下你可以用与普通 DOM 相同的方式附加任何元素。

![显示文档、影子根和影子宿主交互的图示的 SVG 版本。](/zh-CN/docs/Web/API/Web_components/Using_shadow_DOM/shadowdom.svg)

有一些影子 DOM 术语需要注意：

* **影子宿主（Shadow host）**: 影子 DOM 附加到的常规 DOM 节点。
* **影子树（Shadow tree）**: 影子 DOM 内部的 DOM 树。
* **影子边界（Shadow boundary）**: 影子 DOM 终止，常规 DOM 开始的地方。
* **影子根（Shadow root）**: 影子树的根节点。

你可以用与非影子节点完全相同的方式来影响影子 DOM 中的节点——例如添加子节点和设置属性、使用 element.style.foo 对单个节点进行样式设置，或将整个影子树内的样式添加到一个 `<style>` 元素中。不同之处在于影子 DOM 内的所有代码都不会影响它的外部，从而便于实现封装。

在影子 DOM 向 web 开发者提供之前，浏览器已经使用它来封装元素的内部结构。以 `<video>` 元素举例，它暴露了默认浏览器控件。在 DOM 中你只能看到 `<video>` 元素，但其影子 DOM 中包含了一系列按钮和其它控件。影子 DOM 规范使你能够操纵自定义元素的影子 DOM。

创建一个影子 DOM
----------

下面的页面包含两个元素，一个 `id` 属性为 `"host"` 的 `<div>` 元素，以及一个包含了一些文本的 `<span>` 元素：

```html
<div id="host"></div>
<span>I'm not in the shadow DOM</span>
```

我们将使用 `"host"` 元素作为影子宿主。我们调用宿主上的 `attachShadow()` 来创建影子 DOM，然后可以像我们将节点添加到主 DOM 一样将节点添加到影子 DOM 中。在这个示例中，我们添加了单个 `<span>` 元素：

```js
const host = document.querySelector("#host");
const shadow = host.attachShadow({ mode: "open" });
const span = document.createElement("span");
span.textContent = "I'm in the shadow DOM";
shadow.appendChild(span);
```

结果看起来像这样：

JavaScript 封装
-------------

目前这可能看起来并不怎么样。但让我们看看如果页面中运行的代码试图访问影子 DOM 中的元素会发生什么。

这个页面就像上一个一样，只是我们添加了两个 `<button>` 元素。

```html
<div id="host"></div>
<span>I'm not in the shadow DOM</span>
<br />

<button id="upper" type="button">将 span 元素转换为大写</button>
<button id="reload" type="button">重新加载</button>
```

 点击“将 span 元素转换为大写”按钮将找到页面中所有 `<span>` 元素并将它们的文本改变为大写。
 点击“重新加载”按钮只会重新加载页面，这样你就可以再次尝试。

```js
const host = document.querySelector("#host");
const shadow = host.attachShadow({ mode: "open" });
const span = document.createElement("span");
span.textContent = "I'm in the shadow DOM";
shadow.appendChild(span);

const upper = document.querySelector("button#upper");
upper.addEventListener("click", () => {
  const spans = Array.from(document.querySelectorAll("span"));
  for (const span of spans) {
    span.textContent = span.textContent.toUpperCase();
  }
});

const reload = document.querySelector("#reload");
reload.addEventListener("click", () => document.location.reload());
```

如果你点击“将 span 元素转换为大写”，你将看到 `Document.querySelectorAll()` 并没有找到在我们影子 DOM 中元素：它们对页面中的 JavaScript 来说基本上是隐藏的：

Element.shadowRoot 和“mode”选项
----------------------------

在上面的例子中，我们将一个参数 `{ mode: "open" }` 传入 `attachShadow()`。当 `mode` 设置为 `"open"` 时，页面中的 JavaScript 可以通过影子宿主的 `shadowRoot` 属性访问影子 DOM 的内部。

在这个例子中，就像之前一样，HTML 包含影子宿主、主 DOM 树中的一个 `<span>` 元素以及两个按钮：

```html
<div id="host"></div>
<span>I'm not in the shadow DOM</span>
<br />

<button id="upper" type="button">将 span 元素转换为大写</button>
<button id="reload" type="button">重新加载</button>
```

这次，“大写”按钮使用 `shadowRoot` 来找到 DOM 中的 `<span>` 元素：

```js
const host = document.querySelector("#host");
const shadow = host.attachShadow({ mode: "open" });
const span = document.createElement("span");
span.textContent = "I'm in the shadow DOM";
shadow.appendChild(span);

const upper = document.querySelector("button#upper");
upper.addEventListener("click", () => {
  const spans = Array.from(host.shadowRoot.querySelectorAll("span"));
  for (const span of spans) {
    span.textContent = span.textContent.toUpperCase();
  }
});

const reload = document.querySelector("#reload");
reload.addEventListener("click", () => document.location.reload());
```

这次，页面中运行的 JavaScript 可以访问影子 DOM 内部：

使用 `{mode: "open"}` 参数为页面提供一种破坏影子 DOM 封装的方法。如果你不希望给页面这个能力，传递 `{mode: "closed"}` 作为替代，此时 `shadowRoot` 返回 `null`。

然而，你不应将这视为一个强大的安全机制，因为它可以被绕过，比如通过在页面中运行的浏览器扩展。这更多地是一个指示页面不应访问影子 DOM 树内部的一种提示。

CSS 封装
------

在这个版本的页面中，HTML 与原始版本相同：

```html
<div id="host"></div>
<span>I'm not in the shadow DOM</span>
```

在 JavaScript 中，我们创建影子 DOM：

```js
const host = document.querySelector("#host");
const shadow = host.attachShadow({ mode: "open" });
const span = document.createElement("span");
span.textContent = "I'm in the shadow DOM";
shadow.appendChild(span);
```

这次，我们将具有针对页面中 `<span>` 元素的一些 CSS：

```css
span {
  color: blue;
  border: 1px solid black;
}
```

页面的 CSS 不会影响影子 DOM 内的节点：

在影子 DOM 内应用样式
-------------

在这个部分，我们将看到两种不同的方法来在影子 DOM 树中应用样式：

* *编程式*，通过构建一个 `CSSStyleSheet` 对象并将其附加到影子根。
* *声明式*，通过在一个 `<template>` 元素的声明中添加一个 `<style>` 元素。

在这两种情况下，影子 DOM 树中定义的样式局限在该树内，所以就像页面样式就像不会影响影子 DOM 中的元素一样，影子 DOM 样式也不会影响页面中其它元素的样式。

### 可构造样式表

要使用可构造样式表为影子 DOM 中的页面元素设置样式，我们可以：

1. 创建一个空的 `CSSStyleSheet` 对象
2. 使用 `CSSStyleSheet.replace()` (en-US) 或 `CSSStyleSheet.replaceSync()` (en-US) 设置其内容
3. 通过将其赋给 `ShadowRoot.adoptedStyleSheets` (en-US) 来添加到影子根

在 `CSSStyleSheet` 中定义的规则将局限在影子 DOM 树的内部，以及我们将其分配到的任何其它 DOM 树。

这里，同样是包含宿主（host）和一个 `<span>` 的 HTML：

```html
<div id="host"></div>
<span>I'm not in the shadow DOM</span>
```

这次我们将创建影子 DOM 并将一个 `CSSStyleSheet` 对象赋给它：

```js
const sheet = new CSSStyleSheet();
sheet.replaceSync("span { color: red; border: 2px dotted black;}");

const host = document.querySelector("#host");

const shadow = host.attachShadow({ mode: "open" });
shadow.adoptedStyleSheets = [sheet];

const span = document.createElement("span");
span.textContent = "I'm in the shadow DOM";
shadow.appendChild(span);
```

影子 DOM 树中定义的样式不会应用到页面的其它部分：

### 在 `<template>` 声明中添加 `<style>` 元素

构建 `CSSStyleSheet` 对象的一个替代方法是将一个 `<style>` 元素包含在用于定义 web 组件的 `<template>` 元素中。

在这种情况下，HTML 包含 `<template>` 声明

```html
<template id="my-element">
  <style>
 span {
 color: red;
 border: 2px dotted black;
 }
 </style>
  <span>I'm in the shadow DOM</span>
</template>

<div id="host"></div>
<span>I'm not in the shadow DOM</span>
```

在 JavaScript 中，我们将创建影子 DOM 并将 `<template>` 的内容添加到其中：

```js
const host = document.querySelector("#host");
const shadow = host.attachShadow({ mode: "open" });
const template = document.getElementById("my-element");

shadow.appendChild(template.content);
```

同样地，在 `<template>` 中定义的样式局限在影子 DOM 树内，而不是在页面的其它部分：

### 在编程式和声明式中选择

使用哪种方式取决于你的应用程序和个人喜好。

创建一个 `CSSStyleSheet` 并通过 `adoptedStyleSheets` 将其赋给影子根允许你创建单一样式表并将其与多个 DOM 树共享。例如，一个组件库可以创建单个样式表，然后将其与该库的所有自定义元素共享。浏览器将仅解析该样式表。此外，你可以对样式表进行动态更改，并将更改传播到使用表的所有组件。

而当你希望是声明式的、需要较少的样式并且不需要在不同组件之间共享样式的时候，附加 `<style>` 元素的方法则非常适合。

影子 DOM 和自定义元素
-------------

如果没有影子 DOM 提供的封装，自定义元素将变得无法使用。只需在某个页面上运行一些 JavaScript 或 CSS，就有可能无意间破坏自定义元素的行为或布局。作为自定义元素的开发者，你将无法知道适用于自定义元素内部的选择器是否与使用你自定义元素的页面中应用的选择器发生冲突。

自定义元素被实现为一个类，它可以继承 `HTMLElement` 或像 `HTMLParagraphElement` 这样的内置 HTML 元素。通常，自定义元素本身是一个影子宿主，该元素在其根节点下创建多个元素，以提供元素的内部实现。

下面的示例创建了一个 `<filled-circle>` 自定义元素，该元素仅渲染一个填充了实心颜色的圆形。

```js
class FilledCircle extends HTMLElement {
  constructor() {
    super();
  }
  connectedCallback() {
    // 创建一个影子根
    // 自定义元素自身是影子宿主
    const shadow = this.attachShadow({ mode: "open" });

    // 创建内部实现
    const svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
    const circle = document.createElementNS(
      "http://www.w3.org/2000/svg",
      "circle",
    );
    circle.setAttribute("cx", "50");
    circle.setAttribute("cy", "50");
    circle.setAttribute("r", "50");
    circle.setAttribute("fill", this.getAttribute("color"));
    svg.appendChild(circle);

    shadow.appendChild(svg);
  }
}

customElements.define("filled-circle", FilledCircle);
```

```html
<filled-circle color="blue"></filled-circle>
```

对于更多示例和描述自定义元素实现的不同方面，请参见我们的自定义元素指南。

参见
--

* 使用自定义元素
* 使用模板和插槽
* `Element.attachShadow()`
* `ShadowRoot.adoptedStyleSheets` (en-US)
* `CSSStyleSheet.replace()` (en-US)
* `CSSStyleSheet.replaceSync()` (en-US)
* `<template>`
* CSS 域 (en-US)模块
* `:host`
* `:host()`
* `:host-context()`
* `::slotted()`
* CSS 影子部件 (en-US)模块
* `::part`


使用 templates and slots - Web API 接口参考

使用 templates and slots
======================

这篇文章阐述了如何使用 `<template>` 和 `<slot>` 元素创建一个可以用来灵活填充 Web 组件的 shadow DOM 的模板。

关于模板 (Templates)
----------------

当你必须在网页上重复使用相同的标记结构时，使用某种模板而不是一遍又一遍地重复相同的结构是有意义的。以前这是可行的，但 HTML `<template>` 元素使它更容易实现 (这在现代浏览器中得到了很好的支持)。此元素及其内容不会在 DOM 中呈现，但仍可使用 JavaScript 去引用它。

让我们看一个简单的示例：

```html
<template id="my-paragraph">
  <p>My paragraph</p>
</template>
```

上面的代码不会展示在你的页面中，直到你用 JavaScript 获取它的引用，然后添加到 DOM 中，如下面的代码：

```js
let template = document.getElementById("my-paragraph");
let templateContent = template.content;
document.body.appendChild(templateContent);
```

虽然是个简单的例子，但你已经可以开始了解它是多么的有用了。

在 Web Component 中使用模板
---------------------

模板（Template）本身就是有用的，而与 web 组件（web component）一起使用效果更好。我们定义一个 web 组件使用模板作为阴影（shadow）DOM 的内容，叫它 `<my-paragraph>`：

```js
customElements.define(
  "my-paragraph",
  class extends HTMLElement {
    constructor() {
      super();
      let template = document.getElementById("my-paragraph");
      let templateContent = template.content;

      const shadowRoot = this.attachShadow({ mode: "open" });
      shadowRoot.appendChild(templateContent.cloneNode(true));
    }
  },
);
```

要注意的关键是我们使用 `Node.cloneNode()` 方法添加了模板的拷贝到阴影的根结点上。

因为我们添加了模板的内容到 shadow DOM，所以我们可以加入一些样式信息到模板的 `<style>` 标签里，这些样式信息稍后会封装到自定义的元素中。如果只给它添加到一个标准的 DOM 中是不起作用的。

比如：

```html
<template id="my-paragraph">
  <style>
 p {
 color: white;
 background-color: #666;
 padding: 5px;
 }
 </style>
  <p>My paragraph</p>
</template>
```

现在我们将它添加自己的 HTML 文档中试试看：

```html
<my-paragraph></my-paragraph>
```

**备注：** 模板在浏览器中的支持情况很好，Firefox（版本 63 及以上），Chrome，Opera ,Edge（版本 79 及以上）和 Safari 支持 Shadow DOM

使用槽 (slots) 添加灵活度
-----------------

尽管到这一步已经挺好了，但是元素仍旧不是很灵活。我们只能在里面放一点文本，甚至没有普通的 p 标签管用！我们使用 `<slot>` 让它能在单个实例中通过声明式的语法展示不同的文本。浏览器对这个功能的支持比`<template>`少，在 Chrome 53, Opera 40, Safari 10，火狐 59 和 Edge 79 中支持。

插槽由其`name`属性标识，并且允许你在模板中定义占位符，当在标记中使用该元素时，该占位符可以填充所需的任何 HTML 标记片段。

如果你想添加一个槽到我们的这个例子，我们会将模板的 p 标签改成下面这样：

```html
<p><slot name="my-text">My default text</slot></p>
```

如果在标记中包含元素时未定义相关的插槽内容，或者浏览器不支持 slot 属性，则`<my-paragraph>`仅包含后备内容"My default text"。(译者注：此处的意思是使用<my-paragraph>时内部不包裹任何内容时会显示 slot 定义好的默认值。具体使用可参考下面)

要定义插槽内容，我们在`<my-paragraph>`元素内包括一个 HTML 结构，该结构具有`slot`属性，其值等于我们要填充的`<slot>`的 name 属性的值。和以前一样，它可以是你喜欢的任何东西，例如：

```html
<my-paragraph>
  <span slot="my-text">Let's have some different text!</span>
</my-paragraph>
```

或者

```html
<my-paragraph>
  <ul slot="my-text">
    <li>Let's have some different text!</li>
    <li>In a list!</li>
  </ul>
</my-paragraph>
```

以上为例。

**备注：** 将能被插入到槽中的元素视为 `Slotable` (en-US); 称已经插入到槽中的元素为 *slotted*.

这就我们普通的例子，你想更深入了解你可以 在 GitHub 上查看 (也可以看 在线运行)。

更深入的示例
------

为了更好的结束这篇文章，我们来个看一些不寻常的示例。

下面的一组代码段展示了我们如何联合使用 `<slot>` 和 `<template>` 以及一些 JavaScript 去完成：

* 使用 命名的槽 (named slots) 在 阴影 (shadow root) 创建一个 **`<element-details>`** 元素。
* 将 **`<element-details>`** 元素设计成在文档中使用时，它将它的 shadow root 中的内容和该元素内部的内容糅合在一起 —也就是元素内容中的片断被填充到元素的 阴影 (shadow root) 的 命名的槽 (named slots) 中。

注意，技术上来讲没有`<template>` 使用 `<slot>` 也是可以的。例如， `<slot>` 在一个常规的`<div>` 标签里，仍然有占位符的特性，就像在 shadow DOM 中一样，这样我们能避免需要先获取模板对象的 `content` 属性再使用它的麻烦。然而这个特性在向一个 `<template>` 元素中添加槽时更加实用，因为你不会基于一个渲染的元素定义一个模式。

另外，就算它还没有渲染，使用`<template>`时，作为模板的容器应该语义上应当干净。而且 `<template>` 可以直接挂一些对象，如 `<td>` (en-US) ，当加到一个 `<div>` 中时会隐藏。

**备注：** 查看在完整的例子在 element-details ( 在线运行 ).

### 使用槽 (slot) 创建一个模板

首先，我们在`<template>` 使用 `<slot>` 标签创建一个"element-details-template"包含一些 命名的槽 (named slot) 的 文档片段 (document fragment)

```html
<template id="element-details-template">
  <style>
 details {
 font-family: "Open Sans Light", Helvetica, Arial;
 }
 .name {
 font-weight: bold;
 color: #217ac0;
 font-size: 120%;
 }
 h4 {
 margin: 10px 0 -8px 0;
 }
 h4 span {
 background: #217ac0;
 padding: 2px 6px 2px 6px;
 }
 h4 span {
 border: 1px solid #cee9f9;
 border-radius: 4px;
 }
 h4 span {
 color: white;
 }
 .attributes {
 margin-left: 22px;
 font-size: 90%;
 }
 .attributes p {
 margin-left: 16px;
 font-style: italic;
 }
 </style>
  <details>
    <summary>
      <span>
        <code class="name"
 ><<slot name="element-name">NEED NAME</slot>></code
 >
        <i class="desc"><slot name="description">NEED DESCRIPTION</slot></i>
      </span>
    </summary>
    <div class="attributes">
      <h4><span>Attributes</span></h4>
      <slot name="attributes"><p>None</p></slot>
    </div>
  </details>
  <hr />
</template>
```

上面这个`<template>` 有几个特征：

* `<template>` 标签有一个 `<style>` 标签，里面有一些 只能在当前`<template>` 的派生中生效的 CSS 样式
* `<template>` 用 `<slot>` 和它的 `name` 属性生成一个 命名的槽 (named slots)：
    + `<slot name="element-name">`
    + `<slot name="description">`
    + `<slot name="attributes">`
* `<template>` 包裹 命名的槽 (named slots) 在 `<details>` 元素中。
### 从<template> 中创建一个新的 <element-details> 对象

接下来，我们定义一个新的 **`<element-details>`** 元素然后用 `Element.attachShadow` 来将它附着到它的 阴影 (shadow) root, 这个阴影上我们创建了 `<template>` 元素。这和我们上面使用的简单例子中的一样。

```js
customElements.define(
  "element-details",
  class extends HTMLElement {
    constructor() {
      super();
      var template = document.getElementById(
        "element-details-template",
      ).content;
      const shadowRoot = this.attachShadow({ mode: "open" }).appendChild(
        template.cloneNode(true),
      );
    }
  },
);
```

### 结合命名的 slots 使用自定义元素 <element-details>

现在让我们正式在文档流中使用<element-details>标签：

```html
<element-details>
  <span slot="element-name">slot</span>
  <span slot="description"
 >A placeholder inside a web component that users can fill with their own
    markup, with the effect of composing different DOM trees together.</span
 >
  <dl slot="attributes">
    <dt>name</dt>
    <dd>The name of the slot.</dd>
  </dl>
</element-details>

<element-details>
  <span slot="element-name">template</span>
  <span slot="description"
 >A mechanism for holding client- side content that is not to be rendered
    when a page is loaded but may subsequently be instantiated during runtime
    using JavaScript.</span
 >
</element-details>
```

关于以上代码片段，请注意以下几点：

* 该代码片段有 2 个 **`<element-details>`** 标签，他们都使用了“slot”属性来引用名为`"element-name"`和`"description"`的槽，并把他们都放在根下。
* 只有第一个 **`<element-details>`** 元素 引用了名为 `"attributes"` 的槽，而第二个 `<element-details>` 标签没有引用名为`"attributes"`的槽。
* 只有第一个 **`<element-details>`** 标签中的"dl"使用了名为 `"attributes"`的槽，他包含子元素："dt"和"dd"元素。
### 添加一些最终的样式

在完成之前，我们在文档中给"dl"和"dt"以及"dd"标签增加一些 CSS 样式。

```css
dl {
  margin-left: 6px;
}
dt {
  font-weight: bold;
  color: #217ac0;
  font-size: 110%;
}
dt {
  font-family: Consolas, "Liberation Mono", Courier;
}
dd {
  margin-left: 16px;
}
```

```
body {
  margin-top: 47px;
}
```

### 结果

最后，让我们所有的代码片段结合在一起，看看渲染的结果是什么样的。

| Screenshot | Live sample |
| --- | --- |
|  |  |

关于结果请注意以下几点：

* 即使 **`<element-details>`** 标签并没有直接使用`"details"`标签元素，但是在渲染的时候使用了`"details"`标签，是因为 shadow root 使得他们被填充。
* 在渲染出来的`"details"`标签，**`<element-details>`** 标签从根开始填充了相应的命名的槽。换言之，**`<element-details>`** 的 DOM 树和 shadow root 的内容结合在了一起。
* 对于这两个 **`<element-details>`** 标签，属性标题会自动的从 shadow root 中添加到名为`"attribute"`的插槽的前面。
* 因为第一个 **`<element-details>`** 标签的`"dl"`元素就明确的引用了名为`"attributes"`的插槽，所以该`"dl"`元素的内容将会替换该标签中名为`"attributes"`的插槽。
* 因为第二个 **`<element-details>`** 标签没有引用名为`"attributes"` 的槽，所以标签内名为`"attributes"的`插槽的内容将会使用模板中默认的内容。
```

<!doctype html>
<html>
  <head>
    <title>slot example</title>
    <style>
 dl {
 margin-left: 6px;
 }
 dt {
 font-weight: bold;
 color: #217ac0;
 font-size: 110%;
 }
 dt {
 font-family: Consolas, "Liberation Mono", Courier;
 }
 dd {
 margin-left: 16px;
 }
 </style>
  </head>
  <body>
    <template id="element-details-template">
      <style>
 details {
 font-family: "Open Sans Light", Helvetica, Arial;
 }
 .name {
 font-weight: bold;
 color: #217ac0;
 font-size: 120%;
 }
 h4 {
 margin: 10px 0 -8px 0;
 }
 h4 span {
 background: #217ac0;
 padding: 2px 6px 2px 6px;
 }
 h4 span {
 border: 1px solid #cee9f9;
 border-radius: 4px;
 }
 h4 span {
 color: white;
 }
 .attributes {
 margin-left: 22px;
 font-size: 90%;
 }
 .attributes p {
 margin-left: 16px;
 font-style: italic;
 }
 </style>
      <details>
        <summary>
          <span>
            <code class="name"
 ><<slot name="element-name">NEED NAME</slot>></code
 >
            <i class="desc"><slot name="description">NEED DESCRIPTION</slot></i>
          </span>
        </summary>
        <div class="attributes">
          <h4><span>Attributes</span></h4>
          <slot name="attributes"><p>None</p></slot>
        </div>
      </details>
      <hr />
    </template>

    <element-details>
      <span slot="element-name">slot</span>
      <span slot="description"
 >A placeholder inside a web component that users can fill with their own
        markup, with the effect of composing different DOM trees together.</span
 >
      <dl slot="attributes">
        <dt>name</dt>
        <dd>The name of the slot.</dd>
      </dl>
    </element-details>

    <element-details>
      <span slot="element-name">template</span>
      <span slot="description"
 >A mechanism for holding client- side content that is not to be rendered
        when a page is loaded but may subsequently be instantiated during
        runtime using JavaScript.</span
 >
    </element-details>

    <script>
 customElements.define(
 "element-details",
 class extends HTMLElement {
 constructor() {
 super();
 const template = document.getElementById(
 "element-details-template",
 ).content;
 const shadowRoot = this.attachShadow({ mode: "open" }).appendChild(
 template.cloneNode(true),
 );
 }
 },
 );
 </script>
  </body>
</html>
```


StorageManager.persisted() - Web API 接口参考

StorageManager.persisted()
==========================

**安全上下文:** 此项功能仅在一些支持的浏览器的安全上下文（HTTPS）中可用。

**实验性:** **这是一项实验性技术**
在将其用于生产之前，请仔细检查浏览器兼容性表格。

\*\*`persisted()`\*\*方法是`StorageManager`的一个接口，如果盒存储模式（**box mode**）的值为“**persistent**”则返回一个 resolved 状态值为`true`的`Promise`。

语法
--
```

navigator.storage.persisted().then(function(persistent) { ... })
```

### 参数

无

### 返回值

一个状态为 resolved，值为`Boolean`类型的`Promise`。

示例
--

```js
if (navigator.storage && navigator.storage.persist)
  navigator.storage.persisted().then(function (persistent) {
    if (persistent)
      console.log("Storage will not be cleared except by explicit user action");
    else
      console.log("Storage may be cleared by the UA under storage pressure.");
  });
```

规范
--

| Specification |
| --- |
| Storage Standard # dom-storagemanager-persisted |

浏览器兼容性
------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.


StorageManager.persist() - Web API 接口参考

StorageManager.persist()
========================

**安全上下文:** 此项功能仅在一些支持的浏览器的安全上下文（HTTPS）中可用。

**实验性:** **这是一项实验性技术**
在将其用于生产之前，请仔细检查浏览器兼容性表格。

\*\*`persist()`\*\*方法是`StorageManager`的一个接口，用于请求本地数据存储的权限，如果被授予权限，则返回一个 resolved 状态值为`true`的`Promise`对象，否则返回`false`。

语法
--
```

navigator.storage.persist().then(function(persistent) { ... })
```

### 参数

无

### 返回值

一个 resolved 状态，值为`Boolean`类型的`Promise`对象。

示例
--

```js
if (navigator.storage && navigator.storage.persist)
  navigator.storage.persist().then(function (persistent) {
    if (persistent)
      console.log("Storage will not be cleared except by explicit user action");
    else
      console.log("Storage may be cleared by the UA under storage pressure.");
  });
```

规范
--

| Specification |
| --- |
| Storage Standard # ref-for-dom-storagemanager-persist |

浏览器兼容性
------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.


StorageManager.estimate() - Web API 接口参考

StorageManager.estimate()
=========================

**安全上下文:** 此项功能仅在一些支持的浏览器的安全上下文（HTTPS）中可用。

\*\*`estimate()`\*\*方法是`StorageManager`的一个接口，用于估算某一个域名（或一个站点）下 Storage Manager 的总存储空间和已经使用了的存储空间。此方法为一个异步方法，如果此方法可用，那么其返回一个结果为 resolved 的`Promise`对象。resolved 接收的参数是一个带有已使用数据存储空间和总可用总存储空间的`StorageEstimate`对象。

语法
--
```

var estimatePromise = StorageManager.estimate();
```

### 参数

无

### 返回值

`StorageEstimate`类型的状态为 resolved 的`Promise`
此数据包含了此应用（或域名）可用的存储空间（`StorageEstimate.quota` (en-US)）和目前已经使用了的存储空间（`StorageEstimate.usage` (en-US)）。

这些值不是明确的数字，在进行压缩，重复数据删除和出于安全原因起见进行了混淆之后，这个数据是不精确的。

你可能会发现不同的应用或站点分配的存储空间不同，具体取决于用户访问频率，和网站受欢迎程度等数据。

示例
--

在这个示例中，我们使用 estimate() 得到目前所使用的存储空间占全部存储空间的百分比。

### HTML

```html
<p>
  You're currently using about <span id="percent"> </span>% of your available
  storage.
</p>
```

### JavaScript

```js
navigator.storage.estimate().then(function (estimate) {
  document.getElementById("percent").innerHTML = (
    (estimate.usage / estimate.quota) \*
    100
  ).toFixed(2);
});
```

### 结果

规范
--

| Specification |
| --- |
| Storage Standard # ref-for-dom-storagemanager-estimate |

浏览器兼容性
------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.

参见
--

* Storage API
* `Storage`, the object returned by `Window.localStorage`
* `StorageManager`
* `navigator.storage` (en-US)


LockManager：request() 方法 - Web API 接口参考

LockManager：request() 方法
========================

**安全上下文:** 此项功能仅在一些支持的浏览器的安全上下文（HTTPS）中可用。

`LockManager` 接口的 **`request()`** 方法请求一个 `Lock` 对象，该方法发参数指定返回对象的名称和特征。请求的 `Lock` 对象被传递给回调，而函数本身返回一个 `Promise`，在锁被释放后解析（或拒绝）回调的结果，或者在请求被中止时拒绝。

`options` 参数的 `mode` 属性可以接收 `"exclusive"` 或 `"shared"`。

当锁一次只能由一个代码实例持有时，请求 `"exclusive"` 锁。这适用于标签页和 worker 中的代码。使用它来表示对资源的互斥访问。当持有给定名称的 `"exclusive"` 锁时，不能持有其他同名的锁。

当代码的多个实例可以共享对资源的访问时，请求 `"shared"` 锁。当持有给定名称的 `"shared"` 锁时，可以授予同名的其他 `"shared"` 锁，但不能持有或授予具有该名称的 `"exclusive"` 锁。

这种共享/独占锁模式在数据库事务架构中很常见，例如允许多个并发读取器（每个读取器请求一个 `"shared"` 锁），但仅允许一个写入器（单个 `"exclusive"` 锁）。这称为读者——作者模式。在 IndexedDB API 中，这被暴露为具有相同语义的 `"readonly"` 和 `"readwrite"` 事务。

**备注：** 此特性在 Web Worker 中可用

语法
--

```js
request(name, callback)
request(name, options, callback)
```

### 参数

`name`

你想要请求的锁的标识符。

`options` 可选

一个描述你要创建的锁的特征的对象。有效的值为：

`mode` 可选

`"exclusive"` 或 `"shared"` 之一。默认值是· `"exclusive"`。

`ifAvailable` 可选

如果为 `true`，则只有在尚未持有锁定请求时才会授予该锁定请求。如果无法授予，则将传入 `null` 而非 `Lock` 实例来调用回调。默认值为 `false`。

`steal` 可选

如果为 `true`，则任何持有的同名锁将被释放，并且请求将被授予，抢占任何排队中的锁请求。默认值为 `false`。

**警告：** 小心使用！之前在锁内运行的代码会继续运行，并且可能与现在持有锁的代码发生冲突。

`signal` 可选

一个 `AbortSignal`（`AbortController` 的 `signal` 属性）；如果指定并且 `AbortController` 被中止，则锁请求将被丢弃（如果尚未授予）。

`callback`

授予锁时调用的方法。当回调返回（或抛出异常）时，锁会自动释放。通常回调是一个异步函数，这会导致只有当异步函数完全完成时才会释放锁。

### 返回值

一个 `Promise`，在锁释放后解析（或拒绝）回调结果，或者拒绝若请求中止。

### 异常

此方法可能会返回一个被以下类型之一的 `DOMException` 拒绝的 promise：

`InvalidStateError` `DOMException`

如果当前环境的文档未完全激活，则抛出该异常。

`SecurityError` `DOMException`

如果无法获取当前环境的锁管理器，则抛出该异常。

`NotSupportedError` `DOMException`

如果 `name` 以连字符（`-`）开头，或选项 `steal` 和 `ifAvailable` 均为 `true`，或者选项 `signal` 存在且选项 `steal` 或 `ifAvailable` 为 `true`。

`AbortError` `DOMException`

如果选项 `signal` 存在并且被中止，则抛出该异常。

示例
--

### 一般示例

以下示例显示了 `request()` 方法的基本用法，并以异步函数作为回调函数。一旦调用回调函数，该源上的其他正在运行的代码就无法持有 `my_resource` 锁，直到回调返回。

```js
await navigator.locks.request("my\_resource", async (lock) => {
  // 锁被授予。
});
```

### `mode` 示例

以下示例展示了如何为读取器和写入器使用 `mode` 选项。

请注意，这两个函数都使用名为 `my_resource` 的锁。`do_read()` 请求 `'shared'` 模式下的锁定，这意味着多个调用可能会在不同的事件处理器、标签页或 worker 中同时发生。

```js
async function do\_read() {
  await navigator.locks.request(
    "my\_resource",
    { mode: "shared" },
    async (lock) => {
      // 在这里放置执行读取器具体操作的代码。
    },
  );
}
```

`do_write()` 函数使用相同的锁，但处于 `'exclusive'` 模式，这将延迟 `do_read()` 中 `request()` 的调用，直到写操作完成。这适用于事件处理器、标签页或 worker。

```js
async function do\_write() {
  await navigator.locks.request(
    "my\_resource",
    { mode: "exclusive" },
    async (lock) => {
      // 在这里放置执行写入器具体操作的代码。
    },
  );
}
```

### `ifAvailable` 示例

要仅在尚未持有锁时才获取锁，使用 `ifAvailable` 选项。在此函数中，`await` 表示该方法在回调完成之前不会返回。由于仅在可用时才授予锁，因此此调用避免了需要等待锁在其他地方释放。

```js
await navigator.locks.request(
  "my\_resource",
  { ifAvailable: true },
  async (lock) => {
    if (!lock) {
      // 锁没有被授予——尽快结束。
      return;
    }

    // 该锁已被授予，并且该源中没有其他正在运行的代码持有 'my\_res\_lock' 锁，直到该锁返回为止。
  },
);
```

### `signal` 示例

如果只等待锁一小段时间，请使用 `signal` 选项。

```js
const controller = new AbortController();
// 最多等待 200 毫秒。
setTimeout(() => controller.abort(), 200);

try {
  await navigator.locks.request(
    "my\_resource",
    { signal: controller.signal },
    async (lock) => {
      // 锁被获取了！
    },
  );
} catch (ex) {
  if (ex.name === "AbortError") {
    // 该请求在获得锁之前就中止了。
  }
}
```

规范
--

| Specification |
| --- |
| Web Locks API # api-lock-manager-request |

浏览器兼容性
------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.


LockManager：query() 方法 - Web API 接口参考

LockManager：query() 方法
======================

**安全上下文:** 此项功能仅在一些支持的浏览器的安全上下文（HTTPS）中可用。

`LockManager` 接口的 **`query()`** 方法返回一个 `Promise`，该对象兑现一个包含持有中的锁和请求中的锁的信息的对象。

**备注：** 此特性在 Web Worker 中可用

语法
--

```js
query()
```

### 参数

无。

### 返回值

一个 `Promise`，兑现一个包含 `LockManager` 状态快照的对象。该对象具有以下属性：

`held`

一个持有中的锁的 `LockInfo` 对象数组。

`pending`

一个请求中的锁的 `LockInfo` 对象数组。

`LockInfo` 对象可以具有以下属性：

`name`

请求锁时传递给 `LockManager.request()` 的名称。

`mode`

请求锁时传递给 `LockManager.request()` 的模式。该模式可以是 `"exclusive"` 或 `"shared"`。

`clientId`

调用 `LockManager.request()` 的上下文的唯一标识。该值与 `Client.id` 相同。

### 异常

此方法可能会返回一个被以下类型的 `DOMException` 之一拒绝的 promise：

`InvalidStateError` `DOMException`

如果当前环境的文档未完全激活，则抛出该异常。

`SecurityError` `DOMException`

如果无法获取当前环境的锁管理器，则抛出该异常。

示例
--

```js
const state = await navigator.locks.query();
for (const lock of state.held) {
  console.log(`持有中的锁：名称 ${lock.name}，模式 ${lock.mode}`);
}
for (const request of state.pending) {
  console.log(`请求中的锁：名称 ${request.name}，模式 ${request.mode}`);
}
```

规范
--

| Specification |
| --- |
| Web Locks API # api-lock-manager-query |

浏览器兼容性
------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.


AudioContext.createMediaElementSource() - Web API 接口参考

AudioContext.createMediaElementSource()
=======================================

`AudioContext` 接口的 `createMediaElementSource()` 方法用于创建一个新的 `MediaElementAudioSourceNode` 对象，输入某个存在的 HTML `<audio>` or `<video>` 元素，对应的音频即可被播放或者修改。

为寻求更多关于媒体元素音频源节点的具体信息，请查阅 `MediaElementAudioSourceNode` 参考页。

语法
--

```js
var audioCtx = new AudioContext();
var source = audioCtx.createMediaElementSource(myMediaElement);
```

### 参数

`myMediaElement`

某个被期待被录入音频处理图修改的 `HTMLMediaElement` 对象。

### 返回值

一个 `MediaElementAudioSourceNode` 对象。

示例
--

该示例由 `<audio>` 元素，通过使用 `createMediaElementSource()` 方法，创建了一个音源，将其通过 `GainNode` 节点，输出到`AudioDestinationNode` 节点以播放。当鼠标指针移动时，`updatePage()` 函数被调用，该函数计算当前鼠标 Y 坐标与页面高度的比值，改变 `GainNode` 节点的值以调整音量。你就可以通过鼠标上下移动而改变音乐的音量了。

**备注：** 也可以浏览该示例的在线演示, 或者浏览源代码。

```js
var audioCtx = new (window.AudioContext || window.webkitAudioContext)();
var myAudio = document.querySelector("audio");
var pre = document.querySelector("pre");
var myScript = document.querySelector("script");

pre.innerHTML = myScript.innerHTML;

// Create a MediaElementAudioSourceNode
// Feed the HTMLMediaElement into it
var source = audioCtx.createMediaElementSource(myAudio);

// Create a gain node
var gainNode = audioCtx.createGain();

// Create variables to store mouse pointer Y coordinate
// and HEIGHT of screen
var CurY;
var HEIGHT = window.innerHeight;

// Get new mouse pointer coordinates when mouse is moved
// then set new gain value

document.onmousemove = updatePage;

function updatePage(e) {
  CurY = window.Event
    ? e.pageY
    : event.clientY +
      (document.documentElement.scrollTop
        ? document.documentElement.scrollTop
        : document.body.scrollTop);

  gainNode.gain.value = CurY / HEIGHT;
}

// connect the AudioBufferSourceNode to the gainNode
// and the gainNode to the destination, so we can play the
// music and adjust the volume using the mouse cursor
source.connect(gainNode);
gainNode.connect(audioCtx.destination);
```

**备注：** 作为调用 `createMediaElementSource()` 的结果，`HTMLMediaElement` 的播放将会由 AudioContext 的音频处理图接管。所以对媒体进行播放/暂停等操作仍可通过 media API 与播放器面板来进行。

规范
--

| Specification |
| --- |
| Web Audio API # dom-audiocontext-createmediaelementsource |

浏览器兼容性
------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.

参见
--

* Using the Web Audio API (en-US)


AudioContext.suspend() - Web API 接口参考

AudioContext.suspend()
======================

`AudioContext` 接口的`suspend()` 方法暂停音频上下文对象中的进度，并暂时剥离进程对音频设备硬件的访问权限，减少 CPU 和电池的使用。当程序在一段时间内不会使用音频上下文对象时，这个方法对减少硬件资源占用是非常有用的。

若对`OfflineAudioContext` 调用此方法，将会抛出 `INVALID_STATE_ERR` 错误。

Syntax
------

```js
var audioCtx = new AudioContext();
audioCtx.suspend().then(function() { ... });
```

### Returns

A `Promise` that resolves with void. The promise is rejected if the context has already been closed.

Example
-------

The following snippet is taken from our AudioContext states demo (see it running live.) When the suspend/resume button is clicked, the `AudioContext.state` is queried — if it is `running`, `suspend()` is called; if it is `suspended`, `resume` is called. In each case, the text label of the button is updated as appropriate once the promise resolves.

```js
susresBtn.onclick = function () {
  if (audioCtx.state === "running") {
    audioCtx.suspend().then(function () {
      susresBtn.textContent = "Resume context";
    });
  } else if (audioCtx.state === "suspended") {
    audioCtx.resume().then(function () {
      susresBtn.textContent = "Suspend context";
    });
  }
};
```

Specifications
--------------

| Specification |
| --- |
| Web Audio API # dom-audiocontext-suspend |

Browser compatibility
---------------------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.

See also
--------

* Using the Web Audio API (en-US)
* Web Audio API