除了Document 類型之外,Element 類型就要算是Web 編程中最常用的類型了。Element 類型用
於表現XML 或HTML 元素,提供了對元素標籤名、子節點及特性的訪問。Element 節點具有以下特徵:
nodeType 的值為1;
nodeName 的值為元素的標籤名;
nodeValue 的值為null;
parentNode 可能是Document 或Element;
其子節點可能是Element、Text、Comment、ProcessingInstruction、CDATASection 或
EntityReference。
要訪問元素的標籤名,可以使用nodeName 屬性,也可以使用tagName 屬性;這兩個屬性會返回
相同的值(使用後者主要是為了清晰起見)。以下面的元素為例:
<div id="myDiv"></div>可以像下面這樣取得這個元素及其標籤名:
var div = document.getElementById("myDiv"); alert(div.tagName); //"DIV" alert(div.tagName == div.nodeName); //true這裡的元素標籤名是div,它擁有一個值為"myDiv"的ID。可是,div.tagName 實際上輸出的是
"DIV"而非"div"。在HTML 中,標籤名始終都以全部大寫表示;而在XML(有時候也包括XHTML)
中,標籤名則始終會與原始碼中的保持一致。假如你不確定自己的腳本將會在HTML 還是XML 文檔中
執行,最好是在比較之前將標籤名轉換為相同的大小寫形式,如下面的例子所示:
if (element.tagName == "div"){ //不能這樣比較,很容易出錯! //在此執行某些操作 } if (element.tagName.toLowerCase == "div"){ //這樣最好(適用於任何文檔) //在此執行某些操作 }這個例子展示了圍繞tagName 屬性的兩次比較操作。第一次比較非常容易出錯,因為其代碼在
HTML 文檔中不管用。第二次比較將標籤名轉換成了全部小寫,是我們推薦的做法,因為這種做法適用
於HTML 文檔,也適用於XML 文檔。
可以在任何瀏覽器中通過腳本訪問Element 類型的構造函數及原型,包括IE8 及
之前版本。在Safari 2 之前版本和Opera 8 之前的版本中,不能訪問Element 類型的構
造函數。
1. HTML
元素
所有HTML 元素都由HTMLElement 類型表示,不是直接通過這個類型,也是通過它的子類型來表
示。HTMLElement 類型直接繼承自Element 並添加了一些屬性。添加的這些屬性分別對應於每個HTML
元素中都存在的下列標準特性。
id,元素在文檔中的唯一標識符。
title,有關元素的附加說明信息,一般通過工具提示條顯示出來。
lang,元素內容的語言代碼,很少使用。
dir,語言的方向,值為"ltr"(left-to-right,從左至右)或"rtl"(right-to-left,從右至左),
也很少使用。
className,與元素的class 特性對應,即為元素指定的CSS 類。沒有將這個屬性命名為class,
是因為class 是ECMAScript 的保留字(有關保留字的信息,請參見第1 章)。
上述這些屬性都可以用來取得或修改相應的特性值。以下面的HTML 元素為例:
<div id="myDiv" class="bd" title="Body text" lang="en" dir="ltr"></div>元素中指定的所有信息,都可以通過下列JavaScript 代碼取得:var div = document.getElementById("myDiv"); alert(div.id); //"myDiv""
alert(div.className); //"bd" alert(div.title); //"Body text" alert(div.lang); //"en" alert(div.dir); //"ltr"當然,像下面這樣通過為每個屬性賦予新的值,也可以修改對應的每個特性:div.id = "someOtherId"; div.className = "ft"; div.title =
"Some other text"; div.lang = "fr"; div.dir ="rtl";
並不是對所有屬性的修改都會在頁面中直觀地表現出來。對id 或lang 的修改對用戶而言是透明
不可見的(假設沒有基於它們的值設置的CSS 樣式),而對title 的修改則只會在滑鼠移動到這個元素
之上時才會顯示出來。對dir 的修改會在屬性被重寫的那一刻,立即影響頁面中文本的左、右對齊方式。
修改className 時,如果新類關聯了與此前不同的CSS 樣式,那麼就會立即應用新的樣式。
前面提到過,所有HTML 元素都是由HTMLElement 或者其更具體的子類型來表示的。下表列出了
所有HTML 元素以及與之關聯的類型(以斜體印刷的元素表示已經不推薦使用了)。注意,表中的這些
類型在Opera、Safari、Chrome 和Firefox 中都可以通過JavaScript 訪問,但在IE8 之前的版本中不能通
過JavaScript 訪問。
👁 Image
...
👁 Image
...
表中的每一種類型都有與之相關的特性和方法。本書將會討論其中很多類型。
2.
取得特性
每個元素都有一或多個特性,這些特性的用途是給出相應元素或其內容的附加信息。操作特性的
DOM 方法主要有三個,分別是getAttribute、setAttribute和removeAttribute。這三
個方法可以針對任何特性使用,包括那些以HTMLElement 類型屬性的形式定義的特性。來看下面的例子:
var div = document.getElementById("myDiv"); alert(div.getAttribute("id")); //"myDiv" alert(div.getAttribute("class")); //"bd" alert(div.getAttribute("title")); //"Body text"
alert(div.getAttribute("lang")); //"en" alert(div.getAttribute("dir")); //"ltr"注意,傳遞給getAttribute的特性名與實際的特性名相同。因此要想得到class 特性值,應
該傳入"class"而不是"className",後者只有在通過對象屬性訪問特性時才用。如果給定名稱的特性
不存在,getAttribute返回null。
通過getAttribute方法也可以取得自定義特性(即標準HTML 語言中沒有的特性)的值,以
下面的元素為例:<div id="myDiv" my_special_attribute="hello!"></div>這個元素包含一個名為my_special_attribute 的自定義特性,它的值是"hello!"。可以像取
得其他特性一樣取得這個值,如下所示:var value = div.getAttribute("my_special_attribute");過,特性的名稱是不區分大小寫的,即"ID"和"id"代表的都是同一個特性。另外也要注意,根
據HTML5 規範,自定義特性應該加上data-前綴以便驗證。
任何元素的所有特性,也都可以通過DOM 元素本身的屬性來訪問。當然,HTMLElement 也會有5
個屬性與相應的特性一一對應。不過,只有公認的(非自定義的)特性才會以屬性的形式添加到DOM
對象中。以下面的元素為例:
<div id="myDiv" align="left" my_special_attribute="hello!"></div>因為id 和align 在HTML 中是<div>的公認特性,因此該元素的DOM 對象中也將存在對應的屬
性。不過,自定義特性my_special_attribute 在Safari、Opera、Chrome 及Firefox 中是不存在的;
但IE 卻會為自定義特性也創建屬性,如下面的例子所示:alert(div.id); //"myDiv" alert(div.my_special_attribute); //undefined(IE 除外) alert(div.align); //"left"有兩類特殊的特性,它們雖然有對應的屬性名,但屬性的值與通過getAttribute返回的值並不
相同。第一類特性就是style,用於通過CSS 為元素指定樣式。在通過getAttribute訪問時,返
回的style 特性值中包含的是CSS 文本,而通過屬性來訪問它則會返回一個對象。由於style 屬性是
用於以編程方式訪問元素樣式的(本章後面討論),因此並沒有直接映射到style 特性。
第二類與眾不同的特性是onclick 這樣的事件處理程序。當在元素上使用時,onclick 特性中包
含的是JavaScript 代碼,如果通過getAttribute訪問,則會返回相應代碼的字符串。而在訪問
onclick 屬性時,則會返回一個JavaScript 函數(如果未在元素中指定相應特性,則返回null)。這是
因為onclick 及其他事件處理程序屬性本身就應該被賦予函數值。
由於存在這些差別,在通過JavaScript 以編程方式操作DOM 時,開發人員經常不使用getAttri-
bute,而是只使用對象的屬性。只有在取得自定義特性值的情況下,才會使用getAttribute方法。
在IE7 及以前版本中,通過getAttribute方法訪問style 特性或onclick 這樣
的事件處理特性時,返回的值與屬性的值相同。換句話說,getAttribute("style")返
回一個對象,而getAttribute("onclick")返回一個函數。雖然IE8 已經修復了這個
bug,但不同IE版本間的不一致性,也是導致開發人員不使用getAttribute訪問HTML
特性的一個原因。
3.
設置特性
與getAttribute對應的方法是setAttribute,這個方法接受兩個參數:要設置的特性名和
值。如果特性已經存在,setAttribute會以指定的值替換現有的值;如果特性不存在,setAttribute
則創建該屬性並設置相應的值。來看下面的例子:
div.setAttribute("id", "someOtherId"); div.setAttribute("class", "ft"); div.setAttribute("title", "Some other text"); div.setAttribute("lang","fr"); div.setAttribute("dir",
"rtl");通過setAttribute方法既可以操作HTML 特性也可以操作自定義特性。通過這個方法設置的
特性名會被統一轉換為小寫形式,即"ID"最終會變成"id"。
因為所有特性都是屬性,所以直接給屬性賦值可以設置特性的值,如下所示。div.id = "someOtherId"; div.align = "left";不過,像下面這樣為DOM 元素添加一個自定義的屬性,該屬性不會自動成為元素的特性。div.mycolor = "red"; alert(div.getAttribute("mycolor")); //null(IE 除外)
這個例子添加了一個名為mycolor 的屬性並將它的值設置為"red"。在大多數瀏覽器中,這個屬
性都不會自動變成元素的特性,因此想通過getAttribute取得同名特性的值,結果會返回null。
可是,自定義屬性在IE 中會被當作元素的特性,反之亦然。
在IE7 及以前版本中,setAttribute存在一些異常行為。通過這個方法設置
class 和style 特性,沒有任何效果,而使用這個方法設置事件處理程序特性時也
一樣。儘管到了IE8 才解決這些問題,但我們還是推薦通過屬性來設置特性。
要介紹的最後一個方法是removeAttribute,這個方法用於徹底刪除元素的特性。調用這個方
法不僅會清除特性的值,而且也會從元素中完全刪除特性,如下所示:
div.removeAttribute("class");這個方法並不常用,但在序列化DOM 元素時,可以通過它來確切地指定要包含哪些特性。
IE6 及以前版本不支持removeAttribute。
4. attributes 屬性
Element 類型是使用attributes 屬性的唯一一個DOM 節點類型。attributes 屬性中包含一個
NamedNodeMap,與NodeList 類似,也是一個「動態」的集合。元素的每一個特性都由一個Attr 節
點表示,每個節點都保存在NamedNodeMap 對象中。NamedNodeMap 對象擁有下列方法。
getNamedItem(name):返回nodeName 屬性等於name 的節點;
removeNamedItem(name):從列表中移除nodeName 屬性等於name 的節點;
setNamedItem(node):向列表中添加節點,以節點的nodeName 屬性為索引;
item(pos):返回位於數字pos 位置處的節點。
attributes 屬性中包含一系列節點,每個節點的nodeName 就是特性的名稱,而節點的nodeValue
就是特性的值。要取得元素的id 特性,可以使用以下代碼。
var id = element.attributes.getNamedItem("id").nodeValue;以下是使用方括號語法通過特性名稱訪問節點的簡寫方式。
var id = element.attributes["id"].nodeValue;也可以使用這種語法來設置特性的值,即先取得特性節點,然後再將其nodeValue 設置為新值,
如下所示。
element.attributes["id"].nodeValue = "someOtherId";調用removeNamedItem方法與在元素上調用removeAttribute方法的效果相同——直接刪
除具有給定名稱的特性。下面的例子展示了兩個方法間唯一的區別,即removeNamedItem返回表示
被刪除特性的Attr 節點。
var oldAttr = element.attributes.removeNamedItem("id");最後,setNamedItem是一個很不常用的方法,通過這個方法可以為元素添加一個新特性,為此
需要為它傳入一個特性節點,如下所示。element.attributes.setNamedItem(newAttr);一般來說,由於前面介紹的attributes 的方法不夠方便,因此開發人員更多的會使用
getAttribute、removeAttribute和setAttribute方法。
不過,如果想要遍曆元素的特性,attributes 屬性倒是可以派上用場。在需要將DOM 結構序列
化為XML 或HTML 字符串時,多數都會涉及遍曆元素特性。以下代碼展示了如何疊代元素的每一個特
性,然後將它們構造成name="value" name="value"這樣的字符串格式。function outputAttributes(element){ var pairs = new Array, attrName, attrValue, i, len; for (i=0, len=element.attributes.length; i < len; i++){ attrName
= element.attributes[i].nodeName; attrValue = element.attributes[i].nodeValue; pairs.push(attrName + "=\"" + attrValue + "\""); } return pairs.join(" ");
}這個函數使用了一個數組來保存名值對,最後再以空格為分隔符將它們拼接起來(這是序列化長字
符串時的一種常用技巧)。通過attributes.length 屬性,for 循環會遍歷每個特性,將特性的名稱
和值輸出為字符串。關於以上代碼的運行結果,以下是兩點必要的說明。
針對attributes 對象中的特性,不同瀏覽器返回的順序不同。這些特性在XML 或HTML 代
碼中出現的先後順序,不一定與它們出現在attributes 對象中的順序一致。
IE7 及更早的版本會返回HTML 元素中所有可能的特性,包括沒有指定的特性。換句話說,返
回100 多個特性的情況會很常見。
針對IE7 及更早版本中存在的問題,可以對上面的函數加以改進,讓它只返回指定的特性。每個特
性節點都有一個名為specified 的屬性,這個屬性的值如果為true,則意味著要麼是在HTML 中指
定了相應特性,要麼是通過setAttribute方法設置了該特性。在IE 中,所有未設置過的特性的該
屬性值都為false,而在其他瀏覽器中根本不會為這類特性生成對應的特性節點(因此,在這些瀏覽器
中,任何特性節點的specified 值始終為true)。改進後的代碼如下所示。
function outputAttributes(element){ var pairs = new Array, attrName, attrValue, i, len; for (i=0, len=element.attributes.length; i < len; i++){ attrName = element.attributes[i].nodeName; attrValue
= element.attributes[i].nodeValue; if (element.attributes[i].specified) { pairs.push(attrName + "=\"" + attrValue + "\""); } } return pairs.join(" "); }
這個經過改進的函數可以確保即使在IE7 及更早的版本中,也會只返回指定的特性。
5.
創建元素
使用document.createElement方法可以創建新元素。這個方法只接受一個參數,即要創建元
素的標籤名。這個標籤名在HTML 文檔中不區分大小寫,而在XML(包括XHTML)文檔中,則是區
分大小寫的。例如,使用下面的代碼可以創建一個<div>元素。var div = document.createElement("div");
在使用createElement方法創建新元素的同時,也為新元素設置了ownerDocuemnt 屬性。此
時,還可以操作元素的特性,為它添加更多子節點,以及執行其他操作。來看下面的例子。div.id = "myNewDiv"; div.className = "box";在新元素上設置這些特性只是給它們賦予了相應的信息。由於新元素尚未被添加到文檔樹中,因此
設置這些特性不會影響瀏覽器的顯示。要把新元素添加到文檔樹,可以使用appendChild、inser-
tBefore或replaceChild方法。下面的代碼會把新創建的元素添加到文檔的<body>元素中。document.body.appendChild(div);一旦將元素添加到文檔樹中,瀏覽器就會立即呈現該元素。此後,對這個元素所作的任何修改都會
實時反映在瀏覽器中。
在IE 中可以以另一種方式使用createElement,即為這個方法傳入完整的元素標籤,也可以包
含屬性,如下面的例子所示。var div = document.createElement("<div id=\"myNewDiv\" class=\"box\"></div >");這種方式有助於避開在IE7 及更早版本中動態創建元素的某些問題。下面是已知的一些這類問題。
不能設置動態創建的<iframe>元素的name 特性。
不能通過表單的reset方法重設動態創建的<input>元素(第13 章將討論reset方法)。
動態創建的type 特性值為"reset"的<buttou>元素重設不了表單。
動態創建的一批name 相同的單選按鈕彼此毫無關係。name 值相同的一組單選按鈕本來應該用
於表示同一選項的不同值,但動態創建的一批這種單選按鈕之間卻沒有這種關係。
上述所有問題都可以通過在createElement中指定完整的HTML 標籤來解決,如下面的例子所示。
if (client.browser.ie && client.browser.ie <=7){ //創建一個帶name 特性的iframe 元素 var iframe = document.createElement("<iframe name=\"myframe\"></iframe>"); //創建input 元素 var input =
document.createElement("<input type=\"checkbox\">"); //創建button 元素 var button = document.createElement("<button type=\"reset\"></button>"); //創建單選按鈕 var radio1 =
document.createElement("<input type=\"radio\" name=\"choice\" "+ "value=\"1\">"); var radio2 = document.createElement("<input type=\"radio\" name=\"choice\" "+ "value=\"2\">");
}與使用createElement的慣常方式一樣,這樣的用法也會返回一個DOM 元素的引用。可以將
這個引用添加到文檔中,也可以對其加以增強。但是,由於這樣的用法要求使用瀏覽器檢測,因此我們
建議只在需要避開IE 及更早版本中上述某個問題的情況下使用。其他瀏覽器都不支持這種用法。
6.
元素的子節點
元素可以有任意數目的子節點和後代節點,因為元素可以是其他元素的子節點。元素的
childNodes 屬性中包含了它的所有子節點,這些子節點有可能是元素、文本節點、注釋或處理指令。
不同瀏覽器在看待這些節點方面存在顯著的不同,以下面的代碼為例。<ul id="myList"> <li>Item 1</li> <li>Item 2</li> <li>Item 3</li> </ul>如果是IE 來解析這些代碼,那麼<ul>元素會有3 個子節點,分別是3
個<li>元素。但如果是在其
他瀏覽器中,<ul>元素都會有7 個元素,包括3 個<li>元素和4 個文本節點(表示<li>元素之間的空
白符)。如果像下面這樣將元素間的空白符刪除,那麼所有瀏覽器都會返回相同數目的子節點。<ul id="myList"><li>Item 1</li><li>Item 2</li><li>Item 3</li></ul>對於這段代碼,<ul>元素在任何瀏覽器中都會包含3
個子節點。如果需要通過childNodes 屬性
遍歷子節點,那麼一定不要忘記瀏覽器間的這一差別。這意味著在執行某項操作以前,通常都要先檢查
一下nodeTpye 屬性,如下面的例子所示。
for (var i=0, len=element.childNodes.length; i < len; i++){ if (element.childNodes[i].nodeType == 1){ //執行某些操作 } }這個例子會循環遍歷特定元素的每一個子節點,然後只在子節點的nodeType 等於1(表示是元素
節點)的情況下,才會執行某些操作。
如果想通過某個特定的標籤名取得子節點或後代節點該怎麼辦呢?實際上,元素也支持
getElementsByTagName方法。在通過元素調用這個方法時,除了搜索起點是當前元素之外,其他
方面都跟通過document 調用這個方法相同,因此結果只會返回當前元素的後代。例如,要想取得前面
<ul>元素中包含的所有<li>元素,可以使用下列代碼。var ul = document.getElementById("myList"); var items = ul.getElementsByTagName("li");
要注意的是,這裡<ul>的後代中只包含直接子元素。不過,如果它包含更多層次的後代元素,那
麼各個層次中包含的<li>元素也都會返回。