JavaScript Document 節(jié)點(diǎn)

2023-03-20 15:48 更新

概述

document節(jié)點(diǎn)對象代表整個文檔,每張網(wǎng)頁都有自己的document對象。window.document屬性就指向這個對象。只要瀏覽器開始載入 HTML 文檔,該對象就存在了,可以直接使用。

document對象有不同的辦法可以獲取。

  • 正常的網(wǎng)頁,直接使用documentwindow.document。
  • iframe框架里面的網(wǎng)頁,使用iframe節(jié)點(diǎn)的contentDocument屬性。
  • Ajax 操作返回的文檔,使用XMLHttpRequest對象的responseXML屬性。
  • 內(nèi)部節(jié)點(diǎn)的ownerDocument屬性。

document對象繼承了EventTarget接口和Node接口,并且混入(mixin)了ParentNode接口。這意味著,這些接口的方法都可以在document對象上調(diào)用。除此之外,document對象還有很多自己的屬性和方法。

屬性

快捷方式屬性

以下屬性是指向文檔內(nèi)部的某個節(jié)點(diǎn)的快捷方式。

(1)document.defaultView

document.defaultView屬性返回document對象所屬的window對象。如果當(dāng)前文檔不屬于window對象,該屬性返回null。

document.defaultView === window // true

(2)document.doctype

對于 HTML 文檔來說,document對象一般有兩個子節(jié)點(diǎn)。第一個子節(jié)點(diǎn)是document.doctype,指向<DOCTYPE>節(jié)點(diǎn),即文檔類型(Document Type Declaration,簡寫DTD)節(jié)點(diǎn)。HTML 的文檔類型節(jié)點(diǎn),一般寫成<!DOCTYPE html>。如果網(wǎng)頁沒有聲明 DTD,該屬性返回null。

var doctype = document.doctype;
doctype // "<!DOCTYPE html>"
doctype.name // "html"

document.firstChild通常就返回這個節(jié)點(diǎn)。

(3)document.documentElement

document.documentElement屬性返回當(dāng)前文檔的根元素節(jié)點(diǎn)(root)。它通常是document節(jié)點(diǎn)的第二個子節(jié)點(diǎn),緊跟在document.doctype節(jié)點(diǎn)后面。HTML網(wǎng)頁的該屬性,一般是<html>節(jié)點(diǎn)。

(4)document.body,document.head

document.body屬性指向<body>節(jié)點(diǎn),document.head屬性指向<head>節(jié)點(diǎn)。

這兩個屬性總是存在的,如果網(wǎng)頁源碼里面省略了<head><body>,瀏覽器會自動創(chuàng)建。另外,這兩個屬性是可寫的,如果改寫它們的值,相當(dāng)于移除所有子節(jié)點(diǎn)。

(5)document.scrollingElement

document.scrollingElement屬性返回文檔的滾動元素。也就是說,當(dāng)文檔整體滾動時(shí),到底是哪個元素在滾動。

標(biāo)準(zhǔn)模式下,這個屬性返回的文檔的根元素document.documentElement(即<html>)。兼容(quirk)模式下,返回的是<body>元素,如果該元素不存在,返回null

// 頁面滾動到瀏覽器頂部
document.scrollingElement.scrollTop = 0;

(6)document.activeElement

document.activeElement屬性返回獲得當(dāng)前焦點(diǎn)(focus)的 DOM 元素。通常,這個屬性返回的是<input><textarea><select>等表單元素,如果當(dāng)前沒有焦點(diǎn)元素,返回<body>元素或null。

(7)document.fullscreenElement

document.fullscreenElement屬性返回當(dāng)前以全屏狀態(tài)展示的 DOM 元素。如果不是全屏狀態(tài),該屬性返回null。

if (document.fullscreenElement.nodeName == 'VIDEO') {
  console.log('全屏播放視頻');
}

上面代碼中,通過document.fullscreenElement可以知道<video>元素有沒有處在全屏狀態(tài),從而判斷用戶行為。

節(jié)點(diǎn)集合屬性

以下屬性返回一個HTMLCollection實(shí)例,表示文檔內(nèi)部特定元素的集合。這些集合都是動態(tài)的,原節(jié)點(diǎn)有任何變化,立刻會反映在集合中。

(1)document.links

document.links屬性返回當(dāng)前文檔所有設(shè)定了href屬性的<a><area>節(jié)點(diǎn)。

// 打印文檔所有的鏈接
var links = document.links;
for(var i = 0; i < links.length; i++) {
  console.log(links[i]);
}

(2)document.forms

document.forms屬性返回所有<form>表單節(jié)點(diǎn)。

var selectForm = document.forms[0];

上面代碼獲取文檔第一個表單。

除了使用位置序號,id屬性和name屬性也可以用來引用表單。

