document
節(jié)點(diǎn)對象代表整個文檔,每張網(wǎng)頁都有自己的document
對象。window.document
屬性就指向這個對象。只要瀏覽器開始載入 HTML 文檔,該對象就存在了,可以直接使用。
document
對象有不同的辦法可以獲取。
document
或window.document
。iframe
框架里面的網(wǎng)頁,使用iframe
節(jié)點(diǎn)的contentDocument
屬性。XMLHttpRequest
對象的responseXML
屬性。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),從而判斷用戶行為。
以下屬性返回一個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)算符引用成員。如果成員有id
或name
屬性,還可以用這兩個屬性的值,在HTMLCollection
實(shí)例上引用到這個成員。
// HTML 代碼如下
// <form name="myForm">
document.myForm === document.forms.myForm // true
以下屬性返回文檔信息。
(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.location
和document.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-8
、ISO-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
。
(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
:加載完成這個屬性變化的過程如下。
document.readyState
屬性等于loading
。<script>
元素,并且沒有async
或defer
屬性,就暫停解析,開始執(zhí)行腳本,這時(shí)document.readyState
屬性還是等于loading
。document.readyState
屬性變成interactive
。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
屬性用來操作瀏覽器 Cookie,詳見《瀏覽器模型》部分的《Cookie》章節(jié)。
document.designMode
屬性控制當(dāng)前文檔是否可編輯。該屬性只有兩個值on
和off
,默認(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
屬性只用在<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
屬性返回一個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
方法清除當(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
方法用于向當(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
方法接受一個 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
屬性是note
或alert
的div
元素。
這兩個方法都支持復(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()
方法搜索 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()
方法返回一個類似數(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í)具有foo
和bar
兩個class
的元素,foo
和bar
的順序不重要。
注意,正常模式下,CSS 的class
是大小寫敏感的。(quirks mode
下,大小寫不敏感。)
與getElementsByTagName()
方法一樣,getElementsByClassName()
方法不僅可以在document
對象上調(diào)用,也可以在任何元素節(jié)點(diǎn)上調(diào)用。
// 非document對象上調(diào)用
var elements = rootElement.getElementsByClassName(names);
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()
方法返回匹配指定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')
上面代碼中,兩個方法都能選中id
為myElement
的元素,但是document.getElementById()
比document.querySelector()
效率高得多。
另外,這個方法只能在document
對象上使用,不能在其他元素節(jié)點(diǎn)上使用。
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
方法用來生成元素節(jié)點(diǎn),并返回該節(jié)點(diǎn)。
var newDiv = document.createElement('div');
createElement
方法的參數(shù)為元素的標(biāo)簽名,即元素節(jié)點(diǎn)的tagName
屬性,對于 HTML 網(wǎng)頁大小寫不敏感,即參數(shù)為div
或DIV
返回的是同一種節(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
方法用來生成文本節(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)
// <span>Foo & bar</span>
上面代碼中,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
方法生成一個新的屬性節(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),插入一個值為newVal
的my_attrib
屬性。
document.createComment
方法生成一個新的注釋節(jié)點(diǎn),并返回該節(jié)點(diǎn)。
var CommentNode = document.createComment(data);
document.createComment
方法的參數(shù)是一個字符串,會成為注釋節(jié)點(diǎn)的內(nèi)容。
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
方法生成一個事件對象(Event
實(shí)例),該對象可以被element.dispatchEvent
方法使用,觸發(fā)指定事件。
var event = document.createEvent(type);
document.createEvent
方法的參數(shù)是事件類型,比如UIEvents
、MouseEvents
、MutationEvents
、HTMLEvents
。
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
節(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
方法返回一個布爾值,表示當(dāng)前文檔之中是否有元素被激活或獲得焦點(diǎn)。
var focused = document.hasFocus();
注意,有焦點(diǎn)的文檔必定被激活(active),反之不成立,激活的文檔未必有焦點(diǎn)。比如,用戶點(diǎn)擊按鈕,從當(dāng)前窗口跳出一個新窗口,該新窗口就是激活的,但是不擁有焦點(diǎn)。
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
方法返回一個子節(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)類型寫法如下。
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
上面代碼中,currentNode
和previousNode
都指向同一個的節(jié)點(diǎn)。
注意,遍歷器返回的第一個節(jié)點(diǎn),總是根節(jié)點(diǎn)。
pars[0] === document.body // true
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ù)組。
(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í)文本框。
這個方法指向window.getSelection()
,參見window
對象一節(jié)的介紹。
更多建議: