FileReader.readyState - Web APIs

FileReader.readyState
=====================

提供目前讀取狀態

語法
--
```

var state = instanceOfFileReader.readyState
```

數值
--

數字代表 `FileReader` API 三個可能的狀態：

| Value | State | Description |
| --- | --- | --- |
| `0` | `EMPTY` | Reader has been created. None of the read methods called yet. |
| `1` | `LOADING` | A read method has been called. |
| `2` | `DONE` | The operation is complete. |

規格
--

| Specification |
| --- |
| File API # dom-filereader-readystate |

瀏覽器相容性
------

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

閱讀更多
----

* `Blob`


Event.createEvent() - Web APIs

Event.createEvent()
===================

建立一個新的事件，該事件必須先以其 `init()` method 初始化才行。

### 語法
```

document.createEvent(type)
```

`type`

一個 string 。表示所建立的事件名稱。

這個 method 會回傳一個新的 DOM `Event` object ，其事件類型為傳入的 type 。該事件必須先初始化才能使用。

### 範例

```js
var newEvent = document.createEvent("UIEvents");
```

### 規格定義

* DOM Level 2 Events


Document：scroll事件 - Web APIs

Document：scroll事件
=================

**`scroll`** 事件會在捲動文件的可視區域時觸發。元件（Element）捲動相關請參見`Element: scroll事件` (en-US)。

|  |  |
| --- | --- |
| Bubbles | 是 |
| Cancelable | 否 |
| 介面 | `Event` |
| 事件處理常式的屬性 | `onscroll` (en-US) |

**備註：** iOS 的 UIWebViews 中，`scroll`事件不會在捲動時觸發，而只在捲動結束後觸發。請參見 Bootstrap issue #16202。Safari 和 WKWebViews 不受本 bug 影響。

範例
--

### 限制捲動事件的觸發次數

Since scroll events can fire at a high rate, the event handler shouldn't execute computationally expensive operations such as DOM modifications. Instead, it is recommended to throttle the event using requestAnimationFrame(), setTimeout(), or a CustomEvent, as follows.

Note, however, that input events and animation frames are fired at about the same rate, and therefore the optimization below is often unnecessary. This example optimizes thescroll event for requestAnimationFrame.

有鑒於 `scroll` 事件可能會以很高的速率觸發，事件處理常式不應執行計算密集的操作（如 DOM 修改）。所以，建議改用 `requestAnimationFrame()`、`setTimeout()` (en-US) 或 `CustomEvent` 以調控事件的觸發次數，如下。

不過需要注意，輸入事件和動畫影格的觸發速率大致相同，因此通常沒有必要進行下述的最佳化。本範例最佳化`requestAnimationFrame`的`scroll`事件。

```js
// 參考來源：http://www.html5rocks.com/en/tutorials/speed/animations/

let lastKnownScrollPosition = 0;
let ticking = false;

function doSomething(scrollPos) {
  // 依據捲動位置進行某些操作
}

document.addEventListener("scroll", function (e) {
  lastKnownScrollPosition = window.scrollY;

  if (!ticking) {
    window.requestAnimationFrame(function () {
      doSomething(lastKnownScrollPosition);
      ticking = false;
    });

    ticking = true;
  }
});
```

更多類似範例可見`resize` (en-US)事件頁面。

規格
--

| Specification |
| --- |
| CSSOM View Module # eventdef-document-scroll |
| HTML Standard # handler-onscroll |

瀏覽器相容性
------

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

延伸閱讀
----

* Element: `scroll`事件 (en-US)


Document.forms - Web APIs

Document.forms
==============

`forms` 屬性會回傳一個包含目前頁面中所有 `<form>` (en-US) 元素的集合物件（型別為 `HTMLCollection` (en-US)）。

語法
--
```

collection = document.forms;
```

範例：取得表單資訊
---------

```html
<!doctype html>
<html lang="en">
  <head>
    <title>document.forms example</title>
  </head>

  <body>
    <form id="robby">
      <input
 type="button"
 onclick="alert(document.forms[0].id);"
 value="robby's form" />
    </form>

    <form id="dave">
      <input
 type="button"
 onclick="alert(document.forms[1].id);"
 value="dave's form" />
    </form>

    <form id="paul">
      <input
 type="button"
 onclick="alert(document.forms[2].id);"
 value="paul's form" />
    </form>
  </body>
</html>
```

範例 2：取得表單內的元素
-------------

```js
var selectForm = document.forms[index];
var selectFormElement = document.forms[index].elements[index];
```

規範
--

| Specification |
| --- |
| HTML Standard # dom-document-forms-dev |

參見
--

* `HTMLFormElement` (en-US)


Document.execCommand() - Web APIs

Document.execCommand()
======================

**已棄用:** 不推薦使用此功能。雖可能有一些瀏覽器仍然支援它，但也許已自相關的網頁標準中移除、正準備移除、或僅為了維持相容性而保留。避免使用此功能，盡可能更新現有程式；請參考頁面底部的相容性表格來下決定。請注意：本功能可能隨時停止運作。

當 HTML 文件（document）被切換到 `designMode` 時，它的 `document` 物件就會對外暴露 **`execCommand`** 方法作為操控目前可編輯區域的指令，譬如 form inputs (en-US) 或 `contentEditable` (en-US) 元素。

多數的指令會作用在文件的選取 (en-US) （粗體、斜體等），而其他則像是插入新的元素（新增一個連結）或是影響一整列的文字（縮排）。當使用 `contentEditable` 時， `execCommand()` 會作用在目前活躍的可編輯元素上。

語法
--
```

document.execCommand(aCommandName, aShowDefaultUI, aValueArgument)
```

### 回傳值

如果該指令不被支援或停用將回傳一個 `false` 的 `Boolean` 值。

**備註：** 只有在使用者互動的部分回傳 `true` 。請不要嘗試在呼叫指令前使用回傳值來確認瀏覽器是否支援。

### 參數

`aCommandName`

一個 `DOMString` 作為指定要執行的指令。所有可用的指令列表請見 指令 。

`aShowDefaultUI`

一個 `Boolean` 作為指示是否顯示預設的使用者介面。 Mozilla 並未實作這項功能。

`aValueArgument`

針對需要提供輸入引數的指令，藉由 `DOMString` 提供相關的資訊。譬如， `insertImage` 需要提供圖片的 URL 。若沒有引數的需求則可指定為 `null` 。

### 指令

`backColor`

變更文件的背景色彩。在 `styleWithCss` 模式中，它作用於涵蓋區域的背景色彩。這個指令需要提供一個 `<color>` (en-US) 值字串作為引數值。請留意， Internet Explorer 使用這個指令作為設定文字的背景色彩。

`bold`

切換選取區域插入點的粗體與否。 Internet Explorer 使用 `<strong>` (en-US) 標籤而不是 `<b>` (en-US).

`ClearAuthenticationCache`

清除所有快取中的驗證憑證。

`contentReadOnly`

使內容文件成為唯讀或可編輯。此指令需要提供布林值 true/false 作為引數值。（Internet Explorer 不支援）。

`copy`

複製目前選取的區域到剪貼簿。各個瀏覽器對於這個指令的行為可能有所差異且不斷變更。如果你有使用這個指令的情境，請先查閱相容性表格來決定如何使用。

`createLink`

對選取的區域建立超連結，僅限於有選取內容。需要提供一個 URI 字串值作為超連結的 `href` 。 URI 必須最少包含一個字元且可以是空白字元（Internet Explorer 會建立一個 `null` 值的連結）。

`cut`

移除目前選取的區域並複製到剪貼簿。各個瀏覽器對於這個指令的行為可能有所差異且不斷變更。使用細節請查閱相容性表格。

`decreaseFontSize`

在選取區域或插入點的前後加入一個 `<small>` (en-US) 標籤（ Internet Explorer 不支援）

`defaultParagraphSeparator`

變更可編輯文字區域於新增段落時的段落分隔器。更多細節請查閱 產生 markup 的區別 (en-US)。

`delete`

刪除目前選取的區域。

`enableAbsolutePositionEditor`

啟用或停用用於移動絕對定位元素的抓取器。這個指令在 Firefox 63 Beta/Dev 版本中預設停用（Firefox bug 1449564）。

`enableInlineTableEditing`

啟用或停用表格的列 / 欄的插入及刪除。此指令在 Firefox 63 Beta/Dev 版本中預設停用（Firefox bug 1449564）。

`enableObjectResizing`

啟用或停用圖片、表格、絕對定位元素、其他可重設大小物件的重設大小處理。此指令在 Firefox 63 Beta/Dev 版本中預設停用（Firefox bug 1449564）。

`fontName`

變更選取區域或插入點的字型名稱。此指令需要字型名稱字串（如「Arial」）作為引數值。

`fontSize`

變更選取區域或插入點的字型大小。此指令需要 `1`-`7` 的整數作為引數值。

`foreColor`

變更選取區域或插入點的字型色彩。此指令需要十六進位的色彩字串作為引數值。

`formatBlock`

在目前選取區域的前後加入一個 HTML 區塊層級元素，若選取區域已經存在區塊元素則取代之。（在 Firefox 中， `<blockquote>` 是個例外——它會包裹住任何所包含的區塊元素）。 此指令需要一個標籤名稱字串作為引數值。幾乎所有區塊層級元素都可以使用（Internet Explorer and Edge 僅支援標題標籤 `H1`–`H6` 、 `ADDRESS` 、 `PRE` 且必須由角括號包裹起來，譬如 `"<H1>"` ）。

`forwardDelete`

刪除游標位置後的字元，等同於在 Windows 按下 Delete 鍵盤按鍵。

`heading`

在選取區域或插入點前後加入一個標題元素。此指令需要標籤名稱字串作為引數值（例：`"H1"`, `"H6"` ）（Internet Explorer 及 Safari 不支援）。

`hiliteColor`

變更選取區域或插入點的背景色彩。此指令需要一個色彩字串作為引數值。 `useCSS` 必須為 `true` 才能有作用（Internet Explorer 不支援）。

`increaseFontSize`

在選取區域或插入點前後加入一個 `<big>` (en-US) 標題（Internet Explorer 不支援）。

`indent`

縮排選取區域或插入點所包含的列。在 Firefox ，如果選取的範圍跨越多列且不同的縮排層級，只有選取中最淺層縮排列的才會被縮排。

`insertBrOnReturn`

控制 Enter 按鍵按下時在目前區塊元素中插入 `<br>` 元素或分割成為兩個元素（Internet Explorer 不支援）。

`insertHorizontalRule`

在插入點插入一個 `<hr>` 元素或以它取代選取的內容。

`insertHTML`

在插入點插入一個 HTML 字串（會刪除選取內容）需要一個有效的 HTML 字串作為引數值（Internet Explorer 不支援）。

`insertImage`

在插入點插入一個圖片（會刪除選取內容）。需要一個 URL 字串作為圖片的 `src` 引數值。這個需求跟 `createLink` 的字串是一樣的。

`insertOrderedList`

在選取區域或插入點建立一個有序的清單 (en-US)。

`insertUnorderedList`

在選取區域或插入點建立一個無序的清單 (en-US)。

`insertParagraph`

在選取區域或目前列的前後插入段落 (en-US)（Internet Explorer 會在插入點插入段落並刪除選取的內容）。

`insertText`

在插入點處插入給予的純文字（選取內容將被刪除）。

`italic`

切換選取區域或插入點的斜體開關（Internet Explorer 使用 `<em>` (en-US) 元素而不是 `<i>` (en-US) ）。

`justifyCenter`

置中對齊選取區域或插入點的內容。

`justifyFull`

左右對齊選取區域或插入點的內容。

`justifyLeft`

靠左對齊選取區域或插入點的內容。

`justifyRight`

靠右對齊選取區域或插入點的內容。

`outdent`

凸排選取區域或插入點所包含的列。

`paste`

在目前的插入點貼上剪貼簿的內容（取代目前選取的項目）。網頁內容無法使用。詳閱 [1]。

`redo`

復原上一個取消的指令。

`removeFormat`

移除目前選取區域所有的格式。

`selectAll`

選取可編輯區域的所有內容。

`strikeThrough`

切換選取區域或插入點的刪除線開關。

`subscript`

切換選取區域或插入點的 subscript (en-US) 開關。

`superscript`

切換選取區域或插入點的 superscript (en-US) 開關。

`underline`

切換選取區域或插入點的底線 (en-US)開關。

`undo`

取消上一個執行的指令。

`unlink`

從選取的超連結刪除錨點元素 (en-US)。

`useCSS`
已棄用

針對產生的 markup 使用 HTML 標籤或 CSS。此指令需要一個布林值 true/false 作為引數值。注意：這個引述在邏輯上是反向的（舉例：使用 `false` 會使用 CSS ，反之使用 `true` 則使用 HTML 且 Internet Explorer 不支援。這個指令已經被棄用並由 `styleWithCSS` 取而代之。

`styleWithCSS`

取代 `useCSS` 的指令。 `true` 會在 markup 修改 / 產生 `style` 屬性， false 會產生展示用的元素。

範例
--

一個在 CodePen 上展示如果使用的範例。

規格
--

| 規格 | 狀態 | 備註 |
| --- | --- | --- |
| execCommand |  |  |

瀏覽器相容性
------

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

相關資訊
----

* `HTMLElement.contentEditable` (en-US)
* `document.designMode`
* Rich-Text Editing in Mozilla
* Scribe's "Browser Inconsistencies" documentation with bugs related to `document.execCommand`.


Document.head - Web APIs

Document.head
=============

回傳當前文件的 `<head>` 元素。如果該文件有超過一個 `<head>` 元素，那只會回傳第一個 `<head>` 元素。

語法
--
```

var objRef = document.head;
```

範例
--

```js
// in HTML: <head id="my-document-head">
var aHead = document.head;

alert(aHead.id); // "my-document-head";

alert(document.head === document.querySelector("head")); // true
```

備註
--

`document.head` 是「唯讀」。若是想要將 `document.head` 改成別的值會失敗，這時有些瀏覽器不會告知任何錯誤訊息；有些，例如在 ECMAScript Strict 模式下 (en-US) 的 Gecko 瀏覽器，會給出 `TypeError` (en-US) 異常。

規範
--

| Specification |
| --- |
| HTML Standard # dom-document-head-dev |

瀏覽器相容性
------

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

參見
--

* `document.body` (en-US)


Document.createRange() - Web APIs

Document.createRange()
======================

回傳一 `Range` (en-US) 物件。

語法
--
```

range = document.createRange();
```

`創造 range` 為 `Range` (en-US) 物件.

示例
--

```js
var range = document.createRange();

range.setStart(startNode, startOffset);
range.setEnd(endNode, endOffset);
```

註意
--

當 `Range` 被創建之後，必須先設定其範圍初始點及結束點， 才能使用大部分 `Range` (en-US) 所提供的方法。

規範
--

* DOM Level 2 Range: DocumentRange.createRange


Document.readyState - Web APIs

Document.readyState
===================

`document` (en-US) 的 **`Document.readyState`** 屬性描述文件的讀取狀態。

### 數值

文件的 **readyState** 數值如下：

loading

`document` (en-US) 正在讀取中。

interactive

文件已經完成讀取和解析，但是其他的子資源，如「圖片樣式層次表」，仍然在讀取。這個狀態表示 `DOMContentLoaded` (en-US) 事件已經被觸發。

complete

文件及子資源都完成讀取。這個狀態表示 `load` (en-US) 事件即將被觸發。

當這個屬性的數值改變時， `readystatechange` (en-US) 事件在 `document` (en-US) 上觸發。

表達式
---

```js
var string = document.readyState;
```

範例
--

### 不同的狀態

```js
switch (document.readyState) {
  case "loading":
    // 文件讀取中。
    break;
  case "interactive":
    // 文件已經完成讀取。可以使用 DOM 元素。
    var span = document.createElement("span");
    span.textContent = "A <span> element.";
    document.body.appendChild(span);
    break;
  case "complete":
    // 頁面已經完成讀取。
    console.log(
      "The first CSS rule is: " + document.styleSheets[0].cssRules[0].cssText,
    );
    break;
}
```

### readystatechange 替代 DOMContentLoaded

```js
// alternative to DOMContentLoaded event
document.onreadystatechange = function () {
  if (document.readyState == "interactive") {
    initApplication();
  }
};
```

