JavaScript 通過Document 類型表示文檔。在瀏覽器中,document 對象是HTMLDocument(繼承自Document 類型)的一個實例,表示整個HTML 頁面。而且,document 對象是window 對象的一個屬性,因此可以將其作為全局對象來訪問。Document 節點 ...
JavaScript 通過Document 類型表示文檔。在瀏覽器中,document 對象是HTMLDocument(繼承自Document 類型)的一個實例,表示整個HTML 頁面。而且,document 對象是window 對象的一個屬性,因此可以將其作為全局對象來訪問。Document 節點具有下列特征:
- nodeType 的值為9;
- nodeName 的值為"#document";
- nodeValue 的值為null;
- parentNode 的值為null;
- ownerDocument 的值為 null;
- 其子節點可能是一個DocumentType(最多一個)、Element(最多一個)、ProcessingInstruction或Comment。
Document 類型可以表示HTML 頁面或者其他基於XML 的文檔。不過,最常見的應用還是作為HTMLDocument 實例的document 對象。通過這個文檔對象,不僅可以取得與頁面有關的信息,而且還能操作頁面的外觀及其底層結構。
在Firefox、Safari、Chrome 和Opera 中,可以通過腳本訪問Document 類型的構造函數和原型。但在所有瀏覽器中都可以訪問HTMLDocument 類型的構造函數和原型,包括IE8 及後續版本。
1. 文檔的子節點
雖然DOM 標準規定Document 節點的子節點可以是DocumentType、Element、ProcessingInstruction或Comment,但還有兩個內置的訪問其子節點的快捷方式。第一個就是documentElement屬性,該屬性始終指向HTML 頁面中的<html>元素。另一個就是通過childNodes 列表訪問文檔元素,但通過documentElement 屬性則能更快捷、更直接地訪問該元素。以下麵這個簡單的頁面為例。
<html> <body> </body> </html>
這個頁面在經過瀏覽器解析後,其文檔中只包含一個子節點,即<html>元素。可以通過documentElement 或childNodes 列表來訪問這個元素,如下所示。
var html = document.documentElement; //取得對<html>的引用 alert(html === document.childNodes[0]); //true alert(html === document.firstChild); //true
這個例子說明,documentElement、firstChild 和childNodes[0]的值相同,都指向<html>元素。
作為HTMLDocument 的實例,document 對象還有一個body 屬性,直接指向<body>元素。因為開發人員經常要使用這個元素,所以document.body 在JavaScript 代碼中出現的頻率非常高,其用法如下。
var body = document.body; //取得對<body>的引用
所有瀏覽器都支持document.documentElement 和document.body 屬性。Document 另一個可能的子節點是DocumentType。通常將<!DOCTYPE>標簽看成一個與文檔其他部分不同的實體,可以通過doctype 屬性(在瀏覽器中是document.doctype)來訪問它的信息。
var doctype = document.doctype; //取得對<!DOCTYPE>的引用
瀏覽器對document.doctype 的支持差別很大,可以給出如下總結。
- IE8 及之前版本:如果存在文檔類型聲明,會將其錯誤地解釋為一個註釋並把它當作Comment節點;而document.doctype 的值始終為null。
- IE9+及Firefox:如果存在文檔類型聲明,則將其作為文檔的第一個子節點;document.doctype是一個DocumentType 節點,也可以通過document.firstChild 或document.childNodes[0]訪問同一個節點。
- Safari、Chrome 和Opera:如果存在文檔類型聲明,則將其解析,但不作為文檔的子節點。document.doctype 是一個DocumentType 節點,但該節點不會出現在document.childNodes 中。
由於瀏覽器對document.doctype 的支持不一致,因此這個屬性的用處很有限。
從技術上說,出現在<html>元素外部的註釋應該算是文檔的子節點。然而,不同的瀏覽器在是否解析這些註釋以及能否正確處理它們等方面,也存在很大差異。以下麵簡單的HTML 頁面為例。
<!--第一條註釋 --> <html> <body> </body> </html> <!--第二條註釋 -->
看起來這個頁面應該有3 個子節點:註釋、<html>元素、註釋。從邏輯上講,我們會認為document.childNodes 中應該包含與這3 個節點對應的3 項。但是,現實中的瀏覽器在處理位於<html>外部的註釋方面存在如下差異。
- IE8 及之前版本、Safari 3.1 及更高版本、Opera 和Chrome 只為第一條註釋創建節點,不為第二條註釋創建節點。結果,第一條註釋就會成為document.childNodes 中的第一個子節點。
- IE9 及更高版本會將第一條註釋創建為document.childNodes 中的一個註釋節點,也會將第二條註釋創建為document.childNodes 中的註釋子節點。
- Firefox 以及Safari 3.1 之前的版本會完全忽略這兩條註釋。
同樣,瀏覽器間的這種不一致性也導致了位於<html>元素外部的註釋沒有什麼用處。多數情況下,我們都用不著在document 對象上調用appendChild()、removeChild()和replaceChild()方法,因為文檔類型(如果存在的話)是只讀的,而且它只能有一個元素子節點(該節點通常早就已經存在了)。
2. 文檔信息
作為HTMLDocument 的一個實例,document 對象還有一些標準的Document 對象所沒有的屬性。
這些屬性提供了document 對象所表現的網頁的一些信息。其中第一個屬性就是title,包含著<title>元素中的文本——顯示在瀏覽器視窗的標題欄或標簽頁上。通過這個屬性可以取得當前頁面的標題,也可以修改當前頁面的標題並反映在瀏覽器的標題欄中。修改title 屬性的值不會改變<title>元素。來看下麵的例子。
//取得文檔標題 var originalTitle = document.title; //設置文檔標題 document.title = "New page title";
接下來要介紹的3 個屬性都與對網頁的請求有關,它們是URL、domain 和referrer。URL 屬性中包含頁面完整的URL(即地址欄中顯示的URL),domain 屬性中只包含頁面的功能變數名稱,而referrer屬性中則保存著鏈接到當前頁面的那個頁面的URL。在沒有來源頁面的情況下,referrer 屬性中可能會包含空字元串。所有這些信息都存在於請求的HTTP 頭部,只不過是通過這些屬性讓我們能夠在JavaScrip 中訪問它們而已,如下麵的例子所示。
//取得完整的URL var url = document.URL; //取得功能變數名稱 var domain = document.domain; //取得來源頁面的URL var referrer = document.referrer;
URL 與domain 屬性是相互關聯的。例如,如果document.URL 等於http://www.wrox.com/WileyCDA/,那麼document.domain 就等於www.wrox.com。
在這3 個屬性中,只有domain 是可以設置的。但由於安全方面的限制,也並非可以給domain 設置任何值。如果URL 中包含一個子功能變數名稱,例如p2p.wrox.com,那麼就只能將domain 設置為"wrox.com"(URL 中包含"www",如www.wrox.com 時,也是如此)。不能將這個屬性設置為URL 中不包含的域,如下麵的例子所示。
//假設頁面來自p2p.wrox.com 域 document.domain = "wrox.com"; // 成功 document.domain = "nczonline.net"; // 出錯!
當頁面中包含來自其他子域的框架或內嵌框架時,能夠設置document.domain 就非常方便了。由於跨域安全限制, 來自不同子域的頁面無法通過JavaScript 通信。而通過將每個頁面的document.domain 設置為相同的值,這些頁面就可以互相訪問對方包含的JavaScript 對象了。例如,假設有一個頁面載入自www.wrox.com,其中包含一個內嵌框架,框架內的頁面載入自p2p.wrox.com。
由於document.domain 字元串不一樣,內外兩個頁面之間無法相互訪問對方的JavaScript 對象。但如果將這兩個頁面的document.domain 值都設置為"wrox.com",它們之間就可以通信了。
瀏覽器對domain 屬性還有一個限制,即如果功能變數名稱一開始是“鬆散的”(loose),那麼不能將它再設置為“緊繃的”(tight)。換句話說,在將document.domain 設置為"wrox.com"之後,就不能再將其設置回"p2p.wrox.com",否則將會導致錯誤,如下麵的例子所示。
//假設頁面來自於p2p.wrox.com 域 document.domain = "wrox.com"; //鬆散的(成功) document.domain = "p2p.wrox.com"; //緊繃的(出錯!)
所有瀏覽器中都存在這個限制,但IE8 是實現這一限制的最早的IE 版本。
3. 查找元素
說到最常見的DOM 應用,恐怕就要數取得特定的某個或某組元素的引用,然後再執行一些操作了。
取得元素的操作可以使用document 對象的幾個方法來完成。其中,Document 類型為此提供了兩個方法:getElementById()和getElementsByTagName()。
第一個方法,getElementById(),接收一個參數:要取得的元素的ID。如果找到相應的元素則返回該元素,如果不存在帶有相應ID 的元素,則返回null。註意,這裡的ID 必須與頁面中元素的id特性(attribute)嚴格匹配,包括大小寫。以下麵的元素為例。
<div id="myDiv">Some text</div>
可以使用下麵的代碼取得這個元素:
var div = document.getElementById("myDiv"); //取得<div>元素的引用
但是,下麵的代碼在除IE7 及更早版本之外的所有瀏覽器中都將返回null。var div = document.getElementById("mydiv"); //無效的ID(在IE7 及更早版本中可以)IE8 及較低版本不區分ID 的大小寫,因此"myDiv"和"mydiv"會被當作相同的元素ID。如果頁面中多個元素的ID 值相同,getElementById()只返迴文檔中第一次出現的元素。IE7 及較低版本還為此方法添加了一個有意思的“怪癖”:name 特性與給定ID 匹配的表單元素(<input>、<textarea>、<button>及<select>)也會被該方法返回。如果有哪個表單元素的name 特性等於指定的ID,而且該元素在文檔中位於帶有給定ID 的元素前面,那麼IE 就會返回那個表單元素。來看下麵的例子。
<input type="text" name="myElement" value="Text field"> <div id="myElement">A div</div>
基於這段HTML 代碼,在IE7 中調用document.getElementById("myElement "),結果會返回<input>元素;而在其他所有瀏覽器中,都會返回對<div>元素的引用。為了避免IE 中存在的這個問題,最好的辦法是不讓表單欄位的name 特性與其他元素的ID 相同。另一個常用於取得元素引用的方法是getElementsByTagName()。這個方法接受一個參數,即要取得元素的標簽名,而返回的是包含零或多個元素的NodeList。在HTML 文檔中,這個方法會返回一個HTMLCollection 對象,作為一個“動態”集合,該對象與NodeList 非常類似。例如,下列代碼會取得頁面中所有的<img>元素,並返回一個HTMLCollection。
var images = document.getElementsByTagName("img");
這行代碼會將一個HTMLCollection 對象保存在images 變數中。與NodeList 對象類似,可以使用方括弧語法或item()方法來訪問HTMLCollection 對象中的項。而這個對象中元素的數量則可以通過其length 屬性取得,如下麵的例子所示。
alert(images.length); //輸出圖像的數量 alert(images[0].src); //輸出第一個圖像元素的src 特性 aler t(images.item(0).src); //輸出第一個圖像元素的 src 特性
HTMLCollection 對象還有一個方法,叫做namedItem(),使用這個方法可以通過元素的name特性取得集合中的項。例如,假設上面提到的頁面中包含如下<img>元素:
<img src="myimage.gif" name="myImage">
那麼就可以通過如下方式從images 變數中取得這個<img>元素:
var myImage = images.namedItem("myImage");
在提供按索引訪問項的基礎上,HTMLCollection 還支持按名稱訪問項,這就為我們取得實際想要的元素提供了便利。而且,對命名的項也可以使用方括弧語法來訪問,如下所示:
var myImage = images["myImage"];
對HTMLCollection 而言,我們可以向方括弧中傳入數值或字元串形式的索引值。在後臺,對數值索引就會調用item(),而對字元串索引就會調用namedItem()。
要想取得文檔中的所有元素,可以向getElementsByTagName()中傳入"*"。在JavaScript 及CSS中,星號(*)通常表示“全部”。下麵看一個例子。
var allElements = document.getElementsByTagName("*");
僅此一行代碼返回的HTMLCollection 中,就包含了整個頁面中的所有元素——按照它們出現的先後順序。換句話說,第一項是<html>元素,第二項是<head>元素,以此類推。由於IE 將註釋(Comment)實現為元素(Element),因此在IE 中調用getElementsByTagName("*")將會返回所有註釋節點。
雖然標準規定標簽名需要區分大小寫,但為了最大限度地與既有HTML 頁面相容,傳給getElementsByTagName()的標簽名是不需要區分大小寫的。但對於XML頁面而言(包括XHTML),getElementsByTagName()方法就會區分大小寫。
第三個方法,也是只有HTMLDocument 類型才有的方法,是getElementsByName()。顧名思義,這個方法會返回帶有給定name 特性的所有元素。最常使用getElementsByName()方法的情況是取得單選按鈕;為了確保發送給瀏覽器的值正確無誤,所有單選按鈕必須具有相同的name 特性,如下麵的例子所示。
<fieldset> <legend> Which color do you prefer? </legend> <ul> <li> <input type="radio" value="red" name="color" id="colorRed"> <label for="colorRed"> Red </label> </li> <li> <input type="radio" value="green" name="color" id="colorGreen"> <label for="colorGreen"> Green </label> </li> <li> <input type="radio" value="blue" name="color" id="colorBlue"> <label for="colorBlue"> Blue </label> </li> </ul> </fieldset>
如這個例子所示,其中所有單選按鈕的name 特性值都是"color",但它們的ID 可以不同。ID 的作用在於將<label>元素應用到每個單選按鈕,而name 特性則用以確保三個值中只有一個被髮送給瀏覽器。這樣,我們就可以使用如下代碼取得所有單選按鈕:
var radios = document.getElementsByName("color");
與getElementsByTagName()類似,getElementsByName()方法也會返回一個HTMLCollectioin。但是,對於這裡的單選按鈕來說,namedItem()方法則只會取得第一項(因為每一項的name 特性都相同)。
4. 特殊集合
除了屬性和方法,document 對象還有一些特殊的集合。這些集合都是HTMLCollection 對象,為訪問文檔常用的部分提供了快捷方式,包括:
- document.anchors,包含文檔中所有帶name 特性的<a>元素;
- document.applets,包含文檔中所有的<applet>元素,因為不再推薦使用<applet>元素,所以這個集合已經不建議使用了;
- document.forms,包含文檔中所有的<form>元素,與document.getElementsByTagName("form")得到的結果相同;
- document.images,包含文檔中所有的<img>元素,與document.getElementsByTagName("img")得到的結果相同;
- document.links,包含文檔中所有帶href 特性的<a>元素。
這個特殊集合始終都可以通過HTMLDocument 對象訪問到,而且,與HTMLCollection 對象類似,集合中的項也會隨著當前文檔內容的更新而更新。
5. DOM 一致性檢測
由於DOM 分為多個級別,也包含多個部分,因此檢測瀏覽器實現了DOM的哪些部分就十分必要了。document.implementation 屬性就是為此提供相應信息和功能的對象,與瀏覽器對DOM的實現直接對應。DOM1 級只為document.implementation 規定了一個方法,即hasFeature()。這個方法接受兩個參數:要檢測的DOM 功能的名稱及版本號。如果瀏覽器支持給定名稱和版本的功能,則該方法返回true,如下麵的例子所示:
var hasXmlDom = document.implementation.hasFeature("XML", "1.0");
下表列出了可以檢測的不同的值及版本號。
儘管使用hasFeature()確實方便,但也有缺點。因為實現者可以自行決定是否與DOM 規範的不同部分保持一致。事實上,要想讓hasFearture()方法針對所有值都返回true 很容易,但返回true有時候也不意味著實現與規範一致。例如,Safari 2.x 及更早版本會在沒有完全實現某些DOM 功能的情況下也返回true。為此,我們建議多數情況下,在使用DOM 的某些特殊的功能之前,最好除了檢測hasFeature()之外,還同時使用能力檢測。
6. 文檔寫入
有一個document 對象的功能已經存在很多年了,那就是將輸出流寫入到網頁中的能力。這個能力體現在下列4 個方法中:write()、writeln()、open()和close()。其中,write()和writeln()方法都接受一個字元串參數,即要寫入到輸出流中的文本。write()會原樣寫入,而writeln()則會在字元串的末尾添加一個換行符(\n)。在頁面被載入的過程中,可以使用這兩個方法向頁面中動態地加入內容,如下麵的例子所示。
<html> <head> <title> document.write() Example </title> </head> <body> <p> The current date and time is: <script type="text/javascript"> document.write("<strong>" + (new Date()).toString() + "</strong>"); </script> </p> </body> </html>
這個例子展示了在頁面載入過程中輸出當前日期和時間的代碼。其中,日期被包含在一個<strong>元素中,就像在HTML 頁面中包含普通的文本一樣。這樣做會創建一個DOM 元素,而且可以在將來訪問該元素。通過write()和writeln()輸出的任何HTML 代碼都將如此處理。
此外,還可以使用write()和writeln()方法動態地包含外部資源,例如JavaScript 文件等。在包含JavaScript 文件時,必須註意不能像下麵的例子那樣直接包含字元串"</script>
",因為這會導致該字元串被解釋為腳本塊的結束,它後面的代碼將無法執行。
<html> <head> <title> document.write() Example 2 </title> </head> <body> <script type="text/javascript"> document.write("<script type=\"text/javascript\" src=\"file.js\">" + "</script>"); </script> </body> </html>
即使這個文件看起來沒錯,但字元串"</script>
"將被解釋為與外部的<script>
標簽匹配,結果文本");
將會出現在頁面中。為避免這個問題,只需加入轉義字元\
即可;
第2 章也曾經提及這個問題,解決方案如下。
<html> <head> <title> document.write() Example 3 </title> </head> <body> <script type="text/javascript"> document.write("<script type=\"text/javascript\" src=\"file.js\">" + "<\/script>"); </script> </body> </html>
運行一下
字元串"<\/script>
"不會被當作外部<script>
標簽的關閉標簽,因而頁面中也就不會出現多餘的內容了。
前面的例子使用document.write()在頁面被呈現的過程中直接向其中輸出了內容。如果在文檔載入結束後再調用document.write(),那麼輸出的內容將會重寫整個頁面,如下麵的例子所示:
<html> <head> <title> document.write() Example 4 </title> </head> <body> <p> This is some content that you won't get to see because it will be overwritten. </p> <script type="text/javascript"> window.onload = function() { document.write("Hello world!"); }; </script> </body> </html>
在這個例子中,我們使用了window.onload 事件處理程式(事件將在第13 章討論),等到頁面完全載入之後延遲執行函數。函數執行之後,字元串"Hello world!"會重寫整個頁面內容。
方法open()和close()分別用於打開和關閉網頁的輸出流。如果是在頁面載入期間使用write()或writeln()方法,則不需要用到這兩個方法。
嚴格型XHTML 文檔不支持文檔寫入。對於那些按照application/xml+xhtml內容類型提供的頁面,這兩個方法也同樣無效。
更多章節教程:http://www.shouce.ren/api/view/a/15218