# Document 節點
## 概述
`document`節點對象代表整個文檔,每張網頁都有自己的`document`對象。`window.document`屬性就指向這個對象。只要瀏覽器開始載入 HTML 文檔,該對象就存在了,可以直接使用。
`document`對象有不同的辦法可以獲取。
- 正常的網頁,直接使用`document`或`window.document`。
- `iframe`框架里面的網頁,使用`iframe`節點的`contentDocument`屬性。
- Ajax 操作返回的文檔,使用`XMLHttpRequest`對象的`responseXML`屬性。
- 內部節點的`ownerDocument`屬性。
`document`對象繼承了`EventTarget`接口和`Node`接口,并且混入(mixin)了`ParentNode`接口。這意味著,這些接口的方法都可以在`document`對象上調用。除此之外,`document`對象還有很多自己的屬性和方法。
## 屬性
### 快捷方式屬性
以下屬性是指向文檔內部的某個節點的快捷方式。
**(1)document.defaultView**
`document.defaultView`屬性返回`document`對象所屬的`window`對象。如果當前文檔不屬于`window`對象,該屬性返回`null`。
```javascript
document.defaultView === window // true
```
**(2)document.doctype**
對于 HTML 文檔來說,`document`對象一般有兩個子節點。第一個子節點是`document.doctype`,指向`<DOCTYPE>`節點,即文檔類型(Document Type Declaration,簡寫DTD)節點。HTML 的文檔類型節點,一般寫成`<!DOCTYPE html>`。如果網頁沒有聲明 DTD,該屬性返回`null`。
```javascript
var doctype = document.doctype;
doctype // "<!DOCTYPE html>"
doctype.name // "html"
```
`document.firstChild`通常就返回這個節點。
**(3)document.documentElement**
`document.documentElement`屬性返回當前文檔的根元素節點(root)。它通常是`document`節點的第二個子節點,緊跟在`document.doctype`節點后面。HTML網頁的該屬性,一般是`<html>`節點。
**(4)document.body,document.head**
`document.body`屬性指向`<body>`節點,`document.head`屬性指向`<head>`節點。
這兩個屬性總是存在的,如果網頁源碼里面省略了`<head>`或`<body>`,瀏覽器會自動創建。另外,這兩個屬性是可寫的,如果改寫它們的值,相當于移除所有子節點。
**(5)document.scrollingElement**
`document.scrollingElement`屬性返回文檔的滾動元素。也就是說,當文檔整體滾動時,到底是哪個元素在滾動。
標準模式下,這個屬性返回的文檔的根元素`document.documentElement`(即`<html>`)。兼容(quirk)模式下,返回的是`<body>`元素,如果該元素不存在,返回`null`。
```javascript
// 頁面滾動到瀏覽器頂部
document.scrollingElement.scrollTop = 0;
```
**(6)document.activeElement**
`document.activeElement`屬性返回獲得當前焦點(focus)的 DOM 元素。通常,這個屬性返回的是`<input>`、`<textarea>`、`<select>`等表單元素,如果當前沒有焦點元素,返回`<body>`元素或`null`。
**(7)document.fullscreenElement**
`document.fullscreenElement`屬性返回當前以全屏狀態展示的 DOM 元素。如果不是全屏狀態,該屬性返回`null`。
```javascript
if (document.fullscreenElement.nodeName == 'VIDEO') {
console.log('全屏播放視頻');
}
```
上面代碼中,通過`document.fullscreenElement`可以知道`<video>`元素有沒有處在全屏狀態,從而判斷用戶行為。
### 節點集合屬性
以下屬性返回一個`HTMLCollection`實例,表示文檔內部特定元素的集合。這些集合都是動態的,原節點有任何變化,立刻會反映在集合中。
**(1)document.links**
`document.links`屬性返回當前文檔所有設定了`href`屬性的`<a>`及`<area>`節點。
```javascript
// 打印文檔所有的鏈接
var links = document.links;
for(var i = 0; i < links.length; i++) {
console.log(links[i]);
}
```
**(2)document.forms**
`document.forms`屬性返回所有`<form>`表單節點。
```javascript
var selectForm = document.forms[0];
```
上面代碼獲取文檔第一個表單。
除了使用位置序號,`id`屬性和`name`屬性也可以用來引用表單。
```javascript
/* HTML 代碼如下
<form name="foo" id="bar"></form>
*/
document.forms[0] === document.forms.foo // true
document.forms.bar === document.forms.foo // true
```
**(3)document.images**
`document.images`屬性返回頁面所有`<img>`圖片節點。
```javascript
var imglist = document.images;
for(var i = 0; i < imglist.length; i++) {
if (imglist[i].src === 'banner.gif') {
// ...
}
}
```
上面代碼在所有`img`標簽中,尋找某張圖片。
**(4)document.embeds,document.plugins**
`document.embeds`屬性和`document.plugins`屬性,都返回所有`<embed>`節點。
**(5)document.scripts**
`document.scripts`屬性返回所有`<script>`節點。
```javascript
var scripts = document.scripts;
if (scripts.length !== 0 ) {
console.log('當前網頁有腳本');
}
```
**(6)document.styleSheets**
`document.styleSheets`屬性返回文檔內嵌或引入的樣式表集合,詳細介紹請看《CSS 對象模型》一章。
**(7)小結**
除了`document.styleSheets`,以上的集合屬性返回的都是`HTMLCollection`實例。
```javascript
document.links instanceof HTMLCollection // true
document.images instanceof HTMLCollection // true
document.forms instanceof HTMLCollection // true
document.embeds instanceof HTMLCollection // true
document.scripts instanceof HTMLCollection // true
```
`HTMLCollection`實例是類似數組的對象,所以這些屬性都有`length`屬性,都可以使用方括號運算符引用成員。如果成員有`id`或`name`屬性,還可以用這兩個屬性的值,在`HTMLCollection`實例上引用到這個成員。
```javascript
// HTML 代碼如下
// <form name="myForm">
document.myForm === document.forms.myForm // true
```
### 文檔靜態信息屬性
以下屬性返回文檔信息。
**(1)document.documentURI,document.URL**
`document.documentURI`屬性和`document.URL`屬性都返回一個字符串,表示當前文檔的網址。不同之處是它們繼承自不同的接口,`documentURI`繼承自`Document`接口,可用于所有文檔;`URL`繼承自`HTMLDocument`接口,只能用于 HTML 文檔。
```javascript
document.URL
// http://www.example.com/about
document.documentURI === document.URL
// true
```
如果文檔的錨點(`#anchor`)變化,這兩個屬性都會跟著變化。
**(2)document.domain**
`document.domain`屬性返回當前文檔的域名,不包含協議和端口。比如,網頁的網址是`http://www.example.com:80/hello.html`,那么`document.domain`屬性就等于`www.example.com`。如果無法獲取域名,該屬性返回`null`。
`document.domain`基本上是一個只讀屬性,只有一種情況除外。次級域名的網頁,可以把`document.domain`設為對應的上級域名。比如,當前域名是`a.sub.example.com`,則`document.domain`屬性可以設置為`sub.example.com`,也可以設為`example.com`。修改后,`document.domain`相同的兩個網頁,可以讀取對方的資源,比如設置的 Cookie。
另外,設置`document.domain`會導致端口被改成`null`。因此,如果通過設置`document.domain`來進行通信,雙方網頁都必須設置這個值,才能保證端口相同。
**(3)document.location**
`Location`對象是瀏覽器提供的原生對象,提供 URL 相關的信息和操作方法。通過`window.location`和`document.location`屬性,可以拿到這個對象。
關于這個對象的詳細介紹,請看《瀏覽器模型》部分的《Location 對象》章節。
**(4)document.lastModified**
`document.lastModified`屬性返回一個字符串,表示當前文檔最后修改的時間。不同瀏覽器的返回值,日期格式是不一樣的。
```javascript
document.lastModified
// "03/07/2018 11:18:27"
```
注意,`document.lastModified`屬性的值是字符串,所以不能直接用來比較。`Date.parse`方法將其轉為`Date`實例,才能比較兩個網頁。
```javascript
var lastVisitedDate = Date.parse('01/01/2018');
if (Date.parse(document.lastModified) > lastVisitedDate) {
console.log('網頁已經變更');
}
```
如果頁面上有 JavaScript 生成的內容,`document.lastModified`屬性返回的總是當前時間。
**(5)document.title**
`document.title`屬性返回當前文檔的標題。默認情況下,返回`<title>`節點的值。但是該屬性是可寫的,一旦被修改,就返回修改后的值。
```javascript
document.title = '新標題';
document.title // "新標題"
```
**(6)document.characterSet**
`document.characterSet`屬性返回當前文檔的編碼,比如`UTF-8`、`ISO-8859-1`等等。
**(7)document.referrer**
`document.referrer`屬性返回一個字符串,表示當前文檔的訪問者來自哪里。
```javascript
document.referrer
// "https://example.com/path"
```
如果無法獲取來源,或者用戶直接鍵入網址而不是從其他網頁點擊進入,`document.referrer`返回一個空字符串。
`document.referrer`的值,總是與 HTTP 頭信息的`Referer`字段保持一致。但是,`document.referrer`的拼寫有兩個`r`,而頭信息的`Referer`字段只有一個`r`。
**(8)document.dir**
`document.dir`返回一個字符串,表示文字方向。它只有兩個可能的值:`rtl`表示文字從右到左,阿拉伯文是這種方式;`ltr`表示文字從左到右,包括英語和漢語在內的大多數文字采用這種方式。
**(9)document.compatMode**
`compatMode`屬性返回瀏覽器處理文檔的模式,可能的值為`BackCompat`(向后兼容模式)和`CSS1Compat`(嚴格模式)。
一般來說,如果網頁代碼的第一行設置了明確的`DOCTYPE`(比如`<!doctype html>`),`document.compatMode`的值都為`CSS1Compat`。
### 文檔狀態屬性
**(1)document.hidden**
`document.hidden`屬性返回一個布爾值,表示當前頁面是否可見。如果窗口最小化、瀏覽器切換了 Tab,都會導致導致頁面不可見,使得`document.hidden`返回`true`。
這個屬性是 Page Visibility API 引入的,一般都是配合這個 API 使用。
**(2)document.visibilityState**
`document.visibilityState`返回文檔的可見狀態。
它的值有四種可能。
> - `visible`:頁面可見。注意,頁面可能是部分可見,即不是焦點窗口,前面被其他窗口部分擋住了。
> - `hidden`:頁面不可見,有可能窗口最小化,或者瀏覽器切換到了另一個 Tab。
> - `prerender`:頁面處于正在渲染狀態,對于用戶來說,該頁面不可見。
> - `unloaded`:頁面從內存里面卸載了。
這個屬性可以用在頁面加載時,防止加載某些資源;或者頁面不可見時,停掉一些頁面功能。
**(3)document.readyState**
`document.readyState`屬性返回當前文檔的狀態,共有三種可能的值。
- `loading`:加載 HTML 代碼階段(尚未完成解析)
- `interactive`:加載外部資源階段
- `complete`:加載完成
這個屬性變化的過程如下。
1. 瀏覽器開始解析 HTML 文檔,`document.readyState`屬性等于`loading`。
1. 瀏覽器遇到 HTML 文檔中的`<script>`元素,并且沒有`async`或`defer`屬性,就暫停解析,開始執行腳本,這時`document.readyState`屬性還是等于`loading`。
1. HTML 文檔解析完成,`document.readyState`屬性變成`interactive`。
1. 瀏覽器等待圖片、樣式表、字體文件等外部資源加載完成,一旦全部加載完成,`document.readyState`屬性變成`complete`。
下面的代碼用來檢查網頁是否加載成功。
```javascript
// 基本檢查
if (document.readyState === 'complete') {
// ...
}
// 輪詢檢查
var interval = setInterval(function() {
if (document.readyState === 'complete') {
clearInterval(interval);
// ...
}
}, 100);
```
另外,每次狀態變化都會觸發一個`readystatechange`事件。
### document.cookie
`document.cookie`屬性用來操作瀏覽器 Cookie,詳見《瀏覽器模型》部分的《Cookie》章節。
### document.designMode
`document.designMode`屬性控制當前文檔是否可編輯。該屬性只有兩個值`on`和`off`,默認值為`off`。一旦設為`on`,用戶就可以編輯整個文檔的內容。
下面代碼打開`iframe`元素內部文檔的`designMode`屬性,就能將其變為一個所見即所得的編輯器。
```javascript
// HTML 代碼如下
// <iframe id="editor" src="about:blank"></iframe>
var editor = document.getElementById('editor');
editor.contentDocument.designMode = 'on';
```
### document.currentScript
`document.currentScript`屬性只用在`<script>`元素的內嵌腳本或加載的外部腳本之中,返回當前腳本所在的那個 DOM 節點,即`<script>`元素的 DOM 節點。
```html
<script id="foo">
console.log(
document.currentScript === document.getElementById('foo')
); // true
</script>
```
上面代碼中,`document.currentScript`就是`<script>`元素節點。
### document.implementation
`document.implementation`屬性返回一個`DOMImplementation`對象。該對象有三個方法,主要用于創建獨立于當前文檔的新的 Document 對象。
- `DOMImplementation.createDocument()`:創建一個 XML 文檔。
- `DOMImplementation.createHTMLDocument()`:創建一個 HTML 文檔。
- `DOMImplementation.createDocumentType()`:創建一個 DocumentType 對象。
下面是創建 HTML 文檔的例子。
```javascript
var doc = document.implementation.createHTMLDocument('Title');
var p = doc.createElement('p');
p.innerHTML = 'hello world';
doc.body.appendChild(p);
document.replaceChild(
doc.documentElement,
document.documentElement
);
```
上面代碼中,第一步生成一個新的 HTML 文檔`doc`,然后用它的根元素`document.documentElement`替換掉`document.documentElement`。這會使得當前文檔的內容全部消失,變成`hello world`。
## 方法
### document.open(),document.close()
`document.open`方法清除當前文檔所有內容,使得文檔處于可寫狀態,供`document.write`方法寫入內容。
`document.close`方法用來關閉`document.open()`打開的文檔。
```javascript
document.open();
document.write('hello world');
document.close();
```
### document.write(),document.writeln()
`document.write`方法用于向當前文檔寫入內容。
在網頁的首次渲染階段,只要頁面沒有關閉寫入(即沒有執行`document.close()`),`document.write`寫入的內容就會追加在已有內容的后面。
```javascript
// 頁面顯示“helloworld”
document.open();
document.write('hello');
document.write('world');
document.close();
```
注意,`document.write`會當作 HTML 代碼解析,不會轉義。
```javascript
document.write('<p>hello world</p>');
```
上面代碼中,`document.write`會將`<p>`當作 HTML 標簽解釋。
如果頁面已經解析完成(`DOMContentLoaded`事件發生之后),再調用`write`方法,它會先調用`open`方法,擦除當前文檔所有內容,然后再寫入。
```javascript
document.addEventListener('DOMContentLoaded', function (event) {
document.write('<p>Hello World!</p>');
});
// 等同于
document.addEventListener('DOMContentLoaded', function (event) {
document.open();
document.write('<p>Hello World!</p>');
document.close();
});
```
如果在頁面渲染過程中調用`write`方法,并不會自動調用`open`方法。(可以理解成,`open`方法已調用,但`close`方法還未調用。)
```html
<html>
<body>
hello
<script type="text/javascript">
document.write("world")
</script>
</body>
</html>
```
在瀏覽器打開上面網頁,將會顯示`hello world`。
`document.write`是 JavaScript 語言標準化之前就存在的方法,現在完全有更符合標準的方法向文檔寫入內容(比如對`innerHTML`屬性賦值)。所以,除了某些特殊情況,應該盡量避免使用`document.write`這個方法。
`document.writeln`方法與`write`方法完全一致,除了會在輸出內容的尾部添加換行符。
```javascript
document.write(1);
document.write(2);
// 12
document.writeln(1);
document.writeln(2);
// 1
// 2
//
```
注意,`writeln`方法添加的是 ASCII 碼的換行符,渲染成 HTML 網頁時不起作用,即在網頁上顯示不出換行。網頁上的換行,必須顯式寫入`<br>`。
### document.querySelector(),document.querySelectorAll()
`document.querySelector`方法接受一個 CSS 選擇器作為參數,返回匹配該選擇器的元素節點。如果有多個節點滿足匹配條件,則返回第一個匹配的節點。如果沒有發現匹配的節點,則返回`null`。
```javascript
var el1 = document.querySelector('.myclass');
var el2 = document.querySelector('#myParent > [ng-click]');
```
`document.querySelectorAll`方法與`querySelector`用法類似,區別是返回一個`NodeList`對象,包含所有匹配給定選擇器的節點。
```javascript
elementList = document.querySelectorAll('.myclass');
```
這兩個方法的參數,可以是逗號分隔的多個 CSS 選擇器,返回匹配其中一個選擇器的元素節點,這與 CSS 選擇器的規則是一致的。
```javascript
var matches = document.querySelectorAll('div.note, div.alert');
```
上面代碼返回`class`屬性是`note`或`alert`的`div`元素。
這兩個方法都支持復雜的 CSS 選擇器。
```javascript
// 選中 data-foo-bar 屬性等于 someval 的元素
document.querySelectorAll('[data-foo-bar="someval"]');
// 選中 myForm 表單中所有不通過驗證的元素
document.querySelectorAll('#myForm :invalid');
// 選中div元素,那些 class 含 ignore 的除外
document.querySelectorAll('DIV:not(.ignore)');
// 同時選中 div,a,script 三類元素
document.querySelectorAll('DIV, A, SCRIPT');
```
但是,它們不支持 CSS 偽元素的選擇器(比如`:first-line`和`:first-letter`)和偽類的選擇器(比如`:link`和`:visited`),即無法選中偽元素和偽類。
如果`querySelectorAll`方法的參數是字符串`*`,則會返回文檔中的所有元素節點。另外,`querySelectorAll`的返回結果不是動態集合,不會實時反映元素節點的變化。
最后,這兩個方法除了定義在`document`對象上,還定義在元素節點上,即在元素節點上也可以調用。
### document.getElementsByTagName()
`document.getElementsByTagName()`方法搜索 HTML 標簽名,返回符合條件的元素。它的返回值是一個類似數組對象(`HTMLCollection`實例),可以實時反映 HTML 文檔的變化。如果沒有任何匹配的元素,就返回一個空集。
```javascript
var paras = document.getElementsByTagName('p');
paras instanceof HTMLCollection // true
```
上面代碼返回當前文檔的所有`p`元素節點。
HTML 標簽名是大小寫不敏感的,因此`getElementsByTagName()`方法的參數也是大小寫不敏感的。另外,返回結果中,各個成員的順序就是它們在文檔中出現的順序。
如果傳入`*`,就可以返回文檔中所有 HTML 元素。
```javascript
var allElements = document.getElementsByTagName('*');
```
注意,元素節點本身也定義了`getElementsByTagName`方法,返回該元素的后代元素中符合條件的元素。也就是說,這個方法不僅可以在`document`對象上調用,也可以在任何元素節點上調用。
```javascript
var firstPara = document.getElementsByTagName('p')[0];
var spans = firstPara.getElementsByTagName('span');
```
上面代碼選中第一個`p`元素內部的所有`span`元素。
### document.getElementsByClassName()
`document.getElementsByClassName()`方法返回一個類似數組的對象(`HTMLCollection`實例),包括了所有`class`名字符合指定條件的元素,元素的變化實時反映在返回結果中。
```javascript
var elements = document.getElementsByClassName(names);
```
由于`class`是保留字,所以 JavaScript 一律使用`className`表示 CSS 的`class`。
參數可以是多個`class`,它們之間使用空格分隔。
```javascript
var elements = document.getElementsByClassName('foo bar');
```
上面代碼返回同時具有`foo`和`bar`兩個`class`的元素,`foo`和`bar`的順序不重要。
注意,正常模式下,CSS 的`class`是大小寫敏感的。(`quirks mode`下,大小寫不敏感。)
與`getElementsByTagName()`方法一樣,`getElementsByClassName()`方法不僅可以在`document`對象上調用,也可以在任何元素節點上調用。
```javascript
// 非document對象上調用
var elements = rootElement.getElementsByClassName(names);
```
### document.getElementsByName()
`document.getElementsByName()`方法用于選擇擁有`name`屬性的 HTML 元素(比如`<form>`、`<radio>`、`<img>`、`<frame>`、`<embed>`和`<object>`等),返回一個類似數組的的對象(`NodeList`實例),因為`name`屬性相同的元素可能不止一個。
```javascript
// 表單為 <form name="x"></form>
var forms = document.getElementsByName('x');
forms[0].tagName // "FORM"
```
### document.getElementById()
`document.getElementById()`方法返回匹配指定`id`屬性的元素節點。如果沒有發現匹配的節點,則返回`null`。
```javascript
var elem = document.getElementById('para1');
```
注意,該方法的參數是大小寫敏感的。比如,如果某個節點的`id`屬性是`main`,那么`document.getElementById('Main')`將返回`null`。
`document.getElementById()`方法與`document.querySelector()`方法都能獲取元素節點,不同之處是`document.querySelector()`方法的參數使用 CSS 選擇器語法,`document.getElementById()`方法的參數是元素的`id`屬性。
```javascript
document.getElementById('myElement')
document.querySelector('#myElement')
```
上面代碼中,兩個方法都能選中`id`為`myElement`的元素,但是`document.getElementById()`比`document.querySelector()`效率高得多。
另外,這個方法只能在`document`對象上使用,不能在其他元素節點上使用。
### document.elementFromPoint(),document.elementsFromPoint()
`document.elementFromPoint()`方法返回位于頁面指定位置最上層的元素節點。
```javascript
var element = document.elementFromPoint(50, 50);
```
上面代碼選中在`(50, 50)`這個坐標位置的最上層的那個 HTML 元素。
`elementFromPoint`方法的兩個參數,依次是相對于當前視口左上角的橫坐標和縱坐標,單位是像素。如果位于該位置的 HTML 元素不可返回(比如文本框的滾動條),則返回它的父元素(比如文本框)。如果坐標值無意義(比如負值或超過視口大小),則返回`null`。
`document.elementsFromPoint()`返回一個數組,成員是位于指定坐標(相對于視口)的所有元素。
```javascript
var elements = document.elementsFromPoint(x, y);
```
### document.createElement()
`document.createElement`方法用來生成元素節點,并返回該節點。
```javascript
var newDiv = document.createElement('div');
```
`createElement`方法的參數為元素的標簽名,即元素節點的`tagName`屬性,對于 HTML 網頁大小寫不敏感,即參數為`div`或`DIV`返回的是同一種節點。如果參數里面包含尖括號(即`<`和`>`)會報錯。
```javascript
document.createElement('<div>');
// DOMException: The tag name provided ('<div>') is not a valid name
```
注意,`document.createElement`的參數可以是自定義的標簽名。
```javascript
document.createElement('foo');
```
### document.createTextNode()
`document.createTextNode`方法用來生成文本節點(`Text`實例),并返回該節點。它的參數是文本節點的內容。
```javascript
var newDiv = document.createElement('div');
var newContent = document.createTextNode('Hello');
newDiv.appendChild(newContent);
```
上面代碼新建一個`div`節點和一個文本節點,然后將文本節點插入`div`節點。
這個方法可以確保返回的節點,被瀏覽器當作文本渲染,而不是當作 HTML 代碼渲染。因此,可以用來展示用戶的輸入,避免 XSS 攻擊。
```javascript
var div = document.createElement('div');
div.appendChild(document.createTextNode('<span>Foo & bar</span>'));
console.log(div.innerHTML)
// <span>Foo & bar</span>
```
上面代碼中,`createTextNode`方法對大于號和小于號進行轉義,從而保證即使用戶輸入的內容包含惡意代碼,也能正確顯示。
需要注意的是,該方法不對單引號和雙引號轉義,所以不能用來對 HTML 屬性賦值。
```html
function escapeHtml(str) {
var div = document.createElement('div');
div.appendChild(document.createTextNode(str));
return div.innerHTML;
};
var userWebsite = '" onmouseover="alert(\'derp\')" "';
var profileLink = '<a href="' + escapeHtml(userWebsite) + '">Bob</a>';
var div = document.getElementById('target');
div.innerHTML = profileLink;
// <a href="" onmouseover="alert('derp')" "">Bob</a>
```
上面代碼中,由于`createTextNode`方法不轉義雙引號,導致`onmouseover`方法被注入了代碼。
### document.createAttribute()
`document.createAttribute`方法生成一個新的屬性節點(`Attr`實例),并返回它。
```javascript
var attribute = document.createAttribute(name);
```
`document.createAttribute`方法的參數`name`,是屬性的名稱。
```javascript
var node = document.getElementById('div1');
var a = document.createAttribute('my_attrib');
a.value = 'newVal';
node.setAttributeNode(a);
// 或者
node.setAttribute('my_attrib', 'newVal');
```
上面代碼為`div1`節點,插入一個值為`newVal`的`my_attrib`屬性。
### document.createComment()
`document.createComment`方法生成一個新的注釋節點,并返回該節點。
```javascript
var CommentNode = document.createComment(data);
```
`document.createComment`方法的參數是一個字符串,會成為注釋節點的內容。
### document.createDocumentFragment()
`document.createDocumentFragment`方法生成一個空的文檔片段對象(`DocumentFragment`實例)。
```javascript
var docFragment = document.createDocumentFragment();
```
`DocumentFragment`是一個存在于內存的 DOM 片段,不屬于當前文檔,常常用來生成一段較復雜的 DOM 結構,然后再插入當前文檔。這樣做的好處在于,因為`DocumentFragment`不屬于當前文檔,對它的任何改動,都不會引發網頁的重新渲染,比直接修改當前文檔的 DOM 有更好的性能表現。
```javascript
var docfrag = document.createDocumentFragment();
[1, 2, 3, 4].forEach(function (e) {
var li = document.createElement('li');
li.textContent = e;
docfrag.appendChild(li);
});
var element = document.getElementById('ul');
element.appendChild(docfrag);
```
上面代碼中,文檔片斷`docfrag`包含四個`<li>`節點,這些子節點被一次性插入了當前文檔。
### document.createEvent()
`document.createEvent`方法生成一個事件對象(`Event`實例),該對象可以被`element.dispatchEvent`方法使用,觸發指定事件。
```javascript
var event = document.createEvent(type);
```
`document.createEvent`方法的參數是事件類型,比如`UIEvents`、`MouseEvents`、`MutationEvents`、`HTMLEvents`。
```javascript
var event = document.createEvent('Event');
event.initEvent('build', true, true);
document.addEventListener('build', function (e) {
console.log(e.type); // "build"
}, false);
document.dispatchEvent(event);
```
上面代碼新建了一個名為`build`的事件實例,然后觸發該事件。
### document.addEventListener(),document.removeEventListener(),document.dispatchEvent()
這三個方法用于處理`document`節點的事件。它們都繼承自`EventTarget`接口,詳細介紹參見《EventTarget 接口》一章。
```javascript
// 添加事件監聽函數
document.addEventListener('click', listener, false);
// 移除事件監聽函數
document.removeEventListener('click', listener, false);
// 觸發事件
var event = new Event('click');
document.dispatchEvent(event);
```
### document.hasFocus()
`document.hasFocus`方法返回一個布爾值,表示當前文檔之中是否有元素被激活或獲得焦點。
```javascript
var focused = document.hasFocus();
```
注意,有焦點的文檔必定被激活(active),反之不成立,激活的文檔未必有焦點。比如,用戶點擊按鈕,從當前窗口跳出一個新窗口,該新窗口就是激活的,但是不擁有焦點。
### document.adoptNode(),document.importNode()
`document.adoptNode`方法將某個節點及其子節點,從原來所在的文檔或`DocumentFragment`里面移除,歸屬當前`document`對象,返回插入后的新節點。插入的節點對象的`ownerDocument`屬性,會變成當前的`document`對象,而`parentNode`屬性是`null`。
```javascript
var node = document.adoptNode(externalNode);
document.appendChild(node);
```
注意,`document.adoptNode`方法只是改變了節點的歸屬,并沒有將這個節點插入新的文檔樹。所以,還要再用`appendChild`方法或`insertBefore`方法,將新節點插入當前文檔樹。
`document.importNode`方法則是從原來所在的文檔或`DocumentFragment`里面,拷貝某個節點及其子節點,讓它們歸屬當前`document`對象。拷貝的節點對象的`ownerDocument`屬性,會變成當前的`document`對象,而`parentNode`屬性是`null`。
```javascript
var node = document.importNode(externalNode, deep);
```
`document.importNode`方法的第一個參數是外部節點,第二個參數是一個布爾值,表示對外部節點是深拷貝還是淺拷貝,默認是淺拷貝(false)。雖然第二個參數是可選的,但是建議總是保留這個參數,并設為`true`。
注意,`document.importNode`方法只是拷貝外部節點,這時該節點的父節點是`null`。下一步還必須將這個節點插入當前文檔樹。
```javascript
var iframe = document.getElementsByTagName('iframe')[0];
var oldNode = iframe.contentWindow.document.getElementById('myNode');
var newNode = document.importNode(oldNode, true);
document.getElementById("container").appendChild(newNode);
```
上面代碼從`iframe`窗口,拷貝一個指定節點`myNode`,插入當前文檔。
### document.createNodeIterator()
`document.createNodeIterator`方法返回一個子節點遍歷器。
```javascript
var nodeIterator = document.createNodeIterator(
document.body,
NodeFilter.SHOW_ELEMENT
);
```
上面代碼返回`<body>`元素子節點的遍歷器。
`document.createNodeIterator`方法第一個參數為所要遍歷的根節點,第二個參數為所要遍歷的節點類型,這里指定為元素節點(`NodeFilter.SHOW_ELEMENT`)。幾種主要的節點類型寫法如下。
- 所有節點:NodeFilter.SHOW_ALL
- 元素節點:NodeFilter.SHOW_ELEMENT
- 文本節點:NodeFilter.SHOW_TEXT
- 評論節點:NodeFilter.SHOW_COMMENT
`document.createNodeIterator`方法返回一個“遍歷器”對象(`NodeFilter`實例)。該實例的`nextNode()`方法和`previousNode()`方法,可以用來遍歷所有子節點。
```javascript
var nodeIterator = document.createNodeIterator(document.body);
var pars = [];
var currentNode;
while (currentNode = nodeIterator.nextNode()) {
pars.push(currentNode);
}
```
上面代碼中,使用遍歷器的`nextNode`方法,將根節點的所有子節點,依次讀入一個數組。`nextNode`方法先返回遍歷器的內部指針所在的節點,然后會將指針移向下一個節點。所有成員遍歷完成后,返回`null`。`previousNode`方法則是先將指針移向上一個節點,然后返回該節點。
```javascript
var nodeIterator = document.createNodeIterator(
document.body,
NodeFilter.SHOW_ELEMENT
);
var currentNode = nodeIterator.nextNode();
var previousNode = nodeIterator.previousNode();
currentNode === previousNode // true
```
上面代碼中,`currentNode`和`previousNode`都指向同一個的節點。
注意,遍歷器返回的第一個節點,總是根節點。
```javascript
pars[0] === document.body // true
```
### document.createTreeWalker()
`document.createTreeWalker`方法返回一個 DOM 的子樹遍歷器。它與`document.createNodeIterator`方法基本是類似的,區別在于它返回的是`TreeWalker`實例,后者返回的是`NodeIterator`實例。另外,它的第一個節點不是根節點。
`document.createTreeWalker`方法的第一個參數是所要遍歷的根節點,第二個參數指定所要遍歷的節點類型(與`document.createNodeIterator`方法的第二個參數相同)。
```javascript
var treeWalker = document.createTreeWalker(
document.body,
NodeFilter.SHOW_ELEMENT
);
var nodeList = [];
while(treeWalker.nextNode()) {
nodeList.push(treeWalker.currentNode);
}
```
上面代碼遍歷`<body>`節點下屬的所有元素節點,將它們插入`nodeList`數組。
### document.execCommand(),document.queryCommandSupported(),document.queryCommandEnabled()
**(1)document.execCommand()**
如果`document.designMode`屬性設為`on`,那么整個文檔用戶可編輯;如果元素的`contenteditable`屬性設為`true`,那么該元素可編輯。這兩種情況下,可以使用`document.execCommand()`方法,改變內容的樣式,比如`document.execCommand('bold')`會使得字體加粗。
```javascript
document.execCommand(command, showDefaultUI, input)
```
該方法接受三個參數。
- `command`:字符串,表示所要實施的樣式。
- `showDefaultUI`:布爾值,表示是否要使用默認的用戶界面,建議總是設為`false`。
- `input`:字符串,表示該樣式的輔助內容,比如生成超級鏈接時,這個參數就是所要鏈接的網址。如果第二個參數設為`true`,那么瀏覽器會彈出提示框,要求用戶在提示框輸入該參數。但是,不是所有瀏覽器都支持這樣做,為了兼容性,還是需要自己部署獲取這個參數的方式。
```javascript
var url = window.prompt('請輸入網址');
if (url) {
document.execCommand('createlink', false, url);
}
```
上面代碼中,先提示用戶輸入所要鏈接的網址,然后手動生成超級鏈接。注意,第二個參數是`false`,表示此時不需要自動彈出提示框。
`document.execCommand()`的返回值是一個布爾值。如果為`false`,表示這個方法無法生效。
這個方法大部分情況下,只對選中的內容生效。如果有多個內容可編輯區域,那么只對當前焦點所在的元素生效。
`document.execCommand()`方法可以執行的樣式改變有很多種,下面是其中的一些:bold、insertLineBreak、selectAll、createLink、insertOrderedList、subscript、delete、insertUnorderedList、superscript、formatBlock、insertParagraph、undo、forwardDelete、insertText、unlink、insertImage、italic、unselect、insertHTML、redo。這些值都可以用作第一個參數,它們的含義不難從字面上看出來。
**(2)document.queryCommandSupported()**
`document.queryCommandSupported()`方法返回一個布爾值,表示瀏覽器是否支持`document.execCommand()`的某個命令。
```javascript
if (document.queryCommandSupported('SelectAll')) {
console.log('瀏覽器支持選中可編輯區域的所有內容');
}
```
**(3)document.queryCommandEnabled()**
`document.queryCommandEnabled()`方法返回一個布爾值,表示當前是否可用`document.execCommand()`的某個命令。比如,`bold`(加粗)命令只有存在文本選中時才可用,如果沒有選中文本,就不可用。
```javascript
// HTML 代碼為
// <input type="button" value="Copy" onclick="doCopy()">
function doCopy(){
// 瀏覽器是否支持 copy 命令(選中內容復制到剪貼板)
if (document.queryCommandSupported('copy')) {
copyText('你好');
}else{
console.log('瀏覽器不支持');
}
}
function copyText(text) {
var input = document.createElement('textarea');
document.body.appendChild(input);
input.value = text;
input.focus();
input.select();
// 當前是否有選中文字
if (document.queryCommandEnabled('copy')) {
var success = document.execCommand('copy');
input.remove();
console.log('Copy Ok');
} else {
console.log('queryCommandEnabled is false');
}
}
```
上面代碼中,先判斷瀏覽器是否支持`copy`命令(允許可編輯區域的選中內容,復制到剪貼板),如果支持,就新建一個臨時文本框,里面寫入內容“你好”,并將其選中。然后,判斷是否選中成功,如果成功,就將“你好”復制到剪貼板,再刪除那個臨時文本框。
### document.getSelection()
這個方法指向`window.getSelection()`,參見`window`對象一節的介紹。
- 前言
- 入門篇
- 導論
- 歷史
- 基本語法
- 數據類型
- 概述
- null,undefined 和布爾值
- 數值
- 字符串
- 對象
- 函數
- 數組
- 運算符
- 算術運算符
- 比較運算符
- 布爾運算符
- 二進制位運算符
- 其他運算符,運算順序
- 語法專題
- 數據類型的轉換
- 錯誤處理機制
- 編程風格
- console 對象與控制臺
- 標準庫
- Object 對象
- 屬性描述對象
- Array 對象
- 包裝對象
- Boolean 對象
- Number 對象
- String 對象
- Math 對象
- Date 對象
- RegExp 對象
- JSON 對象
- 面向對象編程
- 實例對象與 new 命令
- this 關鍵字
- 對象的繼承
- Object 對象的相關方法
- 嚴格模式
- 異步操作
- 概述
- 定時器
- Promise 對象
- DOM
- 概述
- Node 接口
- NodeList 接口,HTMLCollection 接口
- ParentNode 接口,ChildNode 接口
- Document 節點
- Element 節點
- 屬性的操作
- Text 節點和 DocumentFragment 節點
- CSS 操作
- Mutation Observer API
- 事件
- EventTarget 接口
- 事件模型
- Event 對象
- 鼠標事件
- 鍵盤事件
- 進度事件
- 表單事件
- 觸摸事件
- 拖拉事件
- 其他常見事件
- GlobalEventHandlers 接口
- 瀏覽器模型
- 瀏覽器模型概述
- window 對象
- Navigator 對象,Screen 對象
- Cookie
- XMLHttpRequest 對象
- 同源限制
- CORS 通信
- Storage 接口
- History 對象
- Location 對象,URL 對象,URLSearchParams 對象
- ArrayBuffer 對象,Blob 對象
- File 對象,FileList 對象,FileReader 對象
- 表單,FormData 對象
- IndexedDB API
- Web Worker
- 附錄:網頁元素接口
- a
- img
- form
- input
- button
- option
- video,audio