### readystatechange 替代 load

```js
// alternative to load event
document.onreadystatechange = function () {
  if (document.readyState == "complete") {
    initApplication();
  }
};
```

規範
--

| Specification |
| --- |
| HTML Standard # current-document-readiness |

參見
--

* 相關事件：
    + `readystatechange` (en-US)
    + `DOMContentLoaded` (en-US)
    + `load` (en-US)


Document.createElement() - Web APIs

Document.createElement()
========================

於 HTML 文件中，**`Document.createElement()`** 方法可以依指定的標籤名稱（`tagName`）建立 HTML 元素，或是在未定義標籤名稱下建立一個 `HTMLUnknownElement` (en-US)。在 XUL 文件中，`Document.createElement()` 將會建立指定的 XUL 元素。而在其它文件，則會建立一個 namespace URI 為 `null` 的元素。

若要明確指定元素的 namespace URI，請使用 `document.createElementNS()` (en-US)。

語法
--

```js
var element = document.createElement(tagName[, options]);
```

### 參數

`tagName`

一個指定類型給所創建的元素的字串。`nodeName` (en-US) 創建的元素由 `tagName` 的值初始，不要使用吻合名稱（例如 "html:a"）。當該方法在 HTML 文件中被調用時，`createElement()` 會先將 `tagName` 轉化為小寫後再創建元素。在 Firefox、Opera 和 Chrome，`createElement(null)` 與 `createElement("null")` 作用相同。

`options`選擇性

選擇性 `ElementCreationOptions` 物件包含一個屬性 `is`，它的值是先前使用`customElements.define()` 所定義的自定義元素的標籤名稱。為了與以前的 自定義元素規範 相容，一些瀏覽器將允許你在此傳遞一個字串而非物件，其字串的值就是自定義元件的標籤名稱。了解更多訊息以及如何使用此參數，可以參閱 擴展原生 HTML 元素。新元素將被賦予一個 `is` 屬性，其值就是自定義元素的標籤名稱。自定義元素算是實驗中的功能，因此目前只作用於部分瀏覽器中。

### 回傳值

一個新的 `Element` (en-US).

範例
--

這邊創建一個新的 `<div>` ，並將它插入到 ID `div1` 之前。

### HTML

```html
<!doctype html>
<html>
  <head>
    <title>||Working with elements||</title>
  </head>
  <body>
    <div id="div1">The text above has been created dynamically.</div>
  </body>
</html>
```

### JavaScript

```js
document.body.onload = addElement;

function addElement() {
  // create a new div element
  // and give it some content
  var newDiv = document.createElement("div");
  var newContent = document.createTextNode("Hi there and greetings!");
  newDiv.appendChild(newContent); //add the text node to the newly created div.

  // add the newly created element and its content into the DOM
  var currentDiv = document.getElementById("div1");
  document.body.insertBefore(newDiv, currentDiv);
}
```

規範
--

| Specification |
| --- |
| DOM Standard # ref-for-dom-document-createelement① |

瀏覽器相容性
------

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

參見
--

* `Node.removeChild()`
* `Node.replaceChild()` (en-US)
* `Node.appendChild()` (en-US)
* `Node.insertBefore()` (en-US)
* `Node.hasChildNodes()` (en-US)


Document.defaultView - Web APIs

Document.defaultView
====================

在瀏覽器中，**`document.defaultView`** 屬性會指向一個目前 document 所屬的 `window` (en-US) 物件，若無則為 `null`。

語法
--
```

var win = document.defaultView;
```

此為唯讀屬性。

規範
--

| Specification |
| --- |
| HTML Standard # dom-document-defaultview-dev |

瀏覽器相容性
------

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


Document.designMode - Web APIs

Document.designMode
===================

概要
--

`document.designMode` 控制整個文件是否能夠編輯。可用的數值是 `"on"` 和 `"off"`。根據規範，這個屬性預設值為 `"off"`。Firefox 遵從這個標準。較早以前的 Chrome 和 IE 預設值是 `"inherit"`。從 Chrome 43 起，預設值是 「`off`」；不再支援「`inherit`」。在 IE6-10 中，數值為大寫英文字母。

語法
--

```js
var mode = document.designMode;
document.designMode = "on";
document.designMode = "off";
```

範例
--

讓 `<iframe>` (en-US) 的文件可以給使用者編輯：
```

iframeNode.contentDocument.designMode = "on";
```

規範
--

| Specification |
| --- |
| HTML Standard # dom-document-designmode-dev |

瀏覽器相容性
------

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

參考
--

* Rich-Text Editing in Mozilla
* `HTMLElement.contentEditable` (en-US)
* designMode property on MSDN


document.querySelector - Web APIs

document.querySelector
======================

摘要
--

回傳 document 第一個符合特定選擇器群組的元素（採用深度優先，前序追蹤 document 節點）。

語法
--
```

element = document.querySelector(selectors);
```

其中

* `element` 是元素 (en-US)物件。
* `selectors` 是以逗號分隔，包含一個或多個 CSS 選擇器的字串。
範例
--

這個範例會回傳 document 選到的第一個 "`myclass`" class：

```js
var el = document.querySelector(".myclass");
```

注意事項
----

若找不到相應元素就會回傳 `null`，否則回傳第一個符合的元素。

若選擇器符合某 ID，且該 ID 在 document 中誤用數次，就會回傳第一個符合的元素。

當特定選擇器群組無效，會擲回 `SYNTAX_ERR` 例外狀況。

`querySelector()` 是由 Selectors API 引入的選擇器。

傳入 `querySelector` 的字串參數必須遵循 CSS 語法。若要選取未遵循 CSS 語法的 ID 或選擇器（例如不當使用冒號或空格），必須強制加上兩個反斜線來跳脫錯誤的字元：

```html
<div id="foo\bar"></div>
<div id="foo:bar"></div>

<script>
 document.querySelector("#foo\bar"); // 甚麼都沒選到
 document.querySelector("#foo\\\\bar"); // 選到第一個 div
 document.querySelector("#foo:bar"); // 甚麼都沒選到
 document.querySelector("#foo\\:bar"); // 選到第二個 div
</script>
```

瀏覽器相容性
------

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

規格文件
----

* Selectors API
詳見
--

* `document.querySelectorAll()` (en-US)
* `element.querySelector()` (en-US)
* `element.querySelectorAll()` (en-US)
* querySelector 代碼片段


Document.getElementsByClassName() - Web APIs

Document.getElementsByClassName()
=================================

針對所有給定的 class 子元素，回傳類似陣列的物件。當呼叫 document 物件時，它會搜尋整個文件，包括根節點在內。你也可以在所有元素呼叫 `getElementsByClassName()` (en-US)，那它就只會回傳含有給定 class 的特定根元素的後代元素。

表達式
---
```

var elements = document.getElementsByClassName(names); // or:
var elements = rootElement.getElementsByClassName(names);
```

* *elements* 為符合 class 名稱的 `HTMLCollection` (en-US)。
* *names* 為符合 class 名稱的字串；class 名稱可以用空白分隔。
* getElementsByClassName 可以被任何不只在 document 的元素呼叫。呼叫這個方法的元素將會成為搜尋 class 的根元素。
範例
--

取得所有 class 為 「test」 的元素：

```js
document.getElementsByClassName("test");
```

取得所有 class 為 「test」 和 「red」 的元素：

```js
document.getElementsByClassName("red test");
```

取得所有在 id 為 '「main」 的元素裡 class 為 「test」 的元素：

```js
document.getElementById("main").getElementsByClassName("test");
```

我們也可以藉由傳遞 `HTMLCollection` (en-US) 為 \*this \*來使用 `Array.prototype` 的方法。下面的例子將會找到所有 class 為 「test」 的 div 元素：

```js
var testElements = document.getElementsByClassName("test");
var testDivs = Array.prototype.filter.call(
  testElements,
  function (testElement) {
    return testElement.nodeName === "DIV";
  },
);
```

取得 class 是 test 的元素
-------------------

這是最常用的操作方法：
```

<!doctype html>
<html>
<head>
    <meta charset="UTF-8">
    <title>Document</title>
</head>
<body>
    <div id="parent-id">
        <p>hello word1</p>
        <p class="test">hello word2</p>
        <p>hello word3</p>
        <p>hello word4</p>
    </div>
    <script>
        var parentDOM = document.getElementById("parent-id");

        var test=parentDOM.getElementsByClassName("test");//test is not target element
        console.log(test);//HTMLCollection[1]

        var testTarget=parentDOM.getElementsByClassName("test")[0];//year , this element is target
        console.log(testTarget);//<p class="test">hello word2</p>
    </script>
</body>
</html>
```

瀏覽器相容性
------

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

規範
--

* W3C: getElementsByClassName


Document.documentElement - Web APIs

Document.documentElement
========================

**`Document.documentElement`** 會回傳目前文件（`document` (en-US)）中的根元素（`Element` (en-US)），如：HTML 文件中的 `<html>` 元素。

語法
--
```

var element = document.documentElement;
```

範例
--

```js
var rootElement = document.documentElement;
var firstTier = rootElement.childNodes;

// firstTier is the NodeList of the direct children of the root element
for (var i = 0; i < firstTier.length; i++) {
  // do something with each direct kid of the root element
  // as firstTier[i]
}
```

備註
--

對於所有非空的 HTML 文件， `document.documentElement` 將會是一個 `<html>` (en-US) 元素 ; 對於所有非空的 XML 文件，`document.documentElement` 則會是文件的根元素。

規範
--

| Specification |
| --- |
| DOM Standard # ref-for-dom-document-documentelement① |

瀏覽器相容性
------

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


Document.createTextNode() - Web APIs

Document.createTextNode()
=========================

創建一個新的文字節點.

Syntax
------
```

var text = document.createTextNode(data);
```

* 文字屬於一個文字節點.
* 將含有數據的串加入文字節點中.
Example
-------

```js
<!DOCTYPE html>
<html lang="en">
<head>
<title>createTextNode example</title>
<script>
function addTextNode(text) {
  var newtext = document.createTextNode(text),
      p1 = document.getElementById("p1");

  p1.appendChild(newtext);
}
</script>
</head>

<body>
  <button onclick="addTextNode('YES! ');">YES!</button>
  <button onclick="addTextNode('NO! ');">NO!</button>
  <button onclick="addTextNode('WE CAN! ');">WE CAN!</button>

  <hr />

  <p id="p1">First line of paragraph.</p>
</body>
</html>
```

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

| Specification |
| --- |
| DOM Standard # ref-for-dom-document-createtextnode① |

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

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


Document.createDocumentFragment() - Web APIs

Document.createDocumentFragment()
=================================

建立新的 `DocumentFragment`.

語法
--
```

var fragment = document.createDocumentFragment();
```

`fragment` 是 `DocumentFragment` 的一個參考物件。

描述
--

`DocumentFragment`s 是 DOM 節點(Nodes)。他們不會成為 DOM 主幹的一部份。最常見的作法是先建立文本片段 (document fragment)，然後將元素 (element) 加入文本片段中，最後再將文本片段加入 DOM 樹中。在 DOM 樹中，文本片段將會被他所有的子元素取代。

正因為文本片段是存在**記憶體**中，並且不是 DOM 主幹的一部分，增加子元素並不會導致網頁重刷 (reflow)(重新計算元素的位置和幾何)。因此採用文本片段通常會有比較好的效能表現 (better performance)。

舉例
--

這個例子中用清單來呈現主流瀏覽器。

### HTML

```html
<ul id="ul"></ul>
```

### JavaScript

```js
var element = document.getElementById("ul"); // assuming ul exists
var fragment = document.createDocumentFragment();
var browsers = ["Firefox", "Chrome", "Opera", "Safari", "Internet Explorer"];

browsers.forEach(function (browser) {
  var li = document.createElement("li");
  li.textContent = browser;
  fragment.appendChild(li);
});

element.appendChild(fragment);
```

### 結果

在 jsfiddle 上看範例結果。

規格
--

| Specification |
| --- |
| DOM Standard # ref-for-dom-document-createdocumentfragment① |

瀏覽器相容性
------

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

更多參考
----

* `document.implementation.createDocument()` (en-US)
* `documentFragment`


使用 web 和 XML 開發來使用 DOM - Web APIs

使用 web 和 XML 開發來使用 DOM
======================

本章介紹了使用 DOM 進行 Web 以及 XML 開發的一些長範例。只要可能，在例子就會使用通用的 JavaScript Web API 、技巧以及模式來操作文檔對象（the document object）。

範例一：高度和寬度
---------

下面的例子展示了在不同尺寸的圖片時使用其高（height）和寬（width）屬性的情況：

```html
<!doctype html>
<html lang="en">
  <head>
    <title>width/height example</title>
    <script>
 function init() {
 var arrImages = new Array(3);

 arrImages[0] = document.getElementById("image1");
 arrImages[1] = document.getElementById("image2");
 arrImages[2] = document.getElementById("image3");

 var objOutput = document.getElementById("output");
 var strHtml = "<ul>";

 for (var i = 0; i < arrImages.length; i++) {
 strHtml +=
 "<li>image" +
 (i + 1) +
 ": height=" +
 arrImages[i].height +
 ", width=" +
 arrImages[i].width +
 ", style.height=" +
 arrImages[i].style.height +
 ", style.width=" +
 arrImages[i].style.width +
 "<\/li>";
 }

 strHtml += "<\/ul>";

 objOutput.innerHTML = strHtml;
 }
 </script>
  </head>
  <body onload="init();">
    <p>
      Image 1: no height, width, or style
      <img id="image1" src="http://www.mozilla.org/images/mozilla-banner.gif" />
    </p>

    <p>
      Image 2: height="50", width="500", but no style
      <img
 id="image2"
 src="http://www.mozilla.org/images/mozilla-banner.gif"
 height="50"
 width="500" />
    </p>

    <p>
      Image 3: no height, width, but style="height: 50px; width: 500px;"
      <img
 id="image3"
 src="http://www.mozilla.org/images/mozilla-banner.gif"
 style="height: 50px; width: 500px;" />
    </p>

    <div id="output"></div>
  </body>
</html>
```

範例二：圖片屬性
--------

```html
<!doctype html>
<html lang="en">
  <head>
    <title>Modifying an image border</title>

    <script>
 function setBorderWidth(width) {
 document.getElementById("img1").style.borderWidth = width + "px";
 }
 </script>
  </head>

  <body>
    <p>
      <img
 id="img1"
 src="image1.gif"
 style="border: 5px solid green;"
 width="100"
 height="100"
 alt="border test" />
    </p>

    <form name="FormName">
      <input
 type="button"
 value="Make border 20px-wide"
 onclick="setBorderWidth(20);" />
      <input
 type="button"
 value="Make border 5px-wide"
 onclick="setBorderWidth(5);" />
    </form>
  </body>
</html>
```

範例三：改變樣式（style）
---------------

在下面這個簡單的例子中，透過取得的 DOM 元素中的 style 物件和 style 物件中的屬性，我們可以取得 HTML 段落中的一些基本樣式屬性。在本例中，你可以直接操控各別的樣式屬性。在下一個的例子裡（見例 4），你可以使用 stylesheets 和它的規則來改變整個文檔的樣式。

```html
<!doctype html>
<html lang="en">
  <head>
    <title>Changing color and font-size example</title>

    <script>
 function changeText() {
 var p = document.getElementById("pid");

 p.style.color = "blue";
 p.style.fontSize = "18pt";
 }
 </script>
  </head>
  <body>
    <p id="pid" onclick="window.location.href = 'http://www.cnn.com/';">
      linker
    </p>

    <form>
      <p><input value="rec" type="button" onclick="changeText();" /></p>
    </form>
  </body>
</html>
```

範例四：使用樣式表（stylesheets）
----------------------

在文檔物件的 styleSheets 屬性會回傳一系列載入文檔中的樣式表（stylesheets）。你可以透過 styleSheets、style 和 CSSRule 物件來獲取樣式表和每條樣式規則。在下面的例子中，把所有的樣式規則中的選擇器文本（字符串）打印到控制台中。

```js
var ss = document.styleSheets;

for (var i = 0; i < ss.length; i++) {
  for (var j = 0; j < ss[i].cssRules.length; j++) {
    dump(ss[i].cssRules[j].selectorText + "\n");
  }
}
```

