Document 對象時通往DOM功能的入口,它向你提供了當前文檔的信息,以及一組可供探索、導航、搜索或操作結構與內容的功能。 我們通過全局變數document訪問Document對象,它是瀏覽器為我們創建的關鍵對象之一。Document對象提供了文檔的整體信息,並讓你能夠訪問模型里的各個對象。簡單 ...
Document 對象時通往DOM功能的入口,它向你提供了當前文檔的信息,以及一組可供探索、導航、搜索或操作結構與內容的功能。
我們通過全局變數document訪問Document對象,它是瀏覽器為我們創建的關鍵對象之一。Document對象提供了文檔的整體信息,並讓你能夠訪問模型里的各個對象。簡單示例如下:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Example</title>
</head>
<body>
<p id="first">
15歲的時候再得到那個5歲的時候熱愛的布娃娃,65歲的時候終於有錢買25歲的時候熱愛的那條裙子,又有什麼意義。
什麼都可以從頭再來,只有青春不能。那麼多事情,跟青春綁在一起就是美好,離開青春,就是傻冒。
</p>
<p id="second">
你的特別不是因為你在創業,不是因為你進了牛企,不是因為你的牛offer,而是因為你就是你,堅信自己的特別,堅信自己的內心,勇敢做自己。
IT DOESN'T MATTER WHERE YOU ARE, IT MATTERS WHO YOU ARE.
</p>
<script>
document.writeln("<pre>URL: "+document.URL);
var elems = document.getElementsByTagName("p");
for(var i=0; i<elems.length; i++){
document.writeln("Element ID: "+elems[i].id);
elems[i].style.border = "medium double black";
elems[i].style.padding = "4px";
}
document.write("</pre>");
</script>
</body>
</html>
我們能對Document對象做的最基本操作之一就是獲取當前正在處理的HTML文檔信息。這就是腳本的第一行所做的:
document.writeln("<pre>URL: "+document.URL);
此例中,讀取了 document.URL 屬性的值,它返回的是當前文檔的URL。瀏覽器就是用這個URL載入此腳本所屬文檔的。
這句語句還調用了writeln方法:
document.writeln("<pre>URL: "+document.URL);
此方法會將內容附加到HTML文檔的末尾。此例中,寫入了pre元素的開始標簽和URL屬性的值。這就是一個非常簡單的修改DOM範例,意思是我已經改變了文檔的結構。
接下來,從文檔中選擇了一些元素:
var elems = document.getElementsByTagName("p");
getElementsByTagName 選擇屬於某一給定類型的所有元素,此例中是p元素。任何包含在文檔里的p元素都會被該方法返回,並被存放在一個elems的變數里。所有元素都是由HTMLElement對象代表的,它提供了基本的功能以代表HTML元素。getElementsByTagName方法返回的結果是HTMLElement對象所組成的一個集合。
有了可以處理的HTMLElement對象集合之後,使用了一個for迴圈來列舉集合里的內容,處理瀏覽器從HTML文檔里找出的各個p元素:
for(var i=0; i<elems.length; i++){
document.writeln("Element ID: "+elems[i].id);
elems[i].style.border = "medium double black";
elems[i].style.padding = "4px";
}
對集合里的每個HTMLElement,會讀取它的id屬性來獲取id 值,然使用 document.writeln 方法吧結果附加到之前生成的pre元素的內容上:
for(var i=0; i<elems.length; i++){
document.writeln("Element ID: "+elems[i].id);
elems[i].style.border = "medium double black";
elems[i].style.padding = "4px";
}
id屬性是HTMLElement定義的眾多屬性之一。你可以使用這些屬性來獲取某個元素的信息,也可以對其進行修改(改動會同時應用到它所代表的HTML元素上)。此例中,使用了style屬性來改變CSS border和padding 屬性的值:
for(var i=0; i<elems.length; i++){
document.writeln("Element ID: "+elems[i].id);
elems[i].style.border = "medium double black";
elems[i].style.padding = "4px";
}
這些改動為每個圓度都創建了一個內嵌樣式,這些元素都是之前用getElementsByTagName 方法找到的。當修改某個對象時,瀏覽器會立即把改動應用到對應的元素上,此例中是給這些p元素添加內邊距和邊框。
腳本的最後一行寫入了pre元素的結束標簽,就是在腳本開頭初始化的那個元素。這裡用的是write方法,但不會給添加到文檔里的字元串附上行結束字元。這兩種方法區別不大,除非編寫的內容是預格式化的,或者使用非標準的空白處理方式。
使用pre元素就意思味著writeln方法所添加的行結束符字元會被用來構建內容。從下麵的顯示效果圖可以看到文檔排列的效果:
1. 使用 Document 元數據
下表介紹了可以用來獲取文檔元數據的屬性。
1.1 獲取文檔信息
可以使用元數據屬性來獲取一些有用的文檔信息。
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Example</title>
</head>
<body>
<script>
document.writeln("<pre>");
document.writeln("document.title);
document.write("</pre>");
</script>
</body>
</html>
理解怪異模式
compatMode 屬性告訴你瀏覽器是如何處理文檔內容的。現如今存在著大量的非標準HTML,瀏覽器則試圖顯示出這類網頁,哪怕它們並不遵循HTML規範。一些這樣的內容依賴於瀏覽器的獨特功能,而這些功能來源於瀏覽器依靠自身特點(而非遵循標準)進行競爭的年代。compatMode屬性會返回兩個值中的一個,如下表所示:
1.2 使用Location 對象
document.location 屬性返回一個Location 對象,這個對象提供了細粒度的文檔地址信息,也允許導航到其他文檔上。下表介紹了Location對象里的函數和屬性:
document.location 屬性最簡單的用途就是獲取當前文檔的地址信息。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>使用Location 對象</title>
</head>
<body>
<script>
document.writeln("<pre>")
document.writeln("protocol: "+ document.location.protocol);
document.writeln("host: "+ document.location.host);
document.writeln("href: "+ document.location.href);
document.writeln("hostname: "+ document.location.hostname);
document.writeln("port: "+ document.location.port);
document.writeln("pathname: "+ document.location.pathname);
document.writeln("search: "+ document.location.search);
document.writeln("hash: "+ document.location.hash);
document.write("</pre>");
</script>
</body>
</html>
PS:當埠號為HTTP預設的80時,屬性不會返回值
使用Location對象導航到其他地方
還可以使用Location對象(通過document.location屬性)來導航到其他地方。具體的顯示方式有好幾種。首先,可以為之前示例用到的某個屬性指派新值。
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Example</title>
</head>
<body>
<p id="first">
15歲的時候再得到那個5歲的時候熱愛的布娃娃,65歲的時候終於有錢買25歲的時候熱愛的那條裙子,又有什麼意義。
什麼都可以從頭再來,只有青春不能。那麼多事情,跟青春綁在一起就是美好,離開青春,就是傻冒。
</p>
<button id="pressme">Press Me</button>
<p id="second">
你的特別不是因為你在創業,不是因為你進了牛企,不是因為你的牛offer,而是因為你就是你,堅信自己的特別,堅信自己的內心,勇敢做自己。
IT DOESN'T MATTER WHERE YOU ARE, IT MATTERS WHO YOU ARE.
</p>
<img id="banana" src="imgs/banana-small.png" alt="small banna" />
<script>
document.getElementById("pressme").onclick = function(){
document.location.hash = "banana";
}
</script>
</body>
</html>
此例中包含一個button元素,當它被點擊時會給document.location.hash 屬性指派一個新值。通過一個事件把按鈕和點擊時只需的JavaScript函數關聯起來。這就是onclick屬性的作用。
這一改動會讓瀏覽器導航到某一id屬性值匹配hash值的元素上,這這個案例里是img元素。從下麵的效果圖可以看到導航的效果:
雖然只是導航到了相同文檔的不同位置,但也可以用Location對象的屬性來導航到其他文檔。不過,這種做法通常是用href屬性實現的,因為可以設置完整的URL。也可以使用Location對象定義的一些方法。
assign和replace方法的區別在於,replace會把當前文檔從瀏覽器歷史中移除,這就意味著如果用戶點擊了後退按鈕,瀏覽器就會跳過當前文檔,就像它從未訪問過該文檔一樣。下麵的例子展示瞭如何使用assign方法。
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Example</title>
</head>
<body>
<button id="pressme">Press Me</button>
<script>
document.getElementById("pressme").onclick = function(){
document.location.assign("http://yexiaochao.github.io")
}
</script>
</body>
</html>
當用戶點擊button元素時,瀏覽器會導航到指定的URL上,在這個示例中是 http://yexiaochao.github.io 。
1.3 讀取和寫入cookie
cookie 屬性讓你可以讀取、添加和更新文檔所關聯的 cookie。下麵例子對此進行了演示:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Example</title>
</head>
<body>
<p id="cookiedata"></p>
<button id="write">Add Coojie</button>
<button id="update">Update Cookie</button>
<script>
var cookieCount = 0;
document.getElementById("write").onclick = createCookie;
document.getElementById("update").onclick = updateCookie;
function readCookies(){
document.getElementById("cookiedata").innerHTML = document.cookie;
}
function createCookie(){
cookieCount++;
document.cookie =cookieCount;
readCookies();
}
function updateCookie(){
document.cookie = "Cookie_"+cookieCount+"=Update_"+cookieCount;
readCookies();
}
</script>
</body>
</html>
cookie 屬性的工作方式稍微有點古怪。當讀取該屬性的值時,會得到與文檔相關聯的所有cookie。cookie是形式為name=value的名稱/值對。如果存在多個cookie,那麼cookie屬性會把它們作為結果返回,之間以分號相隔,如 name1=value1;name2=value2。
與之相對,當想要創建新的cookie時,要指派新的cookie時,要指派一個新的名稱/值對作為cookie屬性的值,它將會添加到文檔的cookie集合。一次只能設置一個cookie。如果設置的值和現有的某個cookie具備相同的名稱部分,那麼就會用值部分更新那個cookie。
為了演示這一點,代碼中包含了一段腳本來讀取、創新和更新cookie。 readCookies 函數讀取document.cookie 屬性的值,並將結果設置為某個段落(p)元素的內容。
這個文檔里有兩個button元素。當Add Cookie按鈕被點擊時,createCookie函數會給cookie屬性指派一個新值,這個值會被添加到cookie集合中。Update Cookie按鈕會調用updateCookie函數。這個函數給某個現有的cookie提供一個新值。從下圖可以看到這段腳本的效果:
從效果圖中可以看到,添加了三個cookie,其中一個已經被更新為某個新值。雖然添加cookie的預設形式是name=value,但可以額外應用一些數據來改變cookie的處理方式。下表介紹了這些額外數據:
這些額外的項目可以被附加到名稱/值對的後面,以分號分隔,就像這樣:
document.cookie = "MyCookie=MyValue;max-age=10";
1.4 理解就緒狀態
document.readyState 屬性提供了載入和解析HTML文檔過程中當前處於哪個階段的信息。請記住,在預設情況下瀏覽器會在遇到文檔里的script元素時立即執行腳本,但可以使用defer屬性推遲腳本的執行。正如我們在一些例子所見到的,可以使用Javascript的事件系統來獨立執行各個函數,作為對文檔變化或用戶操作的反饋。
在所有這些情況下,瞭解瀏覽器載入和處理HTML到了哪個階段可能會很有用。readyState屬性會返回三個不同的值。
隨著瀏覽器逐步載入和處理文檔,readyState屬性的值從loading轉為 interactive,再轉為complete。這個屬性和readystatechange事件結合使用時用處最大,該事件會在每次readyState屬性的值發生變化時觸發。
下麵代碼展示瞭如何同時使用這兩個事件和屬性來完成一項常見任務。
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Example</title>
<script>
document.onreadystatechange = function(){
if (document.readyState == "interactive"){
document.getElementById("pressme").onclick = function(){
document.getElementById("results").innerHTML = "Button Pressed";
}
}
}
</script>
</head>
<body>
<button id="pressme">Press Me</button>
<pre id="results"></pre>
</body>
</html>
這段腳本使用文檔就緒狀態來推遲一個函數的執行,直到文檔進入interactive 階段。腳本代碼要求能夠找到在腳本執行時尚未被瀏覽器載入的文檔元素。通過推遲腳本執行直至文檔載入完成,就能確認這些元素是可以找到的。這種方式可以作為把script元素放到文檔末尾的替代。
1.5 獲取DOM的實現情況
document.implementation 屬性提供了瀏覽器對DOM功能的實現信息。這個屬性返回一個DOMImplementation 對象,它包含一個 hasFeature 方法,可以使用這個方法來判斷哪些DOM功能已實現。
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Example</title>
</head>
<body>
<script>
var features = ["Core","HTML","CSS","Selectors-API"];
var levels = ["1.0","2.0","3.0"];
document.writeln("<pre>")
for(var i = 0; i < features.length; i++){
document.writeln("Checking for features: "+features[i]);
for (var j=0; j<levels.length; j++){
document.write(features[i]+" Level "+levels[j]+": ");
document.writeln(document.implementation.hasFeature(features[i],levels[j]))
}
}
document.write("</pre>");
</script>
</body>
</html>
這段腳本檢測了若幹不同的DOM功能,以及所定義的功能等級。它並不像看上去那麼有用。首先,瀏覽器並不總是能正確報告它們實現的功能。某些功能實現並不會通過hasFeature方法進行報告,而另一些報告了卻根本沒有實現。其次,瀏覽器報告了某項功能並不意味著它的實現方式是有用的。雖然這個問題不如以前嚴重,但DOM的實現是存在一些差別的,
如果你打算編寫能在所有主流瀏覽器上工作的代碼(你也應該這樣想),那麼hasFeature方法的用處不大。你應該選擇在測試階段全面檢查代碼,在需要的時候測試支持情況和備用措施,同時也可以考慮使用某個JavaScript庫(例如jQuery),它可以消除不同DOM實現之間的差別。
2. 獲取 HTML 元素文檔
Document 對象的一大關鍵功能是作為一個入口,能訪問代表文檔里各個元素的對象。可以用幾種不同的方法來執行這個任務。有些屬性會返回代表特定文檔元素類型的對象,有些方法能很方便地運用條件搜索來找到匹配的元素,還可以將DOM視為一棵樹並沿著它的結構進行導航。
2.1 使用屬性獲取元素對象
Document對象提供了一組屬性,它們會返回代表文檔中特定元素或元素類型的對象。
上表裡描述的大多數都返回一個HTMLCollection 對象。DOM就是用這種方式來表示一組代表元素的對象集合。下麵代碼演示了訪問集合內對象的兩種方法。
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Example</title>
<style>
pre { border: medium double black;}
</style>
</head>
<body>
<pre id="results"></pre>
<img id="lemon" src="imgs/lemon.png" alt="Lemon" />
<img src="imgs/apple.png" id="apple" alt="apple" />
<img src="imgs/banana-small.png" alt="small banana" id="banana">
<script>
var resultsElement = document.getElementById("results");
var elems = document.images;
for(var i=0;i<elems.length;i++){
resultsElement.innerHTML += "Imgage Element: "+elems[i].id +"\n";
}
var srcValue = elems.namedItem("apple").src;
resultsElement.innerHTML += "Src for apple element is: "+ srcValue + "\n";
</script>
</body>
</html>
第一種使用HTMLCollection 對象的方法是將它視為一個數組。它的length 屬性會返回集合里的項目數量,它還支持使用標準的 JavaScript 數組索引標記(element[i]這種表示方法)來直接訪問集合里的各個對象。此例中用 document.images 屬性獲得了一個 HTMLCollection, 它包含了所有代表文檔里 img 元素的對象。
第二種方法是使用 namedItem 方法,它會返回集合裡帶有指定id或name屬性值的項目。此例中使用了namedItem方法來獲取代表某個特定img元素的對象,該元素的id屬性值為apple。
此例效果圖如下:
2.2 搜索元素
Document 對象定義了許多方法,可以用它們搜索文檔里的元素。
這些方法中的一些方法會返回多個元素。在表裡它們展現為返回一個HTMLElement對象數組,但嚴格來說並非如此。事實上,這些方法返回一個NodeList,它是底層DOM規範的一部分,處理的是通用結構文檔格式,而不僅僅是HTML。但是,對這些用途而言,可以將它們視為數組,把註意力集中在HTML5上。
這些搜索方法可以被分成兩類。下麵代碼演示了其中一類,即名稱 getElement開頭的那些方法。
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Example</title>
<style>
pre { border: medium double black;}
</style>
</head>
<body>
<pre id="results"></pre>
<img id="lemon" class="fruits" name="apple" src="imgs/lemon.png" alt="Lemon" />
<p>
There are lots of different kinds of fruits - there are over 500 varieties of bananas alone.
By the time we add the countless types of apples, oranges, and other well-known fruit,
we are faced with thousands of choices.
</p>
<img id="apple" class="fruits" name="apple" src="imgs/apple.png" alt="apple" />
<p>
One of the most interesting aspects of fruit is the variety available in each country.
I live near London, in an area which is know for its apples.
</p>
<img id="banana" src="imgs/banana-small.png" alt="small banana">
<script>
var resultElement = document.getElementById("results");
var pElems = document.getElementsByTagName("p");
resultElement.innerHTML += "There are " + pElems.length + " p elements\n";
var fruitsElems = document.getElementsByClassName("fruits");
resultElement.innerHTML += "There are "+ fruitsElems.length + " elements in the fruits class\n";
var nameElems = document.getElementsByName("apple");
resultElement.innerHTML += "There are " + nameElems.length + " elements with the name 'apple'";
</script>
</body>
</html>
在使用 getElementById方法時,如果找不到帶有指定id值的元素,瀏覽器就會返回null。與之相對,其他的方法總是會返回一個HTMLElement對象數組,但如果找不到匹配,length屬性就會返回0。
用CSS選擇器進行搜索
使用CSS選擇器是一種有用的替代性搜索方式。選擇器可以在文檔里找到範圍更廣的元素。
修改前面示例的JavaScript代碼如下:
<script>
var resultsElement = document.getElementById("results");
var elems = document.querySelectorAll("p,img#apple");
resultsElement.innerHTML += "The selector matched "+ elems.length + " elements\n"
</script>
此例中使用了一個選擇器,它會匹配所有的p元素和id值為apple的img元素。用其他document方法很難達到同樣的效果。
2.4 合併進行鏈式搜索
DOM的一個實用功能是幾乎所有Document對象實現的搜索方法同時也能被HTMLElement對象實現(一個例外),這使得可以合併進行鏈式搜索。唯一的例外是getElementById方法,只有Document對象才能使用它。下麵代碼展示了鏈式搜索:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>合併進行鏈式搜索</title>
<style>
pre { border: medium double green;}
</style>
</head>
<body>
<pre id="results"></pre>
<p id="tblock">
There are lots of different kinds of fruit - there are over 500 varieties of <span id="banana">banana</span> alone.
Bt the time we add the countless types of <span id="apple">apples</span>, <span id="orange">oranges</span>,
and other well-known fruit, we are faced with thousands of choices.
</p>
<script>
var resultsElement = document.getElementById("results");
var elems = document.getElementById("tblock").getElementsByTagName("span");
resultsElement.innerHTML += "There are " + elems.length + " span elements\n";
var elems2 = document.getElementById("tblock").querySelectorAll("span");
resultsElement.innerHTML += "There are " + elems2.length + " span elements (Mix)\n";
var selElems = document.querySelectorAll("#tblock > span");
resultsElement.innerHTML += "There are " + selElems.length + " span elements (CSS)\n";
</script>
</body>
</html>
此例中有兩次鏈式搜索,這兩次都從getElementById方法開始(它會返回之後進行處理的單個對象)。第一次鏈接中,使用getElementsByTagName方法鏈接了一個搜索;在第一次鏈接中,則通過querySelectorAll方法使用了一個非常簡單的CSS選擇器。這些鏈接都返回了一個span元素的集合,它們都位於id為tblock的p元素之內。
當然,也可以通過單獨給Document對象應用CCS選擇器方法來實現同樣的效果,但是這一功能在某些情況下會很方便,比如處理由腳本中的其他函數(或第三方腳本)所生成的HTMLElement對象。此例顯示效果如下:
3. 在DOM樹里導航
另一種搜索方法是將DOM視為一棵樹,然後在它的層級結構里導航。所有的DOM對象都支持一組屬性和方法來做到這點。
下麵代碼展示了一段腳本,它可以導航到文檔各處,併在一個pre元素里顯示當前所選元素的信息。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>在DOM樹里導航</title>
<style>
pre { border: medium double green;}
</style>
</head>
<body>
<pre id="results"></pre>
<p id="tblock">
There are lots of different kinds of fruit - there are over 500 varieties of <span id="banana">banana</span> alone.
Bt the time we add the countless types of <span id="apple">apples</span>, <span id="orange">oranges</span>,
and other well-known fruit, we are faced with thousands of choices.
</p>
<img id="apple" class="fruits images" name="apple" src="../imgs/apple.png" alt="apple" />
<img id="banana" src="../imgs/banana-small.png" alt="small banana" />
<p>
One of most interesting aspects of fruit is the variety available in each country.
I live near London, in an area which is known for its apple.
</p>
<p>
<button id="parent">Parent</button>
<button id="child">First Child</button>
<button id="prev">Prev Sibling</button>
<button id="next">Next Sibling</button>
</p>
<script>
var resultsElem = document.getElementById("results");
var element = document.getElementById("tblock");
var buttons = document.getElementsByTagName("button");
for(var i = 0; i < buttons.length; i++){
buttons[i].onclick = handleButtonClick;
}
processNewElement(element);
function handleButtonClick(e){
if(element.style){
element.style.backgroundColor = "white";
}
if(e.target.id == "parent" && element != document.body){
element = element.parentNode;
}else if(e.target.id == "child" && element.hasChildNodes()){
element = element.firstChild;
}else if(e.target.id == "prev" && element.previousSibling){
element = element.previousSibling;
}else if(e.target.id == "next" && element.nextSibling){
element = element.nextSibling;
}
processNewElement(element);
if(element.style){
element.style.backgroundColor = "lightgrey";
}
}
function processNewElement(elem){
resultsElem.innerHTML = "Element type: " + elem + "\n";
resultsElem.innerHTML += "Element id: " + elem.id + "\n";
resultsElem.innerHTML += "Has child nodes: " + elem.hasChildNodes() + "\n";
if(elem.previousSibling){
resultsElem.innerHTML += ("Prev sibling is: " + elem.previousSibling + "\n");
}else {
resultsElem.innerHTML += "No prev sibling\n";
}
if (elem.nextSibling){
resultsElem.innerHTML += "Next sibling is " + elem.nextSibling + "\n";
}else {
resultsElem.innerHTML += "No next sibling\n";
}
}
</script>
</body>
</html>
這段腳本的重要之處用粗體進行顯示,它們是實際進行導航操作的部分。腳本的其餘部分則是在做準備工作,處理按鈕點擊以及顯示當前所選元素的信息。