/* 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>圖片節(jié)點(diǎn)。

var imglist = document.images;

for(var i = 0; i < imglist.length; i++) {
  if (imglist[i].src === 'banner.gif') {
    // ...
  }
}

上面代碼在所有img標(biāo)簽中,尋找某張圖片。

(4)document.embeds,document.plugins

document.embeds屬性和document.plugins屬性,都返回所有<embed>節(jié)點(diǎn)。

(5)document.scripts

document.scripts屬性返回所有<script>節(jié)點(diǎn)。

var scripts = document.scripts;
if (scripts.length !== 0 ) {
  console.log('當(dāng)前網(wǎng)頁有腳本');
}

(6)document.styleSheets

document.styleSheets屬性返回網(wǎng)頁內(nèi)嵌或引入的 CSS 樣式表集合,詳細(xì)介紹請看《CSS 操作》一章。

(7)小結(jié)

除了document.styleSheets屬性,以上的其他集合屬性返回的都是HTMLCollection實(shí)例。document.styleSheets屬性返回的是StyleSheetList實(shí)例。

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實(shí)例是類似數(shù)組的對象,所以上面這些屬性都有length屬性,都可以使用方括號運(yùn)算符引用成員。如果成員有idname屬性,還可以用這兩個屬性的值,在HTMLCollection實(shí)例上引用到這個成員。

// HTML 代碼如下
// <form name="myForm">
document.myForm === document.forms.myForm // true

文檔靜態(tài)信息屬性

以下屬性返回文檔信息。

(1)document.documentURI,document.URL

document.documentURI屬性和document.URL屬性都返回一個字符串,表示當(dāng)前文檔的網(wǎng)址。不同之處是它們繼承自不同的接口,documentURI繼承自Document接口,可用于所有文檔;URL繼承自HTMLDocument接口,只能用于 HTML 文檔。

document.URL
// http://www.example.com/about

document.documentURI === document.URL
// true

如果文檔的錨點(diǎn)(#anchor)變化,這兩個屬性都會跟著變化。

(2)document.domain

document.domain屬性返回當(dāng)前文檔的域名,不包含協(xié)議和端口。比如,網(wǎng)頁的網(wǎng)址是http://www.example.com:80/hello.html,那么document.domain屬性就等于www.example.com。如果無法獲取域名,該屬性返回null。

document.domain基本上是一個只讀屬性,只有一種情況除外。次級域名的網(wǎng)頁,可以把document.domain設(shè)為對應(yīng)的上級域名。比如,當(dāng)前域名是a.sub.example.com,則document.domain屬性可以設(shè)置為sub.example.com,也可以設(shè)為example.com。修改后,document.domain相同的兩個網(wǎng)頁,可以讀取對方的資源,比如設(shè)置的 Cookie。

另外,設(shè)置document.domain會導(dǎo)致端口被改成null。因此,如果通過設(shè)置document.domain來進(jìn)行通信,雙方網(wǎng)頁都必須設(shè)置這個值,才能保證端口相同。

(3)document.location

Location對象是瀏覽器提供的原生對象,提供 URL 相關(guān)的信息和操作方法。通過window.locationdocument.location屬性,可以拿到這個對象。

關(guān)于這個對象的詳細(xì)介紹,請看《瀏覽器模型》部分的《Location 對象》章節(jié)。

(4)document.lastModified

document.lastModified屬性返回一個字符串,表示當(dāng)前文檔最后修改的時(shí)間。不同瀏覽器的返回值,日期格式是不一樣的。

document.lastModified
// "03/07/2018 11:18:27"

注意,document.lastModified屬性的值是字符串,所以不能直接用來比較。Date.parse方法將其轉(zhuǎn)為Date實(shí)例,才能比較兩個網(wǎng)頁。

var lastVisitedDate = Date.parse('01/01/2018');
if (Date.parse(document.lastModified) > lastVisitedDate) {
  console.log('網(wǎng)頁已經(jīng)變更');
}

如果頁面上有 JavaScript 生成的內(nèi)容,document.lastModified屬性返回的總是當(dāng)前時(shí)間。

(5)document.title

document.title屬性返回當(dāng)前文檔的標(biāo)題。默認(rèn)情況下,返回<title>節(jié)點(diǎn)的值。但是該屬性是可寫的,一旦被修改,就返回修改后的值。

document.title = '新標(biāo)題';
document.title // "新標(biāo)題"

(6)document.characterSet

document.characterSet屬性返回當(dāng)前文檔的編碼,比如UTF-8ISO-8859-1等等。

(7)document.referrer

document.referrer屬性返回一個字符串,表示當(dāng)前文檔的訪問者來自哪里。

document.referrer
// "https://example.com/path"

如果無法獲取來源,或者用戶直接鍵入網(wǎng)址而不是從其他網(wǎng)頁點(diǎn)擊進(jìn)入,document.referrer返回一個空字符串。

document.referrer的值,總是與 HTTP 頭信息的Referer字段保持一致。但是,document.referrer的拼寫有兩個r,而頭信息的Referer字段只有一個r

(8)document.dir

document.dir返回一個字符串,表示文字方向。它只有兩個可能的值:rtl表示文字從右到左,阿拉伯文是這種方式;ltr表示文字從左到右,包括英語和漢語在內(nèi)的大多數(shù)文字采用這種方式。

(9)document.compatMode

compatMode屬性返回瀏覽器處理文檔的模式,可能的值為BackCompat(向后兼容模式)和CSS1Compat(嚴(yán)格模式)。

一般來說,如果網(wǎng)頁代碼的第一行設(shè)置了明確的DOCTYPE(比如<!doctype html>),document.compatMode的值都為CSS1Compat

文檔狀態(tài)屬性

(1)document.hidden

document.hidden屬性返回一個布爾值,表示當(dāng)前頁面是否可見。如果窗口最小化、瀏覽器切換了 Tab,都會導(dǎo)致導(dǎo)致頁面不可見,使得document.hidden返回true

這個屬性是 Page Visibility API 引入的,一般都是配合這個 API 使用。

(2)document.visibilityState

document.visibilityState返回文檔的可見狀態(tài)。

它的值有四種可能。

  • visible:頁面可見。注意,頁面可能是部分可見,即不是焦點(diǎn)窗口,前面被其他窗口部分擋住了。
  • hidden:頁面不可見,有可能窗口最小化,或者瀏覽器切換到了另一個 Tab。
  • prerender:頁面處于正在渲染狀態(tài),對于用戶來說,該頁面不可見。
  • unloaded:頁面從內(nèi)存里面卸載了。

這個屬性可以用在頁面加載時(shí),防止加載某些資源;或者頁面不可見時(shí),停掉一些頁面功能。

(3)document.readyState

document.readyState屬性返回當(dāng)前文檔的狀態(tài),共有三種可能的值。

  • loading:加載 HTML 代碼階段(尚未完成解析)
  • interactive:加載外部資源階段
  • complete:加載完成

這個屬性變化的過程如下。

  1. 瀏覽器開始解析 HTML 文檔,document.readyState屬性等于loading
  2. 瀏覽器遇到 HTML 文檔中的<script>元素,并且沒有asyncdefer屬性,就暫停解析,開始執(zhí)行腳本,這時(shí)document.readyState屬性還是等于loading。
  3. HTML 文檔解析完成,document.readyState屬性變成interactive。
  4. 瀏覽器等待圖片、樣式表、字體文件等外部資源加載完成,一旦全部加載完成,document.readyState屬性變成complete

下面的代碼用來檢查網(wǎng)頁是否加載成功。

// 基本檢查
if (document.readyState === 'complete') {
  // ...
}

// 輪詢檢查
var interval = setInterval(function() {
  if (document.readyState === 'complete') {
    clearInterval(interval);
    // ...
  }
}, 100);

另外,每次狀態(tài)變化都會觸發(fā)一個readystatechange事件。

document.cookie

document.cookie屬性用來操作瀏覽器 Cookie,詳見《瀏覽器模型》部分的《Cookie》章節(jié)。

document.designMode

document.designMode屬性控制當(dāng)前文檔是否可編輯。該屬性只有兩個值onoff,默認(rèn)值為off。一旦設(shè)為on,用戶就可以編輯整個文檔的內(nèi)容。

下面代碼打開iframe元素內(nèi)部文檔的designMode屬性,就能將其變?yōu)橐粋€所見即所得的編輯器。

// HTML 代碼如下
// <iframe id="editor" src="about:blank"></iframe>
var editor = document.getElementById('editor');
editor.contentDocument.designMode = 'on';

document.currentScript

document.currentScript屬性只用在<script>元素的內(nèi)嵌腳本或加載的外部腳本之中,返回當(dāng)前腳本所在的那個 DOM 節(jié)點(diǎn),即<script>元素的 DOM 節(jié)點(diǎn)。

<script id="foo">
  console.log(
    document.currentScript === document.getElementById('foo')
  ); // true
</script>

上面代碼中,document.currentScript就是<script>元素節(jié)點(diǎn)。

document.implementation 

document.implementation屬性返回一個DOMImplementation對象。該對象有三個方法,主要用于創(chuàng)建獨(dú)立于當(dāng)前文檔的新的 Document 對象。

  • DOMImplementation.createDocument():創(chuàng)建一個 XML 文檔。
  • DOMImplementation.createHTMLDocument():創(chuàng)建一個 HTML 文檔。
  • DOMImplementation.createDocumentType():創(chuàng)建一個 DocumentType 對象。

下面是創(chuàng)建 HTML 文檔的例子。

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。這會使得當(dāng)前文檔的內(nèi)容全部消失,變成hello world

方法

document.open(),document.close()

document.open方法清除當(dāng)前文檔所有內(nèi)容,使得文檔處于可寫狀態(tài),供document.write方法寫入內(nèi)容。

document.close方法用來關(guān)閉document.open()打開的文檔。

document.open();
document.write('hello world');
document.close();

document.write(),document.writeln()

document.write方法用于向當(dāng)前文檔寫入內(nèi)容。

在網(wǎng)頁的首次渲染階段,只要頁面沒有關(guān)閉寫入(即沒有執(zhí)行document.close()),document.write寫入的內(nèi)容就會追加在已有內(nèi)容的后面。

// 頁面顯示“helloworld”
document.open();
document.write('hello');
document.write('world');
document.close();

注意,document.write會當(dāng)作 HTML 代碼解析,不會轉(zhuǎn)義。

document.write('<p>hello world</p>');

上面代碼中,document.write會將<p>當(dāng)作 HTML 標(biāo)簽解釋。

如果頁面已經(jīng)解析完成(DOMContentLoaded事件發(fā)生之后),再調(diào)用write方法,它會先調(diào)用open方法,擦除當(dāng)前文檔所有內(nèi)容,然后再寫入。

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();
});

如果在頁面渲染過程中調(diào)用write方法,并不會自動調(diào)用open方法。(可以理解成,open方法已調(diào)用,但close方法還未調(diào)用。)

<html>
<body>
hello
<script type="text/javascript">
  document.write("world")
</script>
</body>
</html>

在瀏覽器打開上面網(wǎng)頁,將會顯示hello world。

document.write是 JavaScript 語言標(biāo)準(zhǔn)化之前就存在的方法,現(xiàn)在完全有更符合標(biāo)準(zhǔn)的方法向文檔寫入內(nèi)容(比如對innerHTML屬性賦值)。所以,除了某些特殊情況,應(yīng)該盡量避免使用document.write這個方法。

document.writeln方法與write方法完全一致,除了會在輸出內(nèi)容的尾部添加換行符。

document.write(1);
document.write(2);
// 12

document.writeln(1);
document.writeln(2);
// 1
// 2
//

注意,writeln方法添加的是 ASCII 碼的換行符,渲染成 HTML 網(wǎng)頁時(shí)不起作用,即在網(wǎng)頁上顯示不出換行。網(wǎng)頁上的換行,必須顯式寫入<br>。

document.querySelector(),document.querySelectorAll()

document.querySelector方法接受一個 CSS 選擇器作為參數(shù),返回匹配該選擇器的元素節(jié)點(diǎn)。如果有多個節(jié)點(diǎn)滿足匹配條件,則返回第一個匹配的節(jié)點(diǎn)。如果沒有發(fā)現(xiàn)匹配的節(jié)點(diǎn),則返回null。

var el1 = document.querySelector('.myclass');
var el2 = document.querySelector('#myParent > [ng-click]');

document.querySelectorAll方法與querySelector用法類似,區(qū)別是返回一個NodeList對象,包含所有匹配給定選擇器的節(jié)點(diǎn)。

elementList = document.querySelectorAll('.myclass');

這兩個方法的參數(shù),可以是逗號分隔的多個 CSS 選擇器,返回匹配其中一個選擇器的元素節(jié)點(diǎn),這與 CSS 選擇器的規(guī)則是一致的。

var matches = document.querySelectorAll('div.note, div.alert');

上面代碼返回class屬性是notealertdiv元素。

這兩個方法都支持復(fù)雜的 CSS 選擇器。

// 選中 data-foo-bar 屬性等于 someval 的元素
document.querySelectorAll('[data-foo-bar="someval"]');

// 選中 myForm 表單中所有不通過驗(yàn)證的元素
document.querySelectorAll('#myForm :invalid');

// 選中div元素,那些 class 含 ignore 的除外
document.querySelectorAll('DIV:not(.ignore)');

// 同時(shí)選中 div,a,script 三類元素
document.querySelectorAll('DIV, A, SCRIPT');

但是,它們不支持 CSS 偽元素的選擇器(比如:first-line:first-letter)和偽類的選擇器(比如:link:visited),即無法選中偽元素和偽類。

如果querySelectorAll方法的參數(shù)是字符串*,則會返回文檔中的所有元素節(jié)點(diǎn)。另外,querySelectorAll的返回結(jié)果不是動態(tài)集合,不會實(shí)時(shí)反映元素節(jié)點(diǎn)的變化。

最后,這兩個方法除了定義在document對象上,還定義在元素節(jié)點(diǎn)上,即在元素節(jié)點(diǎn)上也可以調(diào)用。

document.getElementsByTagName()

document.getElementsByTagName()方法搜索 HTML 標(biāo)簽名,返回符合條件的元素。它的返回值是一個類似數(shù)組對象(HTMLCollection實(shí)例),可以實(shí)時(shí)反映 HTML 文檔的變化。如果沒有任何匹配的元素,就返回一個空集。

var paras = document.getElementsByTagName('p');
paras instanceof HTMLCollection // true

上面代碼返回當(dāng)前文檔的所有p元素節(jié)點(diǎn)。

HTML 標(biāo)簽名是大小寫不敏感的,因此getElementsByTagName()方法的參數(shù)也是大小寫不敏感的。另外,返回結(jié)果中,各個成員的順序就是它們在文檔中出現(xiàn)的順序。

如果傳入*,就可以返回文檔中所有 HTML 元素。

var allElements = document.getElementsByTagName('*');

注意,元素節(jié)點(diǎn)本身也定義了getElementsByTagName方法,返回該元素的后代元素中符合條件的元素。也就是說,這個方法不僅可以在document對象上調(diào)用,也可以在任何元素節(jié)點(diǎn)上調(diào)用。

var firstPara = document.getElementsByTagName('p')[0];
var spans = firstPara.getElementsByTagName('span');

上面代碼選中第一個p元素內(nèi)部的所有span元素。

document.getElementsByClassName()

document.getElementsByClassName()方法返回一個類似數(shù)組的對象(HTMLCollection實(shí)例),包括了所有class名字符合指定條件的元素,元素的變化實(shí)時(shí)反映在返回結(jié)果中。

var elements = document.getElementsByClassName(names);

由于class是保留字,所以 JavaScript 一律使用className表示 CSS 的class。

參數(shù)可以是多個class,它們之間使用空格分隔。

var elements = document.getElementsByClassName('foo bar');

上面代碼返回同時(shí)具有foobar兩個class的元素,foobar的順序不重要。

注意,正常模式下,CSS 的class是大小寫敏感的。(quirks mode下,大小寫不敏感。)

getElementsByTagName()方法一樣,getElementsByClassName()方法不僅可以在document對象上調(diào)用,也可以在任何元素節(jié)點(diǎn)上調(diào)用。

// 非document對象上調(diào)用
var elements = rootElement.getElementsByClassName(names);

document.getElementsByName()

document.getElementsByName()方法用于選擇擁有name屬性的 HTML 元素(比如<form>、<radio>、<img>、<frame><embed><object>等),返回一個類似數(shù)組的的對象(NodeList實(shí)例),因?yàn)?code>name屬性相同的元素可能不止一個。

// 表單為 <form name="x"></form>
var forms = document.getElementsByName('x');
forms[0].tagName // "FORM"

document.getElementById()

document.getElementById()方法返回匹配指定id屬性的元素節(jié)點(diǎn)。如果沒有發(fā)現(xiàn)匹配的節(jié)點(diǎn),則返回null。

var elem = document.getElementById('para1');

注意,該方法的參數(shù)是大小寫敏感的。比如,如果某個節(jié)點(diǎn)的id屬性是main,那么document.getElementById('Main')將返回null。

document.getElementById()方法與document.querySelector()方法都能獲取元素節(jié)點(diǎn),不同之處是document.querySelector()方法的參數(shù)使用 CSS 選擇器語法,document.getElementById()方法的參數(shù)是元素的id屬性。

document.getElementById('myElement')
document.querySelector('#myElement')

上面代碼中,兩個方法都能選中idmyElement的元素,但是document.getElementById()document.querySelector()效率高得多。

另外,這個方法只能在document對象上使用,不能在其他元素節(jié)點(diǎn)上使用。

document.elementFromPoint(),document.elementsFromPoint()

document.elementFromPoint()方法返回位于頁面指定位置最上層的元素節(jié)點(diǎn)。

var element = document.elementFromPoint(50, 50);

上面代碼選中在(50, 50)這個坐標(biāo)位置的最上層的那個 HTML 元素。

elementFromPoint方法的兩個參數(shù),依次是相對于當(dāng)前視口左上角的橫坐標(biāo)和縱坐標(biāo),單位是像素。如果位于該位置的 HTML 元素不可返回(比如文本框的滾動條),則返回它的父元素(比如文本框)。如果坐標(biāo)值無意義(比如負(fù)值或超過視口大?。?,則返回null

document.elementsFromPoint()返回一個數(shù)組,成員是位于指定坐標(biāo)(相對于視口)的所有元素。

var elements = document.elementsFromPoint(x, y);

document.createElement()

document.createElement方法用來生成元素節(jié)點(diǎn),并返回該節(jié)點(diǎn)。

var newDiv = document.createElement('div');

createElement方法的參數(shù)為元素的標(biāo)簽名,即元素節(jié)點(diǎn)的tagName屬性,對于 HTML 網(wǎng)頁大小寫不敏感,即參數(shù)為divDIV返回的是同一種節(jié)點(diǎn)。如果參數(shù)里面包含尖括號(即<>)會報(bào)錯。

document.createElement('<div>');
// DOMException: The tag name provided ('<div>') is not a valid name

注意,document.createElement的參數(shù)可以是自定義的標(biāo)簽名。

document.createElement('foo');

document.createTextNode()

document.createTextNode方法用來生成文本節(jié)點(diǎn)(Text實(shí)例),并返回該節(jié)點(diǎn)。它的參數(shù)是文本節(jié)點(diǎn)的內(nèi)容。

var newDiv = document.createElement('div');
var newContent = document.createTextNode('Hello');
newDiv.appendChild(newContent);

上面代碼新建一個div節(jié)點(diǎn)和一個文本節(jié)點(diǎn),然后將文本節(jié)點(diǎn)插入div節(jié)點(diǎn)。

這個方法可以確保返回的節(jié)點(diǎn),被瀏覽器當(dāng)作文本渲染,而不是當(dāng)作 HTML 代碼渲染。因此,可以用來展示用戶的輸入,避免 XSS 攻擊。

var div = document.createElement('div');
div.appendChild(document.createTextNode('<span>Foo & bar</span>'));
console.log(div.innerHTML)
// &lt;span&gt;Foo &amp; bar&lt;/span&gt;

上面代碼中,createTextNode方法對大于號和小于號進(jìn)行轉(zhuǎn)義,從而保證即使用戶輸入的內(nèi)容包含惡意代碼,也能正確顯示。

需要注意的是,該方法不對單引號和雙引號轉(zhuǎn)義,所以不能用來對 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方法不轉(zhuǎn)義雙引號,導(dǎo)致onmouseover方法被注入了代碼。

document.createAttribute()

document.createAttribute方法生成一個新的屬性節(jié)點(diǎn)(Attr實(shí)例),并返回它。

var attribute = document.createAttribute(name);

document.createAttribute方法的參數(shù)name,是屬性的名稱。

var node = document.getElementById('div1');

var a = document.createAttribute('my_attrib');
a.value = 'newVal';

node.setAttributeNode(a);
// 或者
node.setAttribute('my_attrib', 'newVal');

上面代碼為div1節(jié)點(diǎn),插入一個值為newValmy_attrib屬性。

document.createComment()

document.createComment方法生成一個新的注釋節(jié)點(diǎn),并返回該節(jié)點(diǎn)。

var CommentNode = document.createComment(data);

document.createComment方法的參數(shù)是一個字符串,會成為注釋節(jié)點(diǎn)的內(nèi)容。

document.createDocumentFragment()

document.createDocumentFragment方法生成一個空的文檔片段對象(DocumentFragment實(shí)例)。

var docFragment = document.createDocumentFragment();

DocumentFragment是一個存在于內(nèi)存的 DOM 片段,不屬于當(dāng)前文檔,常常用來生成一段較復(fù)雜的 DOM 結(jié)構(gòu),然后再插入當(dāng)前文檔。這樣做的好處在于,因?yàn)?code>DocumentFragment不屬于當(dāng)前文檔,對它的任何改動,都不會引發(fā)網(wǎng)頁的重新渲染,比直接修改當(dāng)前文檔的 DOM 有更好的性能表現(xiàn)。

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>節(jié)點(diǎn),這些子節(jié)點(diǎn)被一次性插入了當(dāng)前文檔。

document.createEvent()

document.createEvent方法生成一個事件對象(Event實(shí)例),該對象可以被element.dispatchEvent方法使用,觸發(fā)指定事件。

var event = document.createEvent(type);

document.createEvent方法的參數(shù)是事件類型,比如UIEvents、MouseEvents、MutationEventsHTMLEvents。

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的事件實(shí)例,然后觸發(fā)該事件。

document.addEventListener(),document.removeEventListener(),document.dispatchEvent()

這三個方法用于處理document節(jié)點(diǎn)的事件。它們都繼承自EventTarget接口,詳細(xì)介紹參見《EventTarget 接口》一章。

// 添加事件監(jiān)聽函數(shù)
document.addEventListener('click', listener, false);

// 移除事件監(jiān)聽函數(shù)
document.removeEventListener('click', listener, false);

// 觸發(fā)事件
var event = new Event('click');
document.dispatchEvent(event);

document.hasFocus()

document.hasFocus方法返回一個布爾值,表示當(dāng)前文檔之中是否有元素被激活或獲得焦點(diǎn)。

var focused = document.hasFocus();

注意,有焦點(diǎn)的文檔必定被激活(active),反之不成立,激活的文檔未必有焦點(diǎn)。比如,用戶點(diǎn)擊按鈕,從當(dāng)前窗口跳出一個新窗口,該新窗口就是激活的,但是不擁有焦點(diǎn)。

document.adoptNode(),document.importNode()

document.adoptNode方法將某個節(jié)點(diǎn)及其子節(jié)點(diǎn),從原來所在的文檔或DocumentFragment里面移除,歸屬當(dāng)前document對象,返回插入后的新節(jié)點(diǎn)。插入的節(jié)點(diǎn)對象的ownerDocument屬性,會變成當(dāng)前的document對象,而parentNode屬性是null。

var node = document.adoptNode(externalNode);
document.appendChild(node);

注意,document.adoptNode方法只是改變了節(jié)點(diǎn)的歸屬,并沒有將這個節(jié)點(diǎn)插入新的文檔樹。所以,還要再用appendChild方法或insertBefore方法,將新節(jié)點(diǎn)插入當(dāng)前文檔樹。

document.importNode方法則是從原來所在的文檔或DocumentFragment里面,拷貝某個節(jié)點(diǎn)及其子節(jié)點(diǎn),讓它們歸屬當(dāng)前document對象。拷貝的節(jié)點(diǎn)對象的ownerDocument屬性,會變成當(dāng)前的document對象,而parentNode屬性是null。

var node = document.importNode(externalNode, deep);

document.importNode方法的第一個參數(shù)是外部節(jié)點(diǎn),第二個參數(shù)是一個布爾值,表示對外部節(jié)點(diǎn)是深拷貝還是淺拷貝,默認(rèn)是淺拷貝(false)。雖然第二個參數(shù)是可選的,但是建議總是保留這個參數(shù),并設(shè)為true。

注意,document.importNode方法只是拷貝外部節(jié)點(diǎn),這時(shí)該節(jié)點(diǎn)的父節(jié)點(diǎn)是null。下一步還必須將這個節(jié)點(diǎn)插入當(dāng)前文檔樹。

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窗口,拷貝一個指定節(jié)點(diǎn)myNode,插入當(dāng)前文檔。

document.createNodeIterator()

document.createNodeIterator方法返回一個子節(jié)點(diǎn)遍歷器。

var nodeIterator = document.createNodeIterator(
  document.body,
  NodeFilter.SHOW_ELEMENT
);

上面代碼返回<body>元素子節(jié)點(diǎn)的遍歷器。

document.createNodeIterator方法第一個參數(shù)為所要遍歷的根節(jié)點(diǎn),第二個參數(shù)為所要遍歷的節(jié)點(diǎn)類型,這里指定為元素節(jié)點(diǎn)(NodeFilter.SHOW_ELEMENT)。幾種主要的節(jié)點(diǎn)類型寫法如下。

  • 所有節(jié)點(diǎn):NodeFilter.SHOW_ALL
  • 元素節(jié)點(diǎn):NodeFilter.SHOW_ELEMENT
  • 文本節(jié)點(diǎn):NodeFilter.SHOW_TEXT
  • 評論節(jié)點(diǎn):NodeFilter.SHOW_COMMENT

document.createNodeIterator方法返回一個“遍歷器”對象(NodeFilter實(shí)例)。該實(shí)例的nextNode()方法和previousNode()方法,可以用來遍歷所有子節(jié)點(diǎn)。

var nodeIterator = document.createNodeIterator(document.body);
var pars = [];
var currentNode;

while (currentNode = nodeIterator.nextNode()) {
  pars.push(currentNode);
}

上面代碼中,使用遍歷器的nextNode方法,將根節(jié)點(diǎn)的所有子節(jié)點(diǎn),依次讀入一個數(shù)組。nextNode方法先返回遍歷器的內(nèi)部指針?biāo)诘墓?jié)點(diǎn),然后會將指針移向下一個節(jié)點(diǎn)。所有成員遍歷完成后,返回null。previousNode方法則是先將指針移向上一個節(jié)點(diǎn),然后返回該節(jié)點(diǎn)。

var nodeIterator = document.createNodeIterator(
  document.body,
  NodeFilter.SHOW_ELEMENT
);

var currentNode = nodeIterator.nextNode();
var previousNode = nodeIterator.previousNode();

currentNode === previousNode // true

上面代碼中,currentNodepreviousNode都指向同一個的節(jié)點(diǎn)。

注意,遍歷器返回的第一個節(jié)點(diǎn),總是根節(jié)點(diǎn)。

pars[0] === document.body // true

document.createTreeWalker()

document.createTreeWalker方法返回一個 DOM 的子樹遍歷器。它與document.createNodeIterator方法基本是類似的,區(qū)別在于它返回的是TreeWalker實(shí)例,后者返回的是NodeIterator實(shí)例。另外,它的第一個節(jié)點(diǎn)不是根節(jié)點(diǎn)。

document.createTreeWalker方法的第一個參數(shù)是所要遍歷的根節(jié)點(diǎn),第二個參數(shù)指定所要遍歷的節(jié)點(diǎn)類型(與document.createNodeIterator方法的第二個參數(shù)相同)。

var treeWalker = document.createTreeWalker(
  document.body,
  NodeFilter.SHOW_ELEMENT
);

var nodeList = [];

while(treeWalker.nextNode()) {
  nodeList.push(treeWalker.currentNode);
}

上面代碼遍歷<body>節(jié)點(diǎn)下屬的所有元素節(jié)點(diǎn),將它們插入nodeList數(shù)組。

document.execCommand(),document.queryCommandSupported(),document.queryCommandEnabled()

(1)document.execCommand()

如果document.designMode屬性設(shè)為on,那么整個文檔用戶可編輯;如果元素的contenteditable屬性設(shè)為true,那么該元素可編輯。這兩種情況下,可以使用document.execCommand()方法,改變內(nèi)容的樣式,比如document.execCommand('bold')會使得字體加粗。

document.execCommand(command, showDefaultUI, input)

該方法接受三個參數(shù)。

  • command:字符串,表示所要實(shí)施的樣式。
  • showDefaultUI:布爾值,表示是否要使用默認(rèn)的用戶界面,建議總是設(shè)為false
  • input:字符串,表示該樣式的輔助內(nèi)容,比如生成超級鏈接時(shí),這個參數(shù)就是所要鏈接的網(wǎng)址。如果第二個參數(shù)設(shè)為true,那么瀏覽器會彈出提示框,要求用戶在提示框輸入該參數(shù)。但是,不是所有瀏覽器都支持這樣做,為了兼容性,還是需要自己部署獲取這個參數(shù)的方式。
var url = window.prompt('請輸入網(wǎng)址');

if (url) {
  document.execCommand('createlink', false, url);
}

上面代碼中,先提示用戶輸入所要鏈接的網(wǎng)址,然后手動生成超級鏈接。注意,第二個參數(shù)是false,表示此時(shí)不需要自動彈出提示框。

document.execCommand()的返回值是一個布爾值。如果為false,表示這個方法無法生效。

這個方法大部分情況下,只對選中的內(nèi)容生效。如果有多個內(nèi)容可編輯區(qū)域,那么只對當(dāng)前焦點(diǎn)所在的元素生效。

document.execCommand()方法可以執(zhí)行的樣式改變有很多種,下面是其中的一些:bold、insertLineBreak、selectAll、createLink、insertOrderedList、subscript、delete、insertUnorderedList、superscript、formatBlock、insertParagraph、undo、forwardDelete、insertText、unlink、insertImage、italic、unselect、insertHTML、redo。這些值都可以用作第一個參數(shù),它們的含義不難從字面上看出來。

(2)document.queryCommandSupported()

document.queryCommandSupported()方法返回一個布爾值,表示瀏覽器是否支持document.execCommand()的某個命令。

if (document.queryCommandSupported('SelectAll')) {
  console.log('瀏覽器支持選中可編輯區(qū)域的所有內(nèi)容');
}

(3)document.queryCommandEnabled()

document.queryCommandEnabled()方法返回一個布爾值,表示當(dāng)前是否可用document.execCommand()的某個命令。比如,bold(加粗)命令只有存在文本選中時(shí)才可用,如果沒有選中文本,就不可用。

// HTML 代碼為
// <input type="button" value="Copy" onclick="doCopy()">

function doCopy(){
  // 瀏覽器是否支持 copy 命令(選中內(nèi)容復(fù)制到剪貼板)
  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();

  // 當(dāng)前是否有選中文字
  if (document.queryCommandEnabled('copy')) {
    var success = document.execCommand('copy');
    input.remove();
    console.log('Copy Ok');
  } else {
    console.log('queryCommandEnabled is false');
  }
}

上面代碼中,先判斷瀏覽器是否支持copy命令(允許可編輯區(qū)域的選中內(nèi)容,復(fù)制到剪貼板),如果支持,就新建一個臨時(shí)文本框,里面寫入內(nèi)容“你好”,并將其選中。然后,判斷是否選中成功,如果成功,就將“你好”復(fù)制到剪貼板,再刪除那個臨時(shí)文本框。

document.getSelection()

這個方法指向window.getSelection(),參見window對象一節(jié)的介紹。


以上內(nèi)容是否對您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號