下面是一個只定義了三條樣式規則的單一樣式表的文檔：

```css
body {
  background-color: darkblue;
}
p {
  font-face: Arial;
  font-size: 10pt;
  margin-left: 0.125in;
}
#lumpy {
  display: none;
}
```

該腳本會輸出如下的結果：
```

BODY
P
#LUMPY
```

範例五：事件傳遞
--------

本例以一種簡單的方法闡述了事件是如何觸發以及在 DOM 中是如何處理的。當這個 HTML 文檔 BODY 載入的時候，在表格的首行註冊了一個事件監聽器。事件監聽器透過執行函數 stopEvent 來處理事件，從而改變在該表格底部的值。

然而，stopEvent 同時也會另外執行一個事件物件的方法`event.stopPropagation`，這會使得該事件（點擊）無法在 DOM 中有進一步的冒泡行為。請注意，表格本身有一個 `onclick` 事件，使得這個表格被點擊時的時候原本應該要會跳出一個訊息。但因為 stopEvent 方法已經阻止了冒泡，所以在表格中的數據更新後，該事件階段有效地結束，表格的點擊事件沒有被觸發，而是顯示一個警告框（event propagation halted）——證實了事件被有效結束而沒有進一步冒泡。

```html
<!doctype html>
<html lang="en">
  <head>
    <title>Event Propagation</title>

    <style>
 #t-daddy {
 border: 1px solid red;
 }
 #c1 {
 background-color: pink;
 }
 </style>

    <script>
 function stopEvent(ev) {
 c2 = document.getElementById("c2");
 c2.innerHTML = "hello";

 // this ought to keep t-daddy from getting the click.
 ev.stopPropagation();
 alert("event propagation halted.");
 }

 function load() {
 elem = document.getElementById("tbl1");
 elem.addEventListener("click", stopEvent, false);
 }
 </script>
  </head>

  <body onload="load();">
    <table id="t-daddy" onclick="alert('hi');">
      <tr id="tbl1">
        <td id="c1">one</td>
      </tr>
      <tr>
        <td id="c2">two</td>
      </tr>
    </table>
  </body>
</html>
```

Example 6: getComputedStyle
---------------------------

這個例子演示了如何用 `window.getComputedStyle` (en-US) 方法來獲取一個尚未被透過樣式元素或 JavaScript 設定的元素樣式（例如，elt.style.backgroundColor="RGB(173，216，230)"）。列舉在 `elt.style` 後面的類型的樣式可以用更直接`elt.style` 屬性獲取。

`getComputedStyle() 返回了一個 ComputedCSSStyleDeclaration 物件，其獨立的樣式屬性可以用該物件的 getPropertyValue() 方法引用，如同下面的例子一樣：`

```html
<!doctype html>
<html lang="en">
  <head>
    <title>getComputedStyle example</title>

    <script>
 function cStyles() {
 var RefDiv = document.getElementById("d1");
 var txtHeight = document.getElementById("t1");
 var h\_style = document.defaultView
 .getComputedStyle(RefDiv, null)
 .getPropertyValue("height");

 txtHeight.value = h\_style;

 var txtWidth = document.getElementById("t2");
 var w\_style = document.defaultView
 .getComputedStyle(RefDiv, null)
 .getPropertyValue("width");

 txtWidth.value = w\_style;

 var txtBackgroundColor = document.getElementById("t3");
 var b\_style = document.defaultView
 .getComputedStyle(RefDiv, null)
 .getPropertyValue("background-color");

 txtBackgroundColor.value = b\_style;
 }
 </script>

    <style>
 #d1 {
 margin-left: 10px;
 background-color: rgb(173, 216, 230);
 height: 20px;
 max-width: 20px;
 }
 </style>
  </head>

  <body>
    <div id="d1"> </div>

    <form action="">
      <p>
        <button type="button" onclick="cStyles();">getComputedStyle</button>
        height<input id="t1" type="text" value="1" /> max-width<input
 id="t2"
 type="text"
 value="2" />
        bg-color<input id="t3" type="text" value="3" />
      </p>
    </form>
  </body>
</html>
```

範例七：顯示事件物件的屬性
-------------

這個例子使用 DOM 方法來顯示所有 `window.onload` (en-US) `event` 物件的屬性及其在 table 中的值。這個方法也展示一個有用的技術，使用 for...in 迴圈來來遍歷一個物件的屬性，以得到它們的值。

不同瀏覽器之間事件物件的屬性有很大不同，WHATWG DOM Standard 規範了事件的標準屬性，然而，許多瀏覽器都大大擴展了這些。

將下面的代碼放到一個空白的文本文件，並將其用各種瀏覽器開啟，你一定會對各種瀏覽器之間的不一致（事件屬性的名稱及其數量）感到驚訝。你可能還喜歡在這個頁面加入一些元素，並呼叫不同的事件處理函數（event handlers）。

```html
<!doctype html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <title>Show Event properties</title>

    <style>
 table {
 border-collapse: collapse;
 }
 thead {
 font-weight: bold;
 }
 td {
 padding: 2px 10px 2px 10px;
 }

 .odd {
 background-color: #efdfef;
 }
 .even {
 background-color: #ffffff;
 }
 </style>

    <script>
 function showEventProperties(e) {
 function addCell(row, text) {
 var cell = row.insertCell(-1);
 cell.appendChild(document.createTextNode(text));
 }

 var e = e || window.event;
 document.getElementById("eventType").innerHTML = e.type;

 var table = document.createElement("table");
 var thead = table.createTHead();
 var row = thead.insertRow(-1);
 var lableList = ["#", "Property", "Value"];
 var len = lableList.length;

 for (var i = 0; i < len; i++) {
 addCell(row, lableList[i]);
 }

 var tbody = document.createElement("tbody");
 table.appendChild(tbody);

 for (var p in e) {
 row = tbody.insertRow(-1);
 row.className = row.rowIndex % 2 ? "odd" : "even";
 addCell(row, row.rowIndex);
 addCell(row, p);
 addCell(row, e[p]);
 }

 document.body.appendChild(table);
 }

 window.onload = function (event) {
 showEventProperties(event);
 };
 </script>
  </head>

  <body>
    <h1>Properties of the DOM <span id="eventType"></span> Event Object</h1>
  </body>
</html>
```

範例八：使用 DOM Table 介面
-------------------

DOM HTMLTableElement 介面提供了一些方便的方法用於創建和操作資料表。兩種常用的方法是`HTMLTableElement.insertRow` (en-US)和`tableRow.insertCell`.。

增加一行和一些細格到現有的資料表：

```html
<table id="table0">
  <tr>
    <td>Row 0 Cell 0</td>
    <td>Row 0 Cell 1</td>
  </tr>
</table>

<script>
 var table = document.getElementById("table0");
 var row = table.insertRow(-1);
 var cell, text;

 for (var i = 0; i < 2; i++) {
 cell = row.insertCell(-1);
 text = "Row " + row.rowIndex + " Cell " + i;
 cell.appendChild(document.createTextNode(text));
 }
</script>
```

### 提醒

* 表格的`innerHTML` (en-US)屬性絕不應該被用來修改表，雖然你可以用它來寫一個完整的表格或細格中的內容。
* 如果用 DOM 核心方法 `document.createElement` 和 `Node.appendChild` (en-US) 來建立表格的行和細格，IE 會要求它們附加到一個 tbody 元素，而其它瀏覽器允許它們附加到一個 table 元素（行會被添加到最後的 tbody 元素）。
* 表格介面 (en-US)還有一些可用於創建和修改的表格的便利方法。


DOM 中的空白字元 - Web APIs

DOM 中的空白字元
==========

#### 問題說明

DOM 裡的空白字元會讓處理節點結構時增加不少麻煩。Mozilla 相關軟體中，原始文件裡所有空白字元都會在 DOM 中出現（不包括標籤內含的空白字元）。這樣的處理方式有其必要，一方面編輯器中可逕行排列文字、二方面 CSS 裡的 `white-space: pre` 也才能發揮作用。 如此一來就表示：

* 有些空白字元會自成一個文字節點。
* 有些空白字元會與其他字串合成一個文字節點。

換句話說，下面這段程式碼的 DOM 節點結構就如附圖一般，其中「\n」代表換行字元：

```html
<!-- My document -->
<html>
  <head>
    <title>My Document</title>
  </head>
  <body>
    <h1>Header</h1>
    <p>Paragraph</p>
  </body>
</html>
```

![dom tree equivalent of the above HTML example](/zh-TW/docs/Web/API/Document_Object_Model/Whitespace/dom-string.png)

這麼一來，要使用 DOM 遊走於節點結構間又不想要無用的空白字元時，會有點困難。

#### 助你一臂之力

以下的 JavaScript 程式碼定義了許多函式，讓你處理 DOM 中的空白字元時能輕鬆點：

```js
/\*\*
 \* 以下所謂的「空白字元」代表：
 \* "\t" TAB \u0009 （移位字元）
 \* "\n" LF \u000A （換行字元）
 \* "\r" CR \u000D （歸位字元）
 \* " " SPC \u0020 （真正的空白）
 \*
 \* 不包括 JavaScript 的「\s」，因為那代表如不斷行字元等其他字元。
 \*/

/\*\*
 \* 測知某節點的文字內容是否全為空白。
 \*
 \* @參數 nod |CharacterData| 類的節點（如 |Text|、|Comment| 或 |CDATASection|）。
 \* @傳回值 若 |nod| 的文字內容全為空白則傳回 true，否則傳回 false。
 \*/
function is\_all\_ws(nod) {
  // Use ECMA-262 Edition 3 String and RegExp features
  return !/[^\t\n\r ]/.test(nod.data);
}

/\*\*
 \* 測知是否該略過某節點。
 \*
 \* @參數 nod DOM1 |Node| 物件
 \* @傳回值 若 |Text| 節點內僅有空白字元或為 |Comment| 節點時，傳回 true，
 \* 否則傳回 false。
 \*/

function is\_ignorable(nod) {
  return (
    nod.nodeType == 8 || // 註解節點
    (nod.nodeType == 3 && is\_all\_ws(nod))
  ); // 僅含空白字元的文字節點
}

/\*\*
 \* 此為會跳過空白字元節點及註解節點的 |previousSibling| 函式
 \* （ |previousSibling| 是 DOM 節點的特性值，為該節點的前一個節點。）
 \*
 \* @參數 sib 節點。
 \* @傳回值 有兩種可能：
 \* 1) |sib| 的前一個「非空白、非註解」節點（由 |is\_ignorable| 測知。）
 \* 2) 若該節點前無任何此類節點，則傳回 null。
 \*/
function node\_before(sib) {
  while ((sib = sib.previousSibling)) {
    if (!is\_ignorable(sib)) return sib;
  }
  return null;
}

/\*\*
 \* 此為會跳過空白字元節點及註解節點的 |nextSibling| 函式
 \*
 \* @參數 sib 節點。
 \* @傳回值 有兩種可能：
 \* 1) |sib| 的下一個「非空白、非註解」節點。
 \* 2) 若該節點後無任何此類節點，則傳回 null。
 \*/
function node\_after(sib) {
  while ((sib = sib.nextSibling)) {
    if (!is\_ignorable(sib)) return sib;
  }
  return null;
}

/\*\*
 \* 此為會跳過空白字元節點及註解節點的 |lastChild| 函式
 \* （ lastChild| 是 DOM 節點的特性值，為該節點之中最後一個子節點。）
 \*
 \* @參數 par 節點。
 \* @傳回值 有兩種可能：
 \* 1) |par| 中最後一個「非空白、非註解」節點。
 \* 2) 若該節點中無任何此類子節點，則傳回 null。
 \*/
function last\_child(par) {
  var res = par.lastChild;
  while (res) {
    if (!is\_ignorable(res)) return res;
    res = res.previousSibling;
  }
  return null;
}

/\*\*
 \* 此為會跳過空白字元節點及註解節點的 |firstChild| 函式
 \*
 \* @參數 par 節點。
 \* @傳回值 有兩種可能：
 \* 1) |par| 中第一個「非空白、非註解」節點。
 \* 2) 若該節點中無任何此類子節點，則傳回 null。
 \*/
function first\_child(par) {
  var res = par.firstChild;
  while (res) {
    if (!is\_ignorable(res)) return res;
    res = res.nextSibling;
  }
  return null;
}

/\*\*
 \* 此為傳回值不包含文字節點資料的首尾所有空白字元、
 \* 並將兩個以上的空白字元縮減為一個的 |data| 函式。
 \*（ data 是 DOM 文字節點的特性值，為該文字節點中的資料。）
 \*
 \* @參數 txt 欲傳回其中資料的文字節點
 \* @傳回值 文字節點的內容，其中空白字元已依前述方式處理。
 \*/
function data\_of(txt) {
  var data = txt.data;
  // Use ECMA-262 Edition 3 String and RegExp features
  data = data.replace(/[\t\n\r ]+/g, " ");
  if (data.charAt(0) == " ") data = data.substring(1, data.length);
  if (data.charAt(data.length - 1) == " ")
    data = data.substring(0, data.length - 1);
  return data;
}
```

#### 範例

以下示範上述函式的應用方法，在節點結構中依序檢查、找出內容為「`"This is the third paragraph"`」的節點，並修改其 class 屬性及文字內容。

```js
var cur = first\_child(document.getElementById("test"));
while (cur) {
  if (data\_of(cur.firstChild) == "This is the third paragraph.") {
    cur.className = "magic";
    cur.firstChild.data = "This is the magic paragraph.";
  }
  cur = node\_after(cur);
}
```


CustomEvent() - Web APIs

CustomEvent()
=============

`CustomEvent()` constructor 可用來建立 `CustomEvent`物件。

語法
--

```javascript
new CustomEvent(type);
new CustomEvent(type, options);
```

### 參數

`type`

一個 `DOMString` 用來表示事件名稱。

`options`選擇性

一個繼承自`Event()`的參數，其類型為 object。它有以下參數

`detail` 選擇性

 用來表示事件相關的資訊。它能藉由 `CustomEvent.detail` (en-US) 屬性來取得值。
 其默認值為`null`。

回傳值
---

一個 `CustomEvent` 物件。

範例
--

```javascript
// create custom events
const catFound = new CustomEvent("animalfound", {
  detail: {
    name: "cat",
  },
});
const dogFound = new CustomEvent("animalfound", {
  detail: {
    name: "dog",
  },
});
// add an appropriate event listener
obj.addEventListener("animalfound", (e) => console.log(e.detail.name));
// dispatch the events
obj.dispatchEvent(catFound);
obj.dispatchEvent(dogFound);
// "cat" and "dog" logged in the console
```

可於 Creating and triggering events 找到更多範例。

規格
--

| Specification |
| --- |
| DOM Standard # ref-for-dom-customevent-customevent |

瀏覽器支援度
------

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

其他
--

### 添加額外參數

 在 Internet Explorer 9 或更高的版本，你可以用以下的方法給
 `CustomEvent()` constructor 添加額外參數

```javascript
(function () {
  function CustomEvent(event, params) {
    params = params || { bubbles: false, cancelable: false, detail: undefined };
    var evt = document.createEvent("CustomEvent");
    evt.initCustomEvent(
      event,
      params.bubbles,
      params.cancelable,
      params.detail,
    );
    return evt;
  }

  CustomEvent.prototype = window.Event.prototype;

  window.CustomEvent = CustomEvent;
})();
```

延伸閱讀
----

* `CustomEvent`
* Creating and triggering events


AnalyserNode.getByteFrequencyData() - Web APIs

AnalyserNode.getByteFrequencyData()
===================================

`AnalyserNode` (en-US) 介面的 **`getByteFrequencyData()`** 方法會將當前的頻率資料複製到 `Uint8Array` (en-US) （無號 byte 陣列）。

如果陣列的元素數目比 `AnalyserNode.frequencyBinCount` (en-US) 少的話，多餘的元素會被 drop 掉。如果比需要的少的話，多餘的元素會被忽略。

語法
--

```js
var audioCtx = new AudioContext();
var analyser = audioCtx.createAnalyser();
var dataArray = new Uint8Array(analyser.frequencyBinCount); // Uint8Array 應該要和 frequencyBinCount 等長
analyser.getByteFrequencyData(dataArray); // 將 getByteFrequencyData() 回傳的資料放進 Uint8Array
```

### 回傳值

`Uint8Array` (en-US)。

範例
--

下面的範例顯示出 `AudioContext` (en-US) 用於建立一個 `AnalyserNode` 的基本用法，然後 `requestAnimationFrame` 以及 `<canvas>` 用來重複收集當前聲音輸入的頻率資料並在 "winamp bargraph style" 畫出輸出。若要知道更完整的範例或是資訊，請參考 Voice-change-O-matic demo （看 app.js 第 128–205 行 會有相關的程式碼）。

```js
var audioCtx = new (window.AudioContext || window.webkitAudioContext)();
var analyser = audioCtx.createAnalyser();

  ...

analyser.fftSize = 256;
var bufferLength = analyser.frequencyBinCount;
console.log(bufferLength);
var dataArray = new Uint8Array(bufferLength);

canvasCtx.clearRect(0, 0, WIDTH, HEIGHT);

function draw() {
  drawVisual = requestAnimationFrame(draw);

  analyser.getByteFrequencyData(dataArray);

  canvasCtx.fillStyle = 'rgb(0, 0, 0)';
  canvasCtx.fillRect(0, 0, WIDTH, HEIGHT);

  var barWidth = (WIDTH / bufferLength) \* 2.5;
  var barHeight;
  var x = 0;

  for(var i = 0; i < bufferLength; i++) {
    barHeight = dataArray[i];

    canvasCtx.fillStyle = 'rgb(' + (barHeight+100) + ',50,50)';
    canvasCtx.fillRect(x,HEIGHT-barHeight/2,barWidth,barHeight/2);

    x += barWidth + 1;
  }
};

draw();
```

參數
--

陣列

頻率定義域會被複製進去的 `Uint8Array` (en-US) 。

規範
--

| Specification |
| --- |
| Web Audio API # dom-analysernode-getbytefrequencydata |

瀏覽器相容性
------

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

參看
--

* 使用 Web Audio API (en-US)


Window.requestAnimationFrame() - Web APIs

Window.requestAnimationFrame()
==============================

\*\*`window.requestAnimationFrame()`\*\*方法通知瀏覽器我們想要產生動畫，並且要求瀏覽器在下次重繪畫面前呼叫特定函數更新動畫。這個方法接受一個引數作為下次重繪前調用的回呼函數。

**備註：** 若是想要在下次重繪時產生另一個動畫，這個回呼函數內必須自行呼叫 requestAnimationFrame()。

當準備好更新頁面上的動畫時應當呼叫這個方法。這表示向瀏覽器請求在下次重繪前呼叫這個動畫函數。回呼的次數通常落在每秒 60 次，但通常會根據 W3C 的建議符合多數瀏覽器重新整理的頻率。當頁面處於背景或隱藏狀態時 `<iframe>` (en-US) ，多數的瀏覽器會暫停 `requestAnimationFrame()` 的呼叫，從而改善效能表現及電池壽命。

回呼方法會得到一個 `DOMHighResTimeStamp` (en-US) 的引數，作為指示目前的時間（基於 time origin (en-US) 之後經過的毫秒數）。當 `requestAnimationFrame()` 佇列了數個回呼並且在同一幀開始觸發多個回呼時，儘管每一個之前的回呼在運作時會消耗一定的時間，但它們都會取得同樣的時間戳記。時間戳記是由十進位數字表示的毫秒且最小的精準度為 1 毫秒（1000 µs）。

語法
--
```

window.requestAnimationFrame(callback);
```

### 參數

`回呼`

當下次重新繪製時用於呼叫並更新動畫。回呼函數會得到一個引數—— `DOMHighResTimeStamp` (en-US) ——類似於由 `performance.now()` (en-US) 回傳的結果，其用於指示當 `requestAnimationFrame()` 開始執行回呼函數的時間。

### 回傳值

`long` 型別的整數值表示請求的 id，作為其進入回呼清單中的唯一識別。雖然回傳值是一個非零值，但不應該對其有其他任何的假設。將這個值傳遞給 `window.cancelAnimationFrame()` (en-US) 可以取消重新整理頁面回呼的請求

範例
--

```js
var start = null;
var element = document.getElementById("SomeElementYouWantToAnimate");

function step(timestamp) {
  if (!start) start = timestamp;
  var progress = timestamp - start;
  element.style.transform =
    "translateX(" + Math.min(progress / 10, 200) + "px)";
  if (progress < 2000) {
    window.requestAnimationFrame(step);
  }
}

window.requestAnimationFrame(step);
```

備註
--

Edge 低於 17 的版本和 Internet Explorer 無法保證在繪製循環前觸發 `requestAnimationFrame`。

規格
--

| Specification |
| --- |
| HTML Standard # dom-animationframeprovider-requestanimationframe |

瀏覽器相容性
------

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

其他參考
----

* `Window.mozAnimationStartTime` (en-US)
* `Window.cancelAnimationFrame()` (en-US)
* mozRequestAnimationFrame - Blog post
* requestAnimationFrame for smart animating - Blog post
* Animating with javascript: from setInterval to requestAnimationFrame - Blog post
* Using PC Hardware more efficiently in HTML5: New Web Performance APIs, Part 1 - Blog post
* TestUFO: Test your web browser for requestAnimationFrame() Timing Deviations
* Paul Irish: requestAnimationFrame API: now with sub-millisecond precision


使用 server-sent 事件 - Web APIs

使用 server-sent 事件
=================

開發一個使用 server-sent 事件的網頁應用程式很簡單。在伺服器端只需要一些的程式碼與網頁串流事件，而客戶端這邊的處理進入事件的部分幾乎跟 websockets 一樣。這是一種單向的連線，所以你無法從客戶端向伺服器傳送事件。

從伺服器端接收事件
---------

server-sent event API 包含在 `EventSource` (en-US) 介面；為了與伺服器端開啟連線並接收事件，需要建立帶有產生事件 script URL 的 `EventSource` (en-US) 物件。例如：

```js
const evtSource = new EventSource("ssedemo.php");
```

如果事件產生的 script 在不同源的伺服器上，在建立 `EventSource` (en-US) 物件時需要同時提供 URL 和第二個參數作為選項設定。假設客戶端的 script 伺服於 `example.com` ：

```js
const evtSource = new EventSource("//api.example.com/ssedemo.php", {
  withCredentials: true,
});
```

當你完成初始化事件來源後，你就可以透過新增 `message` (en-US) 事件的處理器來開始監聽來自伺服器的訊息：

```js
evtSource.onmessage = function (event) {
  const newElement = document.createElement("li");
  const eventList = document.getElementById("list");

  newElement.innerHTML = "message: " + event.data;
  eventList.appendChild(newElement);
};
```

上述的程式碼會監聽進入的訊息（這裡來自伺服器的通知沒有指明 `event` 欄位，所以統一用 `onmessage` 處理即可）並且把訊息的文字附加到 document 的清單。

你也可以利用 `addEventListener()` 監聽事件：

```js
evtSource.addEventListener("ping", function (event) {
  const newElement = document.createElement("li");
  const time = JSON.parse(event.data).time;
  newElement.innerHTML = "ping at " + time;
  eventList.appendChild(newElement);
});
```

上述的程式碼大同小異，不同之處在於若伺服器傳送了 `event` 欄位值為「ping」的訊息時它就會把 `data` 欄位的值解析為 JSON 並輸出到畫面上。

**警告：** 當連線不是透過 **HTTP/2** 時，SSE 會受到最大連線數限制所苦，尤其當開啟多個分頁。每個瀏覽器有自己的限制數而且被限制在很低的數量（6）。這個問題已經被 Chrome 和 Firefox 標註為「Won't fix」（不修復）。限制是基於每個瀏覽器 + 網域，也就是說你可以針對 www.example1.com 網域在所有的分頁中開啟六個 SSE 連線，另一個網域 www.example2.com 也可以開啟六個（根據 Stackoverflow）。當使用 HTTP/2 時最大同時 *HTTP streams* 連線數是由伺服器和客戶端之間協調（預設 100）。

從伺服器發送事件
--------

由伺服器端所發送的事件需要使用 `text/event-stream` 的 MIME 類型回應。每一個通知皆由一組文字組成並由一對換行結尾。如何處理事件串流的格式，請參考 Event stream format 。

下面是一個 PHP 範例：

```php
date\_default\_timezone\_set("America/New\_York");
header("Cache-Control: no-cache");
header("Content-Type: text/event-stream");

$counter = rand(1, 10);
while (true) {
  // Every second, send a "ping" event.

  echo "event: ping\n";
  $curDate = date(DATE\_ISO8601);
  echo 'data: {"time": "' . $curDate . '"}';
  echo "\n\n";

  // Send a simple message at random intervals.

  $counter--;

  if (!$counter) {
    echo 'data: This is a message at time ' . $curDate . "\n\n";
    $counter = rand(1, 10);
  }

  ob\_end\_flush();
  flush();

  // Break the loop if the client aborted the connection (closed the page)

  if ( connection\_aborted() ) break;

  sleep(1);
}
```

 上述的程式碼會在每秒產生一個類型為「ping」的事件。每一個事件的資料是一個 JSON 物件，內容為事件產生時的 ISO 8601 時間戳。同時會隨機發送一個簡易訊息（沒有事件類型）。
 迴圈的執行會獨立於連線的狀態，，所以在迴圈裡必須檢查連線的狀態，若斷線了要關閉連線（譬如，客戶端關閉了網頁）。

**備註：** 你可以從下列的 Github 文章中找到包含本文所使用程式碼的完整範例 —— 參考 Simple SSE demo using PHP.

錯誤處理
----

當錯誤發生時（譬如網路逾時或有關存取控制的問題）會產生錯誤事件。你可以透過對 `EventSource` 物件實作 `onerror` 回呼的方式採取程式化的處理：

```js
evtSource.onerror = function (err) {
  console.error("EventSource failed:", err);
};
```

關閉事件串流
------

預設的情況下，如果客戶端和伺服器的連線關閉則連線會被重啟。連線的關閉會伴隨著 `.close()` 方法的執行。
```

evtSource.close();
```

事件串流（Event Stream）格式
--------------------

事件串流是個簡易的文字資料串流，內容必須以 UTF-8 格式編碼。在事件串流中，不同的訊息以一對換行符號做區隔。若要撰寫註解，則要在該行的開頭加上冒號（:）。

**備註：** 註解將有助於防止連線逾時；伺服器端可以定時發送註解以維持連線活著。

每一個訊息是由一到多列的欄位所組成的文字。每個欄位依序由欄位的名稱、冒號、該欄位的文字內容所組合而成。

### 欄位

每隔訊息皆可以由下列的欄位組合而成，每個欄位以換行做為區隔：

`event`

事件的類型。如果有指定則在瀏覽器端會對該事件名稱的監聽器發布事件；網頁的原始碼必須使用 `addEventListener()` 來監聽已命名的事件。 `onmessage` 處理器只有在訊息沒有指定事件名稱時才會被呼叫。

`data`

訊息的資料欄位。當 EventSource 連續接收到多列以 `data:` 開頭的內容；它會串接這些內容並為每一列插入一個換行字元。最後的換行會被移除。

`id`

`EventSource` (en-US) 物件的最新一個事件 ID 。

`retry`

當嘗試傳送事件時重新連線的時間。這個值必須是整數，單位是毫秒，作為重新連線的時間。若指定是非整數則這個欄位會被忽略。

除上述的幾個欄位，其他欄位均會被忽略。

**備註：** 如果某列的內容沒有包含冒號，則該列的內容都會被視為欄位名稱及空字串的欄位值。

### 範例

#### Data-only 訊息

在下列的範例中，共發送了三個訊息。第一個是註解，因其以冒號開頭。如之前提到的，對不會持續發送訊息的情境下，這將有助於維持連線的存續。

第二則訊息包含了 data 欄位及「some text」的值。第三則訊息包含了 data 欄位及「another message\nwith two lines」。注意在內容所出現的換行符號。
```

: this is a test stream

data: some text

data: another message
data: with two lines
```

#### 命名事件

這個範例傳送了一些命名的事件。每一個事件都被指定了 `event` 欄位並且 `data` 欄位也有相應的 JSON 字串作為客戶端回應事件所需的資料。 `data` 欄位的內容可以是任何的字串；它並沒有強制必須以 JSON 的格式撰寫。
```

event: userconnect
data: {"username": "bobby", "time": "02:33:48"}

event: usermessage
data: {"username": "bobby", "time": "02:34:11", "text": "Hi everyone."}

event: userdisconnect
data: {"username": "bobby", "time": "02:34:23"}

event: usermessage
data: {"username": "sean", "time": "02:34:36", "text": "Bye, bobby."}
```

#### 混合及配對

你並非一定只能用未命名訊息或已分類的事件；實際上你可以在單一的事件中把它們混合在一起。
```

event: userconnect
data: {"username": "bobby", "time": "02:33:48"}

data: Here's a system message of some kind that will get used
data: to accomplish some task.

event: usermessage
data: {"username": "bobby", "time": "02:34:11", "text": "Hi everyone."}
```

### EventSource

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


使用 Web Worker - Web APIs

使用 Web Worker
=============

Web Worker 提供簡單的方法讓網頁在背景執行緒（Thread）中執行程式，而不干擾使用者介面運行，另外，Worker 也可以利用 `XMLHttpRequest` 執行輸出/輸入（但是 responseXML 和 channel 這兩個屬性為 null）；一個 worker 可以藉由事件處理器來和 web worker 創造端互相傳送訊息，接下來本文會提供使用 web worker 的詳細說明。

Web Workers API
---------------

透過 worker 建構子 (如 `Worker()` (en-US)) 便可以產生 worker 物件，並且執行 JavaScript 檔案。在 worker 中的 JavaScript 運行在不同於 `window` (en-US) 的執行緒環境，所以在 worker 中存取全域物件應該要透過 `self` (en-US)，如果透過 `window` (en-US) 會導致錯誤發生。

Dedicated worker (專有 worker) 是一般 worker，只能被產生它的檔案存取，`DedicatedWorkerGlobalScope` (en-US) 物件代表其執行環境；而 Shared worker (共享 worker) 則能夠被不同檔案存取，`SharedWorkerGlobalScope` (en-US)) 物件代表其執行環境。

**備註：** worker 其他文件說明請見 The Web Workers API landing page (en-US) 。

基本上 worker 能夠執行任何事情，比如說 WebSockets、IndexedDB、和 Firefox OS 特有的 Data Store API ，然而直接存取 DOM 或是 `window` (en-US) 物件的一些方法和屬性則不被允許，更多細節請見 worker 可存取知函數和類別 (en-US)。

主執行緒和 worker 執行緒之間用 postMessage() 方法發送訊息，然後透過 `onmessage` 事件接受訊息 (訊息存在 `message` (en-US) 事件的 data 屬性之中)，其中被傳送的資料並非共享而是複製一份後傳送。

worker 可以產生新 worker，只要新 worker 的來源 (origin) 和父頁面相同，也可以利用 `XMLHttpRequest` 執行輸出/輸入（但是 responseXML 和 channel 這兩個屬性為 null）。

Dedicated workers
-----------------

dedicated worker 只能被產生它的檔案存取，下面我們先介紹簡單的 Basic dedicated worker example (run dedicated worker) 範例。這個範例會將兩個數字送入 worker 相乘，然後再於前端頁面顯示相乘結果。

### 偵測 Worker 功能

為了向下相容、避免錯誤，最好是確保 worker 存在後再取用之 (main.js):

```js
if (window.Worker) {

  ...

}
```

### 產生 dedicated worker

只要呼叫 `Worker()` (en-US) 建構子，傳入 JS 檔案的 URI，便可以生成一個 worker 執行緒 (main.js):
```

var myWorker = new Worker("worker.js");
```

### 和 dedicated worker 發送訊息

`postMessage()` (en-US) 方法以及 `onmessage` (en-US) 事件處理器就是和 worker 發送訊息的關鍵 (main.js):

```js
first.onchange = function () {
  myWorker.postMessage([first.value, second.value]);
  console.log("Message posted to worker");
};

second.onchange = function () {
  myWorker.postMessage([first.value, second.value]);
  console.log("Message posted to worker");
};
```

範例中有兩個 `<input>` (en-US) 元素，first 和 second，當元素值改變時，我們會利用 postMessage() 方法告訴 worker 改變的值 (這邊用陣列，也可以用其他類別)。

然後在 worker 裡我們從 `onmessage` 接收訊息 (worker.js):

```js
onmessage = function (e) {
  console.log("Message received from main script");
  var workerResult = "Result: " + e.data[0] \* e.data[1];
  console.log("Posting message back to main script");
  postMessage(workerResult);
};
```

`onmessage` 事件物件的 data 屬性存有傳送過來的訊息資料，也就是 input 值；worker 收到後將傳過來的兩個值相乘，再 postMessage 傳回去。

回到主執行，同樣透過 `onmessage` 事件，收到 worker 回傳還來的計算值 :

```js
myWorker.onmessage = function (e) {
  result.textContent = e.data;
  console.log("Message received from worker");
};
```

拿到存在事件 data 中的計算值後，我們接著將值以 `textContent` 顯示出來。

**備註：** `建構 Worker` 的 URI 必須遵從same-origin policy。目前各家瀏覽器在這方面存有歧異，Gecko 10.0 以後允許 data URI 而 Internet Explorer 10 不允許 Blob URI。

**備註：** 在主執行緒中存取 `onmessage` `與 postMessage` 需要主動掛在 worker 物件上，在 worker 執行緒則不用，這是因為 worker 執行緒的全域物件便是 worker 物件。

**備註：** 和 worker 傳送的資料並非共享而是複製一份後傳送，詳細請參照 和 workers 傳遞資料：更多細節。

### 結束 worker

`在主執行緒裡呼叫` `terminate` (en-US) 就可結束 worker :

```js
myWorker.terminate();
```

請注意不論 worker 正在執行的運算完成與否，一但呼叫後 worker 便會立刻被終止。

而在 worker 執行緒裡，worker 可以呼叫自己的 `close` (en-US) 方法來結束 :

```js
close();
```

### 錯誤處理

當執行時期錯誤發生時，onerror 事件處理器會被呼叫，onerror 事件處理器會收到一名為 error 的事件物件 (實作 ErrorEvent Interface)，該事件不會 bubble 且可取消，如果要避免事件預設行為，可以呼叫 `preventDefault()`。

以下三個部分是錯誤事件較關鍵的地方:

`message`

供人閱讀的錯誤訊息

`filename`

錯誤發生所在的檔案名稱

`lineno`

錯誤發生所在的行數

### 產生 subworker

worker 可以產生其他 worker (subworker)，subworker 的來源也必須和主頁相同，另外，subworker 的 URI 的解析是相對於父 worker 的位置而非所在頁面，這項特色有助於追蹤 worker 間的相依性。

### 引入程式腳本與函式庫 (library)

Worker 執行緒能存取一個全域函數 (global function), importScripts()。importScripts() 可以讓 worker 端引入相同網域的程式碼腳本與 libraries，importScripts()可接收零到數個要被輸入資源的 URI，底下為幾個範例:

```js
importScripts(); /\* imports nothing \*/
importScripts("foo.js"); /\* imports just "foo.js" \*/
importScripts("foo.js", "bar.js"); /\* imports two scripts \*/
```

瀏覽器會載入並執行每個程式碼腳本，然後 worker 能夠存取程式碼腳本內定義的全域變數，若是腳本無法載入，會產生一個 NETWORK\_ERROR，後續的程式碼不會被執行，但是先前執行過的程式碼或用 window.setTimeout() (en-US) 延遲執行的程式碼依然有效，而 importScripts() 之後宣告的函數也一樣存在，因為這些程式碼總是在其他程式碼之前就解析過了。

**備註：** 雖然程式碼腳本的下載順序不一定，但執行順序會遵照傳入 importScripts()的順序，這是同步完成的，importScripts()不會回傳直到所有的程式碼都下載並執行完。

Shared workers
--------------

shared worker 能夠被多個程式腳本存取，縱使跨越不同 window、iframe 或 worker。這邊的 Basic shared worker example (run shared worker) 範例和 dedicated worker 範例類似，但多了兩個可以讓多個檔案存取的函數：*數字相乘以及數字平方*。

請注意 dedicated worker 與 shared worker 間的差異處，範例裡會有兩份 HTML 頁面，各自都利用同一個 worker 處理運算。

**備註：** 所有的瀏覽環境都必需共享相同的來源（相同 protocol, host 和 port），shared worker 才能讓不同瀏覽環境存取。

**備註：** 在 Firefox，shared worker 無法在一般和隱私模式間共享（Firefox bug 1177621）。

### 產生 shared worker

和 dedicated worker 做法差不多，只是用另一個 SharedWorker 建構子來產生 shared worker，見 index.html 和 index2.html:

```js
var myWorker = new SharedWorker("worker.js");
```

相當不 一樣的是和 shared worker 溝通必須要透過 port 物件，其實 dedicated worker 也是如此，只不過一切是在背景後自動完成。

開啟 port 連線一是在 onmessage 事件下背景完成，二是藉由主動呼叫 start() 好開始傳送訊息。範例 multiply.js 以及 worker.js 因為註冊了 onmessage 事件，所以其實可以省略呼叫 start()，然而若是 message 事件是經由 `addEventListener()註冊，那麼便需要呼叫 start() 了。`

當使用 start() 開啟 port 連線，那麼雙向溝通便需要主執行緒和 worker 兩端都呼叫 start()。

```js
myWorker.port.start(); // called in parent thread
```

```js
port.start(); // called in worker thread, assuming the port variable references a port
```

### 和 shared worker 發送訊息

如同前面，現在可以呼叫 `postMessage()` 發送訊息，只不過這次需要透過 port 物件 (一樣請參考 multiply.js 和 square.js):

```js
squareNumber.onchange = function () {
  myWorker.port.postMessage([squareNumber.value, squareNumber.value]);
  console.log("Message posted to worker");
};
```

worker 方面也增加了一些程式碼 (worker.js):

```js
onconnect = function (e) {
  var port = e.ports[0];
  port.onmessage = function (e) {
    var workerResult = "Result: " + e.data[0] \* e.data[1];
    port.postMessage(workerResult);
  };
  port.start(); // not necessary since onmessage event handler is being used
};
```

首先，先監聽連線建立的 onconnect 事件，例如當主執行緒建立 onmessage 事件或呼叫 `start()`。

然後從 onconnect 事件物件，我們可以取得 port 物件使用之。

取得 port 之後，我們註冊 port 上的 onmessage 事件，當有訊息進來便取回資料進行運算後回傳回去；註冊 onmessage 事件的同時也自動建立連線，所以說不需要呼叫 start() 了。

最後在主執行緒端，我們同樣由 onmessage 事件取回回傳過來的訊息 (一樣請參考 multiply.js 和 square.js):

```js
myWorker.port.onmessage = function (e) {
  result2.textContent = e.data[0];
  console.log("Message received from worker");
};
```

執行緒 (Thread) 安全
---------------

`Worker` (en-US) 會產生真正 OS 層級的執行緒，細心的開發者或許會擔心同步問題。

不過 worker 會十分注意和其他執行緒溝通的狀況，不會去存取非執行緒安全的元件，如 DOM ，而且資料的傳遞也都序列化 (serialized) ，所以說很難會發生同步問題。

和 workers 傳遞資料：更多細節
-------------------

和 workers 傳遞的資料會先被複製一份，而非共享；經過序列化後 (serialized) 傳輸，然後在另一端反序列化 (de-serialized) 取出，大部份的瀏覽器都是以 結構化複製 (structured cloning) (en-US) 實作這項特色.

下面的 `emulateMessage() 會模擬和 worker 傳遞訊息時，複製資料的行為。`

```js
function emulateMessage(vVal) {
  return eval("(" + JSON.stringify(vVal) + ")");
}

// Tests

// test #1
var example1 = new Number(3);
console.log(typeof example1); // object
console.log(typeof emulateMessage(example1)); // number

// test #2
var example2 = true;
console.log(typeof example2); // boolean
console.log(typeof emulateMessage(example2)); // boolean

// test #3
var example3 = new String("Hello World");
console.log(typeof example3); // object
console.log(typeof emulateMessage(example3)); // string

// test #4
var example4 = {
  name: "John Smith",
  age: 43,
};
console.log(typeof example4); // object
console.log(typeof emulateMessage(example4)); // object

// test #5
function Animal(sType, nAge) {
  this.type = sType;
  this.age = nAge;
}
var example5 = new Animal("Cat", 3);
alert(example5.constructor); // Animal
alert(emulateMessage(example5).constructor); // Object
```

所謂的訊息就是經過複製、非共享的資料，到這邊你應該已經知道 `postMessage()` 負責發送訊息，然後 `message` 事件 `data` (en-US) 的 attribute 則存有傳送的訊息資料。

**example.html**: (the main page):

```js
var myWorker = new Worker("my\_task.js");

myWorker.onmessage = function (oEvent) {
  console.log("Worker said : " + oEvent.data);
};

myWorker.postMessage("ali");
```

**my\_task.js** (the worker):

```js
postMessage("I'm working before postMessage('ali').");

onmessage = function (oEvent) {
  postMessage("Hi " + oEvent.data);
};
```

結構化複製（structured cloning） (en-US) 演算法支援 JSON 以及迴圈參照（circular references）。

### 資料傳遞範例

#### 範例 1: 非同步 `eval()`

下面透過 data URL (en-US) 和 `eval()，`示範如何在 worker 非同步執行允許的程式碼：

```js
// Syntax: asyncEval(code[, listener])

var asyncEval = (function () {
  var aListeners = [],
    oParser = new Worker(
      "data:text/javascript;charset=US-ASCII,onmessage%20%3D%20function%20%28oEvent%29%20%7B%0A%09postMessage%28%7B%0A%09%09%22id%22%3A%20oEvent.data.id%2C%0A%09%09%22evaluated%22%3A%20eval%28oEvent.data.code%29%0A%09%7D%29%3B%0A%7D",
    );

  oParser.onmessage = function (oEvent) {
    if (aListeners[oEvent.data.id]) {
      aListeners[oEvent.data.id](oEvent.data.evaluated);
    }
    delete aListeners[oEvent.data.id];
  };

  return function (sCode, fListener) {
    aListeners.push(fListener || null);
    oParser.postMessage({
      id: aListeners.length - 1,
      code: sCode,
    });
  };
})();
```

data URL (en-US) 相當於網路請求，範例中的 data URL 會在 worker 執行下列程式碼回應訊息：

```js
onmessage = function (oEvent) {
  postMessage({
    id: oEvent.data.id,
    evaluated: eval(oEvent.data.code),
  });
};
```

應用範例:

```js
// asynchronous alert message...
asyncEval("3 + 2", function (sMessage) {
  alert("3 + 2 = " + sMessage);
});

// asynchronous print message...
asyncEval('"Hello World!!!"', function (sHTML) {
  document.body.appendChild(document.createTextNode(sHTML));
});

// asynchronous void...
asyncEval(
  '(function () {\n\tvar oReq = new XMLHttpRequest();\n\toReq.open("get", "http://www.mozilla.org/", false);\n\toReq.send(null);\n\treturn oReq.responseText;\n})()',
);
```

#### 範例 2: JSON 資料進階傳遞與呼叫系統

下面的範例系統適合需要在主頁面和 worker 傳遞複雜資料和呼叫多個函數的情境。

**example.html** (主頁面):

```html
<!doctype html>
<html>
  <head>
    <meta charset="UTF-8" />
    <title>MDN Example - Queryable worker</title>
    <script type="text/javascript">
 /\*
 QueryableWorker instances methods:
 \* sendQuery(queryable function name, argument to pass 1, argument to pass 2, etc. etc): calls a Worker's queryable function
 \* postMessage(string or JSON Data): see Worker.prototype.postMessage()
 \* terminate(): terminates the Worker
 \* addListener(name, function): adds a listener
 \* removeListener(name): removes a listener
 QueryableWorker instances properties:
 \* defaultListener: the default listener executed only when the Worker calls the postMessage() function directly
 \*/
 function QueryableWorker(sURL, fDefListener, fOnError) {
 var oInstance = this,
 oWorker = new Worker(sURL),
 oListeners = {};
 this.defaultListener = fDefListener || function () {};
 oWorker.onmessage = function (oEvent) {
 if (
 oEvent.data instanceof Object &&
 oEvent.data.hasOwnProperty("vo42t30") &&
 oEvent.data.hasOwnProperty("rnb93qh")
 ) {
 oListeners[oEvent.data.vo42t30].apply(
 oInstance,
 oEvent.data.rnb93qh,
 );
 } else {
 this.defaultListener.call(oInstance, oEvent.data);
 }
 };
 if (fOnError) {
 oWorker.onerror = fOnError;
 }
 this.sendQuery =
 function (/\* queryable function name, argument to pass 1, argument to pass 2, etc. etc \*/) {
 if (arguments.length < 1) {
 throw new TypeError(
 "QueryableWorker.sendQuery - not enough arguments",
 );
 return;
 }
 oWorker.postMessage({
 bk4e1h0: arguments[0],
 ktp3fm1: Array.prototype.slice.call(arguments, 1),
 });
 };
 this.postMessage = function (vMsg) {
 //I just think there is no need to use call() method
 //how about just oWorker.postMessage(vMsg);
 //the same situation with terminate
 //well,just a little faster,no search up the prototye chain
 Worker.prototype.postMessage.call(oWorker, vMsg);
 };
 this.terminate = function () {
 Worker.prototype.terminate.call(oWorker);
 };
 this.addListener = function (sName, fListener) {
 oListeners[sName] = fListener;
 };
 this.removeListener = function (sName) {
 delete oListeners[sName];
 };
 }

 // your custom "queryable" worker
 var oMyTask = new QueryableWorker(
 "my\_task.js" /\* , yourDefaultMessageListenerHere [optional], yourErrorListenerHere [optional] \*/,
 );

 // your custom "listeners"

 oMyTask.addListener("printSomething", function (nResult) {
 document
 .getElementById("firstLink")
 .parentNode.appendChild(
 document.createTextNode(" The difference is " + nResult + "!"),
 );
 });

 oMyTask.addListener("alertSomething", function (nDeltaT, sUnit) {
 alert("Worker waited for " + nDeltaT + " " + sUnit + " :-)");
 });
 </script>
  </head>
  <body>
    <ul>
      <li>
        <a
 id="firstLink"
 href="javascript:oMyTask.sendQuery('getDifference', 5, 3);"
 >What is the difference between 5 and 3?</a
 >
      </li>
      <li>
        <a href="javascript:oMyTask.sendQuery('waitSomething');"
 >Wait 3 seconds</a
 >
      </li>
      <li>
        <a href="javascript:oMyTask.terminate();">terminate() the Worker</a>
      </li>
    </ul>
  </body>
</html>
```

**my\_task.js** (worker):

```js
// your custom PRIVATE functions

function myPrivateFunc1() {
  // do something
}

function myPrivateFunc2() {
  // do something
}

// etc. etc.

// your custom PUBLIC functions (i.e. queryable from the main page)

var queryableFunctions = {
  // example #1: get the difference between two numbers:
  getDifference: function (nMinuend, nSubtrahend) {
    reply("printSomething", nMinuend - nSubtrahend);
  },
  // example #2: wait three seconds
  waitSomething: function () {
    setTimeout(function () {
      reply("alertSomething", 3, "seconds");
    }, 3000);
  },
};

// system functions

function defaultQuery(vMsg) {
  // your default PUBLIC function executed only when main page calls the queryableWorker.postMessage() method directly
  // do something
}

function reply(/\* listener name, argument to pass 1, argument to pass 2, etc. etc \*/) {
  if (arguments.length < 1) {
    throw new TypeError("reply - not enough arguments");
    return;
  }
  postMessage({
    vo42t30: arguments[0],
    rnb93qh: Array.prototype.slice.call(arguments, 1),
  });
}

onmessage = function (oEvent) {
  if (
    oEvent.data instanceof Object &&
    oEvent.data.hasOwnProperty("bk4e1h0") &&
    oEvent.data.hasOwnProperty("ktp3fm1")
  ) {
    queryableFunctions[oEvent.data.bk4e1h0].apply(self, oEvent.data.ktp3fm1);
  } else {
    defaultQuery(oEvent.data);
  }
};
```

### 移轉資料所有權 - 可移轉物件 (transferable objects)

Google Chrome 17+ 以及 Firefox 18+ 能夠和 worker 高效能地傳送另外一種特定型態物件 (可移轉物件, transferable objects，這種物件實作了 `Transferable` 介面)，可移轉物件當被傳送到另一端時並不需要複製，因此可以大大提升傳送大型資料物件的效能；這好比像是 C/C++ 的 pass-by-reference，但是不同的是，一旦移轉後原先的環境便失去了持有資料，例如當主頁面傳送 `ArrayBuffer` 後，主頁面便不再能夠使用這筆資料物件了，這筆資料物件的存取連結已經靜靜地移轉到 worker 端了。

```js
// Create a 32MB "file" and fill it.
var uInt8Array = new Uint8Array(1024 \* 1024 \* 32); // 32MB
for (var i = 0; i < uInt8Array.length; ++i) {
  uInt8Array[i] = i;
}

worker.postMessage(uInt8Array.buffer, [uInt8Array.buffer]);
```

**備註：** 關於更多可移轉物件的資訊, 效能和功能偵測，請參考 HTML5 Rocks 上 Transferable Objects: Lightning Fast! 一文。

Embedded workers
----------------

不像 `<script>`，並沒有一套正式標準的方法將 worker 的程式碼嵌入到頁面之中，不過沒有 src 屬性而且 mime-type 不屬於可執行程式碼的 `<script>` 元素會被視為 javascript 可以取用的資料區塊（data block），資料區塊是一項 HTML5 可用於攜帶文字資料的特色功能，利用資料區塊我們就有辦法嵌入 worker 的程式碼到頁面中：

```html
<!doctype html>
<html>
  <head>
    <meta charset="UTF-8" />
    <title>MDN Example - Embedded worker</title>
    <script type="text/js-worker">
 // This script WON'T be parsed by JS engines because its mime-type is text/js-worker.
 var myVar = "Hello World!";
 // Rest of your worker code goes here.
 </script>
    <script type="text/javascript">
 // This script WILL be parsed by JS engines because its mime-type is text/javascript.
 function pageLog(sMsg) {
 // Use a fragment: browser will only render/reflow once.
 var oFragm = document.createDocumentFragment();
 oFragm.appendChild(document.createTextNode(sMsg));
 oFragm.appendChild(document.createElement("br"));
 document.querySelector("#logDisplay").appendChild(oFragm);
 }
 </script>
    <script type="text/js-worker">
 // This script WON'T be parsed by JS engines because its mime-type is text/js-worker.
 onmessage = function (oEvent) {
 postMessage(myVar);
 };
 // Rest of your worker code goes here.
 </script>
    <script type="text/javascript">
 // This script WILL be parsed by JS engines because its mime-type is text/javascript.

 // In the past...:
 // blob builder existed
 // ...but now we use Blob...:
 var blob = new Blob(
 Array.prototype.map.call(
 document.querySelectorAll('script[type="text\/js-worker"]'),
 function (oScript) {
 return oScript.textContent;
 },
 ),
 { type: "text/javascript" },
 );

 // Creating a new document.worker property containing all our "text/js-worker" scripts.
 document.worker = new Worker(window.URL.createObjectURL(blob));

 document.worker.onmessage = function (oEvent) {
 pageLog("Received: " + oEvent.data);
 };

 // Start the worker.
 window.onload = function () {
 document.worker.postMessage("");
 };
 </script>
  </head>
  <body>
    <div id="logDisplay"></div>
  </body>
</html>
```

Embedded worker 在 `document.worker` 之中。

其他範例
----

下面介紹其他使用 worker 的範例。

### 在背景中執行運算

worker 主要的用處在避免重度 CPU 運算的任務阻礙到 UI 執行緒運行；這邊我們用 worker 來跑 Fibonacci 數列運算。

#### JavaScript

fibonacci.js 中的程式碼會被另一份 HTML 引用。

```js
var results = [];

function resultReceiver(event) {
  results.push(parseInt(event.data));
  if (results.length == 2) {
    postMessage(results[0] + results[1]);
  }
}

function errorReceiver(event) {
  throw event.data;
}

onmessage = function (event) {
  var n = parseInt(event.data);

  if (n == 0 || n == 1) {
    postMessage(n);
    return;
  }

  for (var i = 1; i <= 2; i++) {
    var worker = new Worker("fibonacci.js");
    worker.onmessage = resultReceiver;
    worker.onerror = errorReceiver;
    worker.postMessage(n - i);
  }
};
```

worker 程式碼中註冊了一個 `onmessage` 事件處理器用來接收另一端 `postMessage 過來的訊息` (請注意這並非定義一個全域變數或函數，`var onmessage` 或 `function onmessage` 會定義全域變數，但不會註冊事件處理器)，然後開始進行遞迴運算。

#### HTML

```html
<!doctype html>
<html>
  <head>
    <meta charset="UTF-8" />
    <title>Test threads fibonacci</title>
  </head>
  <body>
    <div id="result"></div>

    <script language="javascript">
 var worker = new Worker("fibonacci.js");

 worker.onmessage = function (event) {
 document.getElementById("result").textContent = event.data;
 dump("Got: " + event.data + "\n");
 };

 worker.onerror = function (error) {
 dump("Worker error: " + error.message + "\n");
 throw error;
 };

 worker.postMessage("5");
 </script>
  </body>
</html>
```

onmessage 事件處理器會接收 worker 回傳的運算結果，然後顯示在頁面上，如果有問題， onerror 事件處理器會 輸出 錯誤訊息。

和 worker 溝通則是利用 postMessage。

範例測試。

### 在背景中執行 web I/O

範例請見 Using workers in extensions 。

### 分割任務到多個 workers

基於多核 cpu 的普及，分割複雜任務到多個 workers 將可能有助於利用多核心 cpu 的優勢。

其他類型的 worker
------------

除了 dedicated 和 shared web workers，還有其他種類：

* ServiceWorkers (en-US) 基本上如同介於 web app 和瀏覽器以及網路之間的代理伺服器 (proxy server)，這類 worker 重點在實現離線服務，service worker 會攔截網路請求，然後依據網路連線和資源狀態做出反應，他們可以存取推播和背景同步 APIs。
* Chrome Workers 是 Firefox 唯一的 worker 類型，他們可以用在開發 add-ons，或是想要使用 js-ctypes。詳情請見 `ChromeWorker`。
* Audio Workers 主要用於音效處理部分。
Worker 可存取之函數與介面
----------------

大多數 Javascript 的功能 worker 皆可以使用，包含：

* `Navigator` (en-US)
* `XMLHttpRequest`
* `Array`, `Date`, `Math`, 與 `String`
* `Window.requestAnimationFrame`, `WindowTimers.setTimeout` (en-US), 與 `WindowTimers.setInterval` (en-US)

worker 無法操作主頁面的物件與 DOM，如有相關需求，必須要間接透過 `DedicatedWorkerGlobalScope.postMessage` (en-US) 通知主頁面，讓主頁面執行需求。

**備註：** 所有 worker 可存取功能一覽表，請見 Functions and interfaces available to workers (en-US).

標準規範
----

| Specification |
| --- |
| HTML Standard # workers |

延伸閱讀
----

* `Worker` (en-US) interface
* `SharedWorker` (en-US) interface
* Functions available to workers (en-US)
* Advanced concepts and examples


Using Fetch - Web APIs

Using Fetch
===========

Fetch API 提供了一種 JavaScript Interface 來操作 HTTP pipeline，比方 request 和 response。同時它也提供了 global 的 `fetch()` (en-US) method，使得在網路上非同步地 fetch resources 這件事變得簡單易懂。

同樣的功能，以前都是使用 `XMLHttpRequest`，而 Fetch 作為其替代方案，能更方便地整合在如 `Service Workers` (en-US) 等相關技術上。此外，Fetch 具備額外的 logical palce，能拿來定義其他和 HTTP 有關的東西，像是 CORS 和 HTTP extensions。

`fetch` 和 `jQuery.ajax()` 有三個主要的差異:

* `fetch()` 回傳的 promise **不會 reject HTTP 的 error status**，就算是 HTTP 404 或 500 也一樣。相反地，它會正常地 resolve，並把 `ok` status 設為 false。會讓它發生 reject 的只有網路錯誤或其他會中斷 request 的情況。
* `fetch` **可以接收跨站的 cookies**，你可以用 Fetch 來建立跨站的 session。
* `fetch` **不會傳送 cookies**，除非你有設定 credentials 的 init option (en-US)。 (Since Aug 25, 2017. The spec changed the default credentials policy to `same-origin`. Firefox changed since 61.0b13.)
使用 Fetch 發送請求 ( request )
-------------------------

用法簡單，如下:

```js
fetch("http://example.com/movies.json")
  .then(function (response) {
    return response.json();
  })
  .then(function (myJson) {
    console.log(myJson);
  });
```

這裡要使用 fetch 透過網路取得 json 然後印出在 console，最簡單的方式只需要一個參數就是資料的 URI，fetch 會回傳一個包含 response 的 promise 。

這個範例使用的 url 只是示意用。

回傳的 response 需要透過 `json()` (en-US) (在 `Body` 可以找到定義, Body 是用 `Request` (en-US) 和 `Response` 實作出來的物件.)

**備註：** 其實 Body 還提供了其他類似的功能可以將內容輸成其他類型格式，詳見Body

Fetch 請求的安全性 Content Security Policy (en-US)(內容安全策略) 是由 header 中的 `connect-src` directive 所設定 ，並非其他 directive ( 比如：img-src、default-src 等)。

### Request 可用的設定值

`fetch()` 第二個參數是選用的，可以傳送一個 `init` Object 來設定 request。

更多可以用的設定值詳見 `fetch()` (en-US)

```js
// 來發個 POST Request:

postData("http://example.com/answer", { answer: 42 })
  .then((data) => console.log(data)) // JSON from `response.json()` call
  .catch((error) => console.error(error));

function postData(url, data) {
  // Default options are marked with \*
  return fetch(url, {
    body: JSON.stringify(data), // must match 'Content-Type' header
    cache: "no-cache", // \*default, no-cache, reload, force-cache, only-if-cached
    credentials: "same-origin", // include, same-origin, \*omit
    headers: {
      "user-agent": "Mozilla/4.0 MDN Example",
      "content-type": "application/json",
    },
    method: "POST", // \*GET, POST, PUT, DELETE, etc.
    mode: "cors", // no-cors, cors, \*same-origin
    redirect: "follow", // manual, \*follow, error
    referrer: "no-referrer", // \*client, no-referrer
  }).then((response) => response.json()); // 輸出成 json
}
```

### 包含憑證(Credentials) 的 Request 用法

要讓瀏覽器將 credentials 跟著 request 一起送出, 方式就是在 `init` object 加上 `credentials: 'include'`

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

如果只想要把 credentials 發送給同源的 URL ，加上`credentials: 'same-origin'`。

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

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

或要確保瀏覽器不會帶著 credentials 請求，可以用 `credentials: 'omit'` 。

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

### 上傳 JSON 資料

使用 `fetch()` (en-US) 來 POST JSON 格式的資料。

```js
var url = "https://example.com/profile";
var data = { username: "example" };

fetch(url, {
  method: "POST", // or 'PUT'
  body: JSON.stringify(data), // data can be `string` or {object}!
  headers: new Headers({
    "Content-Type": "application/json",
  }),
})
  .then((res) => res.json())
  .catch((error) => console.error("Error:", error))
  .then((response) => console.log("Success:", response));
```

### 上傳檔案

上傳檔案可以透過使用 HTML `<input type="file" />` input element, `FormData()` (en-US) 與`fetch()` (en-US).

```js
var formData = new FormData();
var 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())
  .catch((error) => console.error("Error:", error))
  .then((response) => console.log("Success:", response));
```

### 如何確認 fetch 是否成功

當`fetch()` (en-US)遇到 CORS 或 server 設定錯誤導致 network error 時, promise 會 reject 並附上`TypeError` (en-US)的回應, 但在權限或類似問題導致 404 的常見狀況下, 卻不會導致 network error.

因此, 確認`fetch()`是否成功的正確方式, 應包含檢查 promise resolved, 以及檢查`Response.ok` (en-US)的屬性是否為 true. 代碼如下例：

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

### Supplying your own request object

Instead of passing a path to the resource you want to request into the `fetch()` call, you can create a request object using the `Request()` (en-US) constructor, and pass that in as a `fetch()` method argument:

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

var myInit = {
  method: "GET",
  headers: myHeaders,
  mode: "cors",
  cache: "default",
};

var myRequest = new Request("flowers.jpg", myInit);

fetch(myRequest)
  .then(function (response) {
    return response.blob();
  })
  .then(function (myBlob) {
    var objectURL = URL.createObjectURL(myBlob);
    myImage.src = objectURL;
  });
```

`Request()` accepts exactly the same parameters as the `fetch()` method. You can even pass in an existing request object to create a copy of it:

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

This is pretty useful, as request and response bodies are one use only. Making a copy like this allows you to make use of the request/response again, while varying the `init` options if desired. The copy must be made before the body is read, and reading the body in the copy will also mark it as read in the original request.

**備註：** There is also a `clone()` (en-US) method that creates a copy. Both methods of creating a copy will fail if the body of the original request or response has already been read, but reading the body of a cloned response or request will not cause it to be marked as read in the original.

Headers
-------

The `Headers` (en-US) interface allows you to create your own headers object via the `Headers()` (en-US) constructor. A headers object is a simple multi-map of names to values:

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

The same can be achieved by passing an array of arrays or an object literal to the constructor:

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

The contents can be queried and retrieved:

```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")); // [ ]
```

Some of these operations are only useful in `ServiceWorkers` (en-US), but they provide a much nicer API for manipulating headers.

All of the Headers methods throw a `TypeError` if a header name is used that is not a valid HTTP Header name. The mutation operations will throw a `TypeError` if there is an immutable guard (see below). Otherwise they fail silently. For example:

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

A good use case for headers is checking whether the content type is correct before you process it further. For example:

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

### Guard

Since headers can be sent in requests and received in responses, and have various limitations about what information can and should be mutable, headers objects have a guard property. This is not exposed to the Web, but it affects which mutation operations are allowed on the headers object.

Possible guard values are:

* `none`: default.
* `request`: guard for a headers object obtained from a request (`Request.headers` (en-US)).
* `request-no-cors`: guard for a headers object obtained from a request created with `Request.mode` (en-US) `no-cors`.
* `response`: guard for a Headers obtained from a response (`Response.headers` (en-US)).
* `immutable`: Mostly used for ServiceWorkers; renders a headers object read-only.

**備註：** You may not append or set a `request` guarded Headers' `Content-Length` header. Similarly, inserting `Set-Cookie` into a response header is not allowed: ServiceWorkers are not allowed to set cookies via synthesized responses.

Response objects
----------------

As you have seen above, `Response` instances are returned when `fetch()` promises are resolved.

The most common response properties you'll use are:

* `Response.status` (en-US) — An integer (default value 200) containing the response status code.
* `Response.statusText` (en-US) — A string (default value "OK"), which corresponds to the HTTP status code message.
* `Response.ok` (en-US) — seen in use above, this is a shorthand for checking that status is in the range 200-299 inclusive. This returns a `Boolean` (en-US).

They can also be created programmatically via JavaScript, but this is only really useful in `ServiceWorkers` (en-US), when you are providing a custom response to a received request using a `respondWith()` (en-US) method:

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

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

The `Response()` (en-US) constructor takes two optional arguments — a body for the response, and an init object (similar to the one that `Request()` (en-US) accepts.)

**備註：** The static method `error()` (en-US) simply returns an error response. Similarly, `redirect()` (en-US) returns a response resulting in a redirect to a specified URL. These are also only relevant to Service Workers.

Body
----

Both requests and responses may contain body data. A body is an instance of any of the following types:

* `ArrayBuffer`
* `ArrayBufferView` (en-US) (Uint8Array and friends)
* `Blob`/File
* string
* `URLSearchParams` (en-US)
* `FormData`

The `Body` mixin defines the following methods to extract a body (implemented by both `Request` (en-US) and `Response`). These all return a promise that is eventually resolved with the actual content.

* `arrayBuffer()` (en-US)
* `blob()` (en-US)
* `json()` (en-US)
* `text()` (en-US)
* `formData()` (en-US)

This makes usage of non-textual data much easier than it was with XHR.

Request bodies can be set by passing body parameters:

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

Both request and response (and by extension the `fetch()` function), will try to intelligently determine the content type. A request will also automatically set a `Content-Type` header if none is set in the dictionary.

特性偵測
----

想確認是否支持 Fetch API，可透過檢查 `Headers` (en-US)、`Request` (en-US)、`Response` 或 `fetch()` (en-US) 是否存在 `Window` (en-US) 或 `Worker` (en-US) 域中。例如：

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

參見
--

* ServiceWorker API (en-US)
* HTTP access control (CORS)
* HTTP
* Fetch polyfill
* Fetch examples on Github


拖曳操作 - Web APIs

拖曳操作
====

本文會一一說明拖曳各步驟的作業。

The drag operations described in this document use the `DataTransfer` (en-US) interface. This document does *not* use the `DataTransferItem` (en-US) interface nor the `DataTransferItemList` (en-US) interface.

Draggable 屬性
------------

網頁中有些預設的拖曳行為，例如文字選擇、圖片或超連結，當拖曳圖片或超連結時，圖片或超連結的 URL 會被當作拖曳作業中所攜帶的資料，而其他類型元素則必須另外處理才能拖曳，試試看選擇網頁某一部分，然後按住滑鼠鍵來進行拖曳，依據 OS 不同，或許會有一些跟著滑鼠移動的效果，但這僅僅只是預設效果行為，實際上沒有任何資料跟著被拖曳。

In HTML, apart from the default behavior for images, links, and selections, no other elements are draggable by default. In XUL, all elements are draggable.

除了文字選擇、圖片或超連結之外，沒有元素預設是可拖曳的。所以要讓一個元素可以拖曳，有幾件事必須要做：

* 在想要拖曳的元素上設定 `draggable` (en-US) 屬性為 `true`。
* 註冊 `dragstart` (en-US) 事件之事件處理器。
* `Set the drag data` (en-US) within the listener defined above.

以下是一段簡單的範例。

```html
<div
 draggable="true"
 ondragstart="event.dataTransfer.setData('text/plain', 'This text may be dragged')">
  This text <strong>may</strong> be dragged.
</div>
```

draggable 為 true 後，該 DIV 元素便可以拖曳，反之，倘若 draggable 為 false 或無設定則不可拖曳，只有其中下含的文字可以被選擇。draggable 屬性適用於任何元素，一般來說預設為 false，除了圖片和連結預設為 true，所以說如果想要阻止圖片和連結被拖曳，則可以設定 draggable 為 false。

請注意，一旦元素被定為可拖曳之後，其下內含的文字或其他元素便無法像平常一樣用滑鼠選擇，使用者之能夠改用鍵盤或按住 Alt 鍵搭配滑鼠進行選擇。

至於 XUL 元素則是預設皆可拖曳。

```html
<button
 label="Drag Me"
 ondragstart="event.dataTransfer.setData('text/plain', 'Drag Me Button');"></button>
```

開始拖曳
----

下方範例在 dragstart 註冊一個事件處理器。

```html
<div
 draggable="true"
 ondragstart="event.dataTransfer.setData('text/plain', 'This text may be dragged')">
  This text <strong>may</strong> be dragged.
</div>
```

當拖曳作業開始，dragstart 事件會觸發，然後我們可以在事件處理器中準備好我們所要攜帶的資料、想要的拖曳回饋效果，不過基本上其實只需要準備資料就好，因為預設拖曳回饋效果已經足以應付大多數的狀況，此外，我們也可以改在上一層父元素註冊事件處理器，因為拖曳事件會上向傳遞 ( Bubble up ) 。

拖曳資料
----

所有的拖曳事件物件都有一個 dataTransfer (en-US) 屬性，這個屬性是用來攜帶資料。

當拖曳時，資料必須和被拖曳目標作連結，比如說拖曳文字框中反白選擇的文字，那麼文字本身便是連結資料，同理，拖曳連結時 URL 便是連結資料。

資料包含兩個部分，一是資料型態(或格式)、二是資料值。所謂資料型態是用文字描述資料型態(如text/plain (en-US)代表文字資料)，而資料值則是文字，要加入拖曳資料需要提供資料的型態和內容值；有了資料後，我們可以在 dragenter 或 dragover 事件處理器中，透過檢查資料型態來決定是否可以接受後續的放置操作，比如說只接受連結類資料的拖曳目標區(drop target)，會檢查資料型態是否為text/uri-list (en-US)。

資料型態符合 MIME 型態，如text/plain (en-US)或image/jpeg (en-US)等等，而我們自己也可以自定義其他型態，最常使用的型態請見推薦拖曳資料型態 (en-US)。

一趟拖曳作業中可以攜帶多個多種型態的資料，所以我們可以自定義自己的型態同時，還提供其他資料給不認得自定義資料型態的其他拖曳目標區使用。通常最通用的資料會是文字類型資料。

呼叫setData (en-US)方法，傳入資料型態和資料，這樣就可以攜帶想要的資料了：

```js
event.dataTransfer.setData("text/plain", "Text to drag");
```

上例資料是「Text to drag」文字，型態是 text/plain。

呼叫多次 setData 我們就可以攜帶多種資料。

```js
var dt = event.dataTransfer;
dt.setData("application/x-bookmark", bookmarkString);
dt.setData("text/uri-list", "http://www.mozilla.org");
dt.setData("text/plain", "http://www.mozilla.org");
```

這裡加入了三種資料，第一種是自定義的「application/x-bookmark」，雖然有更豐富的內容可使用，但只有我們自己認識，而另外我們又為其他網站或應用加入了兩種比較常見的資料，「text/uri-list」以及「text/plain」。

如果對同一種資料型態加入兩次資料，則新加資料會取代舊資料。

呼叫clearData (en-US)會清除資料。

```js
event.dataTransfer.clearData("text/uri-list");
```

如果呼叫 clearData 時有傳入資料型態，則只會清除該型態資料，如果沒有傳入任何型態，則所有資料皆會被清除。

設定拖曳圖片
------

當拖曳進行中，以拖曳元素為基礎，一個半透明的圖片會自動產生出來，並且跟著滑鼠移動。如果想要，我們也可以呼叫 `setDragImage()` (en-US) 來指定我們自己的拖曳使用圖片。

```js
event.dataTransfer.setDragImage(image, xOffset, yOffset);
```

setDragImage 需要三個參數，一是圖片來源(通常是圖片元素，但也可以是 canvas 元素或其他元素)，拖曳使用圖片會依照圖片來源在螢幕上所顯示的樣子產生；二和三是圖片相對於滑鼠指標的位置位移量。

不過也是能夠使用文件外部的圖片或 canvas 元素，當需要透過 canvas 元素產生客製圖片時，這個技巧很有用，如下範例所示：

```js
function dragWithCustomImage(event) {
  var canvas = document.createElementNS(
    "http://www.w3.org/1999/xhtml",
    "canvas",
  );
  canvas.width = canvas.height = 50;

  var ctx = canvas.getContext("2d");
  ctx.lineWidth = 4;
  ctx.moveTo(0, 0);
  ctx.lineTo(50, 50);
  ctx.moveTo(0, 50);
  ctx.lineTo(50, 0);
  ctx.stroke();

  var dt = event.dataTransfer;
  dt.setData("text/plain", "Data to Drag");
  dt.setDragImage(canvas, 25, 25);
}
```

上面我們的 canvas 是 50 x 50px 大小，然後我們位移一半 25 讓圖片落在滑鼠指標中央。

拖曳效果
----

拖曳作業有好機種；copy 作業代表被拖曳資料會被複製一份到拖曳目標區，move 作業代表移動被拖曳的資料，link 作業代表拖曳來源區和拖曳目標區有某種關係。

在 dragstart 事件中可以設定effectAllowed (en-US)屬性，指定拖曳源頭允許的作業。

```js
event.dataTransfer.effectAllowed = "copy";
```

上面只有 copy 被允許，但還有其他種類：

只能移動或連結。

none

不允許任何作業。

copy

只能複製。

move

只能移動。

link

只有連結。

copyMove

只能複製或移動。

copyLink

只能複製或連結。

linkMove

all

複製、移動或連結皆可。

effectAllowed (en-US) 屬性預設所有作業都接受，如 all 值。

在 dragenter 或 dragover 事件中，我們可以藉由檢查 effectAllowed 來知道那些作業是被允許的，另外，另一個相關聯的dropEffect (en-US)屬性應該要是 effectAllowed 的其中一個作業，但是 dropEffect 不接受多重作業，只可以是 none, copy, move 和 link。

dropEffect 屬性會在在 dragenter 以及 dragover 事件中初始化為使用者想要執行的作業效果，使用者能夠透過按鍵(依平台不同，通常是 Shift 或 Ctrl 鍵)，在複製、移動、連接作業之間切換，同時滑鼠指標也會跟著相應變換，例如複製作業的滑鼠旁會多出一個+的符號。

effectAllowed 和 dropEffect 屬性可以在 dragenter 或 dragover 事件中更改，更改 effectAllowed 屬性能讓拖曳作業只能在支援被允許作業類型的拖曳目標上執行，好比說 effectAllowed 為 copyMove 的作業就會阻止使用者進行 link 類型的作業。

我們也可以更改 dropEffect 來強迫使用者執行某項作業，而且應該要是 effectAllowed 所列舉的作業。

```js
event.dataTransfer.effectAllowed = "copyMove";
event.dataTransfer.dropEffect = "copy";
```

上面的範例中 copy 就是會被執行的作業效果。

若 effectAllowed 或 dropEffect 為 none，那麼沒有放置作業可以被執行。

指定拖曳目標
------

dragenter 和 dragover 事件就是用來指定拖曳目標區，也就是放置資料的目標區，絕大多數的元素預設的事件都不准放置資料。

所以想要放置資料到元素上，就必須取消預設事件行為。取消預設事件行為能夠藉由回傳 false 或呼叫event.preventDefault方法。

```html
<div ondragover="return false">
  <div ondragover="event.preventDefault()"></div>
</div>
```

通常我們只有在適當的時機點才需要呼叫 event.preventDefault 方法、取消預設事件行為，比如說被拖曳進來的是連結。所以檢查被拖曳進來的物件，當符合條件後再來取消預設事件行為。

藉由檢查拖曳資料型態來決定是否允許放置，是最常見的作法。dataTransfer 物件的types (en-US)屬性是一個拖曳資料型態的列表，其中順序按照資料被加入之先後排序。

```js
function doDragOver(event) {
  var isLink = event.dataTransfer.types.contains("text/uri-list");
  if (isLink) event.preventDefault();
}
```

上面我們呼叫 contains 方法檢察 text/uri-list 是否存在拖曳資料型態的列表之內，有的話才取消預設行為、准許放置作業，否則，不取消預設行為、不准許放置作業。

檢查拖曳資料型態後，我們也可以依此更動 effectAllowed 和 dropEffect 屬性，只不過，如果沒有取消預設行為，更動並不會有甚麼影響。

### Updates to DataTransfer.types

Note that the latest spec now dictates that `DataTransfer.types` (en-US) should return a frozen array of `DOMString`s rather than a `DOMStringList` (en-US) (this is supported in Firefox 52 and above).

As a result, the contains (en-US) method no longer works on the property; the includes method should be used instead to check if a specific type of data is provided, using code like the following:

```js
if ([...event.dataTransfer.types].includes("text/html")) {
  // Do something
}
```

You could always use some feature detection to determine which method is supported on `types`, and run code as appropriate.

放置回饋
----

有好幾種方法回饋使用者，告訴使用者甚麼元素可以接受放置作業，最簡單的是滑鼠會指標會自動變換樣式(視平台而定)。

滑鼠指標提示雖然夠用了，不過有時我們還是會想做其他 UI 上的樣式變化。-moz-drag-over 的 CSS pseudo-class 便可以應用在拖曳目標元素上。

```css
.droparea:-moz-drag-over {
  border: 1px solid black;
}
```

當目標元素的 dragenter 預設事件有被取消時，這個 pseudo-class 就會啟動，目標 UI 會套用 1px 的黑色 border，請注意 dragover 並不會檢查這項設定。

其他比如說插入圖片等，在 dragenter 事件內執行更多更複雜的樣式變化也是可以的。

倘若想要做出圖片更著滑鼠在拖曳目標區上面移動的效果，那麼可以在 dragover 事件內來取得的clientX (en-US)和clientY (en-US)的滑鼠座標資訊。

最後，應該要在 dragleave 事件內復原之前所做樣式變更，dragleave 事件不需要取消預設事件行為、永遠都會觸發，即使拖曳被取消了；至於使用-moz-drag-over 的 CSS 方法的話，樣式復原會自動執行，不用擔心。

執行放置作業
------

當使用者在拖曳目標區上放開滑鼠時，drop 事件就會觸發。當 drop 事件發生，我們需要取出被丟入的資料，然後處理之。

要取出被丟入的資料，那就要呼叫 dataTransfer 物件的getData (en-US)方法。getData 方法接受資料型態的參數，它會回傳setData (en-US)所存入的對應資料型態的資料，倘若沒有對應型態資料，那空字串就會被回傳。

```js
function onDrop(event) {
  var data = event.dataTransfer.getData("text/plain");
  event.target.textContent = data;
  event.preventDefault();
}
```

上面的範例會取出文字資料，假設拖曳目標區是文字區域，例如 p 或 div 元素，那麼資料就會被當作文字內容，插入目標元素之中。

網頁之中，如果我們已經處理過放置資料，那應該要呼叫{preventDefault}方法防止瀏覽器再次處理資料，比如說，Firefox 預設是會開啟拖入的連結，但我們可以取消這項預設行為來避免開啟連結。

當然也可以取得其他種類資料來使用，比如說連結資料，text/uri-list (en-US)。

```js
function doDrop(event)
{
  var links = event.dataTransfer.getData("text/uri-list").split("\n");
  for each (var link in links) {
    if (link.indexOf("#") == 0)
      continue;

    var newlink = document.createElement("a");
    newlink.href = link;
    newlink.textContent = link;
    event.target.appendChild(newlink);
  }
  event.preventDefault();
}
```

上面的範例取得連結資料，然後生成連結元素、加入頁面。從 text/uri-list 字面上不難猜出這種資料是一行行的 URL，所以我們呼叫 split (en-US) 方法拆開一行行的 URL，再將 URL 一個一個加入頁面。請注意我們有避開開頭為「#」字元的註解。

更簡單的作法是採用特別 URL 型態。URL 型態是一個特殊簡寫用途形態，它不會出現在{types}屬性中，但它可以方便的取得第一個連結，如下:

```js
var link = event.dataTransfer.getData("URL");
```

這個作法能夠省去檢查註解和一個一個掃過 URL，但只會得到第一個 URL。

下面的例子會從多個支援的資料型態中，找出支援的資料。

```js
function doDrop(event)
{
  var types = event.dataTransfer.types;
  var supportedTypes = ["application/x-moz-file", "text/uri-list", "text/plain"];
  types = supportedTypes.filter(function (value) types.contains(value));
  if (types.length)
    var data = event.dataTransfer.getData(types[0]);
  event.preventDefault();
}
```

完成拖曳
----

拖曳作業完成後，不論成功或取消於否，被拖曳元素的`dragend` (en-US)事件都會觸發，如果想要判別作業是否完成，可以檢查 dropEffect 屬性，若是 dropEffect 為 none，代表拖曳作業被取消，否則 dropEffect 的值代表所完成的作業類型。

有一個 Gecko 專屬的mozUserCancelled (en-US)屬性，當使用者按 ESC 鍵取消拖曳後，這個屬性會為 true，但若是因其他理由被取消或成功，則為 false

拖曳作業的放置可以發生在同一個視窗或其他應用程式，而且 dragend 事件還是會觸發，不過事件中的screenX (en-US)與screenY (en-US)屬性會是放置發生點的資訊。

當 dragend 事件結束傳遞後，拖曳作業也完成了。

[1] 在 Gecko，如果被拖曳元素在拖曳作業還在進行中移動或刪除，那麼 dragend 事件就不會觸發。bug 460801

參見
--

* HTML Drag and Drop API (Overview)
* Dragging and Dropping Multiple Items
* Recommended Drag Types (en-US)
* HTML5 Living Standard: Drag and Drop


HTMLCanvasElement.toDataURL() - Web APIs

HTMLCanvasElement.toDataURL()
=============================

**`HTMLCanvasElement.toDataURL()`** 方法回傳含有圖像和參數設置特定格式的 data URIs (en-US) (預設 PNG). 回傳的圖像解析度為 96 dpi.

* 如果 canvas 的高度或是寬度為 `0`, 將會回傳字串 `"data:,"`.
* 如果要求的圖像類型並非 `image/png`, 但是回傳的類型卻是 `data:image/png`, 表示要求的圖像類型並不支援.
* Chrome 也支援 `image/webp` 類型.
表達式
---
```

canvas.toDataURL(type, encoderOptions);
```

### 參數

`type` 選擇性

圖像格式的 `DOMString`. 預設為 `image/png`.

`encoderOptions` 選擇性

`表示 image/jpeg 或是 image/webp 的圖像品質, 為0` 到 `1` 之間的 `Number`.
 如果值不在上述範圍中, 將會使用預設值. 其他的會忽略.

### 回傳值

包含 data URI (en-US) 的 `DOMString`.

範例
--

創建 `<canvas>` 元素:

```html
<canvas id="canvas" width="5" height="5"></canvas>
```

可以使用下面的方式獲取 data-URL:

```js
var canvas = document.getElementById("canvas");
var dataURL = canvas.toDataURL();
console.log(dataURL);
// "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNby
// blAAAADElEQVQImWNgoBMAAABpAAFEI8ARAAAAAElFTkSuQmCC"
```

### 設置圖像的品質

```js
var fullQuality = canvas.toDataURL("image/jpeg", 1.0);
// data:image/jpeg;base64,/9j/4AAQSkZJRgABAQ...9oADAMBAAIRAxEAPwD/AD/6AP/Z"
var mediumQuality = canvas.toDataURL("image/jpeg", 0.5);
var lowQuality = canvas.toDataURL("image/jpeg", 0.1);
```

### 範例: 動態改變圖像

為了動態改變圖像, 可以與滑鼠事件一起使用 (gray-scale versus color in this example):

#### HTML

```html
<img class="grayscale" src="myPicture.png" alt="Description of my picture" />
```

#### JavaScript

```js
window.addEventListener("load", removeColors);

function showColorImg() {
  this.style.display = "none";
  this.nextSibling.style.display = "inline";
}

function showGrayImg() {
  this.previousSibling.style.display = "inline";
  this.style.display = "none";
}

function removeColors() {
  var aImages = document.getElementsByClassName("grayscale"),
    nImgsLen = aImages.length,
    oCanvas = document.createElement("canvas"),
    oCtx = oCanvas.getContext("2d");
  for (
    var nWidth, nHeight, oImgData, oGrayImg, nPixel, aPix, nPixLen, nImgId = 0;
    nImgId < nImgsLen;
    nImgId++
  ) {
    oColorImg = aImages[nImgId];
    nWidth = oColorImg.offsetWidth;
    nHeight = oColorImg.offsetHeight;
    oCanvas.width = nWidth;
    oCanvas.height = nHeight;
    oCtx.drawImage(oColorImg, 0, 0);
    oImgData = oCtx.getImageData(0, 0, nWidth, nHeight);
    aPix = oImgData.data;
    nPixLen = aPix.length;
    for (nPixel = 0; nPixel < nPixLen; nPixel += 4) {
      aPix[nPixel + 2] =
        aPix[nPixel + 1] =
        aPix[nPixel] =
          (aPix[nPixel] + aPix[nPixel + 1] + aPix[nPixel + 2]) / 3;
    }
    oCtx.putImageData(oImgData, 0, 0);
    oGrayImg = new Image();
    oGrayImg.src = oCanvas.toDataURL();
    oGrayImg.onmouseover = showColorImg;
    oColorImg.onmouseout = showGrayImg;
    oCtx.clearRect(0, 0, nWidth, nHeight);
    oColorImg.style.display = "none";
    oColorImg.parentNode.insertBefore(oGrayImg, oColorImg);
  }
}
```

規範
--

| Specification |
| --- |
| HTML Standard # dom-canvas-todataurl-dev |

瀏覽器相容性
------

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

參見
--

* The interface defining it, `HTMLCanvasElement` (en-US).
* Data URIs (en-US) in the HTTP reference.


UIEvent() - Web APIs

UIEvent()
=========

**`UIEvent()`** constructor 是用來建立新的 `UIEvent`。

語法
--
```

 event = new UIEvent(typeArg, UIEventInit);
```

### 參數

*typeArg*

一個 `DOMString` ，用來表示事件名稱

*UIEventInit*選擇性

一個 `UIEventInit` dictionary ，能接受以下參數：

`detail`

定義事件意義的值（類型：`long`，默認值：`0`）。關於事件的意義於 `UIEvent.detail` (en-US) 已有較詳盡的列表。

`view`

與事件相關的 `Window` (en-US)，默認值：`null`。

規格
--

| Specification |
| --- |
| UI Events # dom-uievent-uievent |

瀏覽器支援度
------

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

延伸閱讀
----

* `UIEvent`，此 constructer 所建立的 object 的 interface。


Element.clientHeight - Web APIs

Element.clientHeight
====================

**`Element.clientHeight`** 唯讀屬性會回傳元素內部高度（像素），包含 padding 但並未包含水平滾動條、border、margin。

`clientHeight` 可以被計算成 CSS `height` + CSS `padding` - 水平滾動條的高度（如果有顯示）

**備註：** 這個屬性會以四捨五入進位取整數. 如果要使用非整數值, 使用 `element.getBoundingClientRect()` (en-US).

表達式
---
```

var h = element.clientHeight;
```

`h` 代表元素高度(pixels)的正整數.

範例
--

![Image:Dimensions-client.png](/zh-TW/docs/Web/API/Element/clientHeight/dimensions-client.png)

規範
--

| Specification |
| --- |
| CSSOM View Module # dom-element-clientheight |

### 註

`clientHeight` 是在 the Internet Explorer 物件介紹的屬性.

參見
--

* `HTMLElement.offsetHeight` (en-US)
* `Element.scrollHeight`
* Determining the dimensions of elements (en-US)
* MSDN Measuring Element Dimension and Location


Document.width - Web APIs

Document.width
==============

**已棄用:** 不推薦使用此功能。雖可能有一些瀏覽器仍然支援它，但也許已自相關的網頁標準中移除、正準備移除、或僅為了維持相容性而保留。避免使用此功能，盡可能更新現有程式；請參考頁面底部的相容性表格來下決定。請注意：本功能可能隨時停止運作。

**備註：** 從 Gecko 6.0 開始， `document.width` 將不再被支援。取而代之的是 `document.body.clientWidth`。請參照：`element.clientWidth`.

傳回目前文件中，`<body>` (en-US) 元素的寬度有多少像素。

Internet Explorer 不支援！

語法
--

```js
pixels = document.width;
```

範例
--

```js
function init() {
  alert("文件的寬度是 " + document.width + " 像素。");
}
```

其他替代
----

```js
document.body.clientWidth; /\* <body> 的寬度 \*/
document.documentElement.clientWidth; /\* <html> 的寬度 \*/
window.innerWidth; /\* 視窗的寬度 \*/
```

規範於
---

HTML5

同時參考
----

* `document.height` (en-US)
* `Element.clientWidth`
* `Element.scrollWidth` (en-US)


keyup - Web APIs

keyup
=====

當鍵盤上的手指離開按鍵時，`keyup事件會被觸發。`

基本資料
----

詳述

DOM L3

介面

KeyboardEvent (en-US)

Bubbles

Yes

Cancelable

Yes

Target

網頁文件, 網頁元素

Default Action

None

屬性
--

| Specification |
| --- |
| UI Events # event-type-keyup |
| HTML Standard # handler-onkeyup |

Related Events
--------------

* `keydown` (en-US)
* `keyup`
* `keypress` (en-US)
* `input` (en-US)


Element.scrollTop - Web APIs

Element.scrollTop
=================

**`Element.scrollTop`** 屬性可以設置和獲取元素被向上捲動的高度(pixels). 元素的 `scrollTop` 是元素頂端和能被看見的最頂端之間的距離. 當元素並未產生滾動條, 那麼 `scrollTop` 的值預設為 `0`.

表達式
---

```js
// 獲得已經被滾動的距離(pixels)
var intElemScrollTop = someElement.scrollTop;
```

`intElemScrollTop` 為 `element` (en-US)已經被滾動的距離(pixels ).

```js
// 設置已經被滾動的距離(pixels)
element.scrollTop = intValue;
```

`scrollTop` 可以被設置為任何和正整數, 注意事項:

* 如果元素不能滾動, `scrollTop` 被設置為 `0`.
* 如果設置的值小於 `0`, `scrollTop` 被設置為 `0`.
* 如果設置的值大於內容可以被滾動的距離, `scrollTop` 被設置為最大值.
範例
--

### Scrolling an element

In this example, try scrolling the inner container with the dashed border, and see how the value of `scrollTop` changes.

#### HTML

```html
<div id="container">
  <div id="scroller">
    <p>
      Far out in the uncharted backwaters of the unfashionable end of the
      western spiral arm of the Galaxy lies a small unregarded yellow sun.
      Orbiting this at a distance of roughly ninety-two million miles is an
      utterly insignificant little blue green planet whose ape-descended life
      forms are so amazingly primitive that they still think digital watches are
      a pretty neat idea.
    </p>
  </div>
</div>

<div id="output">scrollTop: 0</div>
```

#### CSS

```css
#scroller {
  overflow: scroll;
  height: 150px;
  width: 150px;
  border: 5px dashed orange;
}

#output {
  padding: 1rem 0;
}
```

#### JavaScript

```js
const scroller = document.querySelector("#scroller");
const output = document.querySelector("#output");

scroller.addEventListener("scroll", (event) => {
  output.textContent = `scrollTop: ${scroller.scrollTop}`;
});
```

#### 結果

規範
--

| Specification |
| --- |
| CSSOM View Module # dom-element-scrolltop |

參閱
--

* W3C Draft CSSOM View Module
* MSDN's scrollTop definition
* MSDN's Measuring Element Dimension and Location


Element.scrollHeight - Web APIs

Element.scrollHeight
====================

**`Element.scrollHeight`** 是衡量元素包含因為 overflow 而沒顯示在螢幕上的內容高度的唯讀屬性. `scrollHeight` 的值相等於元素要求 `clientHeight` 在視域中沒有使用滾動條顯示所有內容的最小高度值 . 這當中只包含 padding, 並不包含 margin.

**備註：** 這個屬性會以四捨五入進位取整數. 如果要使用非整數值, 使用 `Element.getBoundingClientRect()` (en-US).

表達式
---

```js
var intElemScrollHeight =
  document.getElementById(id_attribute_value).scrollHeight;
```

*intElemScrollHeight* 是個儲存了元素 scrollHeight 的正整數變數. scrollHeight 是唯讀的屬性.

範例
--

padding-top

Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.

Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.

padding-bottom

**Left** **Top** **Right** **Bottom** *margin-top* *margin-bottom* *border-top* *border-bottom*

![Image:scrollHeight.png](/zh-TW/docs/Web/API/Element/scrollHeight/scrollheight.png)

問題與解決方法
-------

### 了解元素是否被滾輪完全滾過

下面的等式代表`如果元素被完全滾過將會`回傳 `true`, 否則回傳 `false`.

```js
element.scrollHeight - element.scrollTop === element.clientHeight;
```

scrollHeight 範例
---------------

藉由 `onscroll` (en-US) 事件, 這個等式對於決定使用者是否已經讀完文字內容是很有用 (參見 `element.scrollTop` (en-US), `element.clientHeight` (en-US) 屬性). 範例:

### HTML

```html
<form name="registration">
  <p>
    <textarea id="rules">
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum at laoreet magna.
Aliquam erat volutpat. Praesent molestie, dolor ut eleifend aliquam, mi ligula ultrices sapien, quis cursus
neque dui nec risus. Duis tincidunt lobortis purus eu aliquet. Quisque in dignissim magna. Aenean ac lorem at
velit ultrices consequat. Nulla luctus nisi ut libero cursus ultrices. Pellentesque nec dignissim enim. Phasellus
ut quam lacus, sed ultricies diam. Vestibulum convallis rutrum dolor, sit amet egestas velit scelerisque id.
Proin non dignissim nisl. Sed mi odio, ullamcorper eget mattis id, malesuada vitae libero. Integer dolor lorem,
mattis sed dapibus a, faucibus id metus. Duis iaculis dictum pulvinar. In nisi nibh, dapibus ac blandit at, porta
at arcu. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Praesent
dictum ipsum aliquet erat eleifend sit amet sollicitudin felis tempus. Aliquam congue cursus venenatis. Maecenas
luctus pellentesque placerat. Mauris nisl odio, condimentum sed fringilla a, consectetur id ligula. Praesent sem
sem, aliquet non faucibus vitae, iaculis nec elit. Nullam volutpat, lectus et blandit bibendum, nulla lorem congue
turpis, ac pretium tortor sem ut nibh. Donec vel mi in ligula hendrerit sagittis. Donec faucibus viverra fermentum.
Fusce in arcu arcu. Nullam at dignissim massa. Cras nibh est, pretium sit amet faucibus eget, sollicitudin in
ligula. Vivamus vitae urna mauris, eget euismod nunc. Aenean semper gravida enim non feugiat. In hac habitasse
platea dictumst. Cras eleifend nisl volutpat ante condimentum convallis. Donec varius dolor malesuada erat
consequat congue. Donec eu lacus ut sapien venenatis tincidunt. Quisque sit amet tellus et enim bibendum varius et
a orci. Donec aliquet volutpat scelerisque. Proin et tortor dolor. Ut aliquet, dolor a mattis sodales, odio diam
pulvinar sem, egestas pretium magna eros vitae felis. Nam vitae magna lectus, et ornare elit. Morbi feugiat, ipsum
ac mattis congue, quam neque mollis tortor, nec mollis nisl dolor a tortor. Maecenas varius est sit amet elit
interdum quis placerat metus posuere. Duis malesuada justo a diam vestibulum vel aliquam nisi ornare. Integer
laoreet nisi a odio ornare non congue turpis eleifend. Cum sociis natoque penatibus et magnis dis parturient montes,
nascetur ridiculus mus. Cras vulputate libero sed arcu iaculis nec lobortis orci fermentum.
    </textarea>
  </p>
  <p>
    <input type="checkbox" name="accept" id="agree" />
    <label for="agree">I agree</label>
    <input type="submit" id="nextstep" value="Next" />
  </p>
</form>
```

### CSS

```css
#notice {
  display: inline-block;
  margin-bottom: 12px;
  border-radius: 5px;
  width: 600px;
  padding: 5px;
  border: 2px #7fdf55 solid;
}

#rules {
  width: 600px;
  height: 130px;
  padding: 5px;
  border: #2a9f00 solid 2px;
  border-radius: 5px;
}
```

### JavaScript

```js
function checkReading() {
  if (checkReading.read) {
    return;
  }
  checkReading.read = this.scrollHeight - this.scrollTop === this.clientHeight;
  document.registration.accept.disabled = document.getElementById(
    "nextstep",
  ).disabled = !checkReading.read;
  checkReading.noticeBox.innerHTML = checkReading.read
    ? "Thank you."
    : "Please, scroll and read the following text.";
}

onload = function () {
  var oToBeRead = document.getElementById("rules");
  checkReading.noticeBox = document.createElement("span");
  document.registration.accept.checked = false;
  checkReading.noticeBox.id = "notice";
  oToBeRead.parentNode.insertBefore(checkReading.noticeBox, oToBeRead);
  oToBeRead.parentNode.insertBefore(document.createElement("br"), oToBeRead);
  oToBeRead.onscroll = checkReading;
  checkReading.call(oToBeRead);
};
```

規範
--

| Specification |
| --- |
| CSSOM View Module # dom-element-scrollheight |

瀏覽器相容性
------

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

參見
--

* MSDN: Measuring Element Dimension and Location with CSSOM in Windows Internet Explorer 9
* `Element.clientHeight`
* `Element.offsetHeight`
* Determining the dimensions of elements (en-US)