本章內(nèi)容
- 理解包含不同層次節(jié)點的 DOM
- 使用不同的節(jié)點類型
- 克服瀏覽器兼容性問題及各種陷阱
DOM 是針對 HTML 和 XML 文檔的一個 API。DOM 描繪了一個層次化的節(jié)點樹,允許開發(fā)人員添加、移除和修改頁面的某一部分。本章主要討論與瀏覽器中的 HTML 頁面相關(guān)的 DOM1 級的特性和應(yīng)用,以及 JavaScript 對 DOM1 級的實現(xiàn)。
注意,IE 中的所有 DOM 對象都是以 COM 對象的形式實現(xiàn)的。這意味著 IE 中的 DOM 對象與原生 JavaScript 對象的行為活動特點并不一致。
10.1 節(jié)點層次
DOM 可以將任何 HTML 或 XML 文檔描繪成一個由多層節(jié)點構(gòu)成的結(jié)構(gòu)。
10.1.1 Node 類型
除了 IE 之外,在其他所有瀏覽器中都可以訪問到這個類型。 JavaScript 中的所有節(jié)點類型都繼承Node 類型,因此所有節(jié)點類型都共享著相同的基本屬性和方法。
每個節(jié)點都有一個 nodeType屬性,用于表明節(jié)點的類型。節(jié)點類型由在Node類型中定義的下列 12 個數(shù)值常量來表示。
- Node.ELEMENT_NODE(1);
- Node.ATTRIBUTE_NODE(2);
- Node.TEXT_NODE(3);
- Node.CDATA_SECTION_NODE(4);
- Node.ENTITY_REFERENCE_NODE(5);
- Node.ENTITY_NODE(6);
- Node.PROCESSING_INSTRUCTION_NODE(7);
- Node.COMMENT_NODE(8);
- Node.DOCUMENT_NODE(9);
- Node.DOCUMENT_TYPE_NODE(10);
- Node.DOCUMENT_FRAGMENT_NODE(11);
- Node.NOTATION_NODE(12)
通過比較上面這些常量,可以很容易地確定節(jié)點類型。如:
if (someNode.nodeType == Node.ELEMENT_NODE) {
alert("Node is an element");
}
由于 IE 沒有公開Node類型的構(gòu)造函數(shù),因此上面的代碼在 IE 中會導(dǎo)致錯誤。為確??鐬g覽器兼容,最好還是將nodeType屬性與數(shù)字值進(jìn)行比較:
if (someNode.nodeType == 1) {
alert("Node is an element");
}
并非所有節(jié)點類型都受到 Web 瀏覽器的支持。開發(fā)人員最常用的就是元素和文本節(jié)點。
- nodeName 和 nodeValue 屬性
在使用這倆屬性之前,最好先檢測一下節(jié)點的類型。
if (someNode.nodeType == 1) {
name = someNode.nodeName;
}
對于元素節(jié)點,nodeName中保存的始終都是元素的標(biāo)簽名,而nodeValue的值則始終未null。
- 節(jié)點關(guān)系
每個節(jié)點都有一個childNodes屬性,其中保存著一個NodeList對象。它是一種類數(shù)組對象,用于保存一組有序的節(jié)點,可以通過位置來訪問這些節(jié)點。它實際上是基于 DOM 結(jié)構(gòu)動態(tài)執(zhí)行查詢的結(jié)果,因此 DOM 結(jié)構(gòu)的變化能夠自動反映在NodeList對象中。
var firstChild = someNode.childNodes[0];
var secondChild = someNode.childNodes.item(1);
var count = someNode.childNodes.length;
其中,length屬性表示的是訪問NodeList的那一刻,其中包含的節(jié)點數(shù)量。對arguments對象使用Array.prototype.slice()方法可以將其轉(zhuǎn)換為數(shù)組。也可以將NodeList對象轉(zhuǎn)換為數(shù)組。
//在 IE8 及之前版本中無效
var arrayOfNodes = Array.prototype.slice.call(someNode.childNodes,0);
要想在 IE 中將NodeList轉(zhuǎn)換為數(shù)組,必須手動枚舉所有成員。
function convertToArray(nodes) {
var array = null;
try {
array = Array.prototype.slice.call(nodes, 0);
} catch(ex) {
array = new Array();
for (var i=0,len=nodes.length;i<len;i++) {
array.push(nodes[i]);
}
}
}
每個節(jié)點都有一個parentNode屬性,該屬性指向文檔樹中的樹節(jié)點。包含在childNodes列表中的所有節(jié)點都具有相同的父節(jié)點,因此它們的parentNode屬性都指向同一個節(jié)點。此外,包含在childNodes列表中的每個節(jié)點相互之間都是同胞節(jié)點。可以使用previousSibling和nextSibling屬性,可以訪問同一列表中的其他節(jié)點。
if (someNode.nextSibling === null){
alert("Last node in the parent's childNodes list");
} else if (someNode.previousSibling === null) {
alert("First node in the parent childNodes list.");
}
父節(jié)點的firstChild和lastChild屬性分別子節(jié)點列表中的第一個和最后一個節(jié)點。
另外,hasChildNodes()也是一個非常有用的方法,這個方法在節(jié)點包含一或多個子節(jié)點的情況下返回true。
所有節(jié)點都有的最后一個屬性是ownerDocument,該屬性指向表示整個文檔的文檔節(jié)點。這種關(guān)系表示的任何節(jié)點都屬于它所在的文檔,任何節(jié)點都不能同時存在于兩個或更多個文檔中。
雖然所有節(jié)點類型都繼承自
Node,但并不是每種節(jié)點都有子節(jié)點。
- 操作節(jié)點
其中,最常用的方法是appendChild(),用于向childNodes列表的末尾添加一個節(jié)點。更新完成后,appendChild()返回新增的節(jié)點。
var returnedNode = someNode.appendChild(newNode);
alert(returnedNode == newNode); //true
alert(someNode.lastChild == newNode); //true
如果傳入到appendChild()中的節(jié)點已經(jīng)是文檔中一部分了,那結(jié)果就是將該節(jié)點從原來的位置轉(zhuǎn)移到新位置。
如果需要把節(jié)點放在列表中的某個位置,而不是放在末尾,可以使用insertBefore()方法。這個方法接受兩個參數(shù):要插入的節(jié)點和作為參照的節(jié)點。如果參照節(jié)點是null,則放在末尾。
//插入后稱為最后一個字節(jié)點
returnedNode = someNode.insertBefore(newNode, null);
alert(newNode == someNode.lastChild); //true
//插入后成為第一個子節(jié)點
var returnedNode = someNode.insertBefore(newNode, someNode.firstChild);
alert(returnedNode == newNode); //true
alert(newNode == someNode.firstChild); //true
replaceChild()方法接受倆參數(shù):要插入的節(jié)點和要替換的節(jié)點。要替換的節(jié)點將由這個方法返回并從文檔樹中被移除,同時由要插入的節(jié)點占據(jù)其位置。
//替換第一個子節(jié)點
var returnedNode = someNode.replaceChild(newNode,someNode.firstChild);
//替換最后一個節(jié)點
returnedNode = someNode.replaceChild(newNode, someNode.lastChild);
如果只想移除而非替換節(jié)點,可以使用removeChild()方法。這個方法接受一個參數(shù),即要移除的節(jié)點。
//移除第一個子節(jié)點
var formerFirstChild = someNode.removeChild(someNode.firstChild);
//移除最后一個子節(jié)點
var formerLastChild = someNode.removeChild(someNode.lastChild);
與使用replaceChild()方法一樣,通過removeChild()移除的節(jié)點仍然為文檔所有,只不過在文檔中已經(jīng)沒有了自己的位置。
要使用這幾個方法必須先取得父節(jié)點(使用parentNode屬性)。另,并不是所有類型的節(jié)點都有子節(jié)點。
- 其他方法
有兩個方法是所有類型的節(jié)點都有的。第一個就是cloneNode(),用于創(chuàng)建調(diào)用這個方法的節(jié)點的一個完全相同的副本。cloneNode()方法接受一個布爾值參數(shù),表示是否執(zhí)行深復(fù)制。參數(shù)為true的情況下,執(zhí)行深復(fù)制,也就是復(fù)制節(jié)點及其整個子節(jié)點樹;參數(shù)為false的情況下,執(zhí)行淺復(fù)制,即只復(fù)制節(jié)點本身。復(fù)制后返回的節(jié)點副本屬于文檔所有,但并沒有為它指定父節(jié)點。因此,這個節(jié)點副本就成為了一個“孤兒”,除非通過appendChild()、insertBefore()或replaceChild()將它添加到文檔中。
cloneNode()方法不會復(fù)制添加到 DOM 節(jié)點中的 JavaScript 屬性,例如事件處理程序等。IE 在此存在一個 bug,即它會復(fù)制事件處理程序,所以我們建議在復(fù)制之前最好先移除事件處理程序。
介紹的最后一個方法是normalize(),這個方法唯一的作用就是處理文檔樹中的文本節(jié)點。當(dāng)在某個節(jié)點上調(diào)用這個方法時,如果找到了空文本節(jié)點,則刪除它;如果找到相鄰的文本節(jié)點,則將它們合并為一個文本節(jié)點。
10.1.2 Document 類型
JavaScript 通過Document類型表示文檔。document對象是HTMLDocument(繼承自 Document 類型)的一個實例,表示整個 HTML 頁面。而且,document對象是window對象的一個屬性,因此可以將其作為全局對象來訪問。Document節(jié)點具有下列特征:
-
nodeType的值為 9; -
nodeName的值為“#document”; -
nodeValue的值為null; -
parentNode的值為null; -
ownerDocument的值為null; - 其子節(jié)點可能是一個
DocumentType(最多一個)、Element(最多一個)、ProcessingInstruction或Comment。
Document類型可以表示 HTML 頁面或者其他基于 XML 的文檔。不過,最常見的還是作為HTMLDocument實例的document對象。通過這個文檔對象,不僅可以取得與頁面有關(guān)的信息,而且還能操作頁面的外觀及其底層結(jié)構(gòu)。
- 文檔的子節(jié)點
有兩個內(nèi)置的訪問其子節(jié)點的快捷方式。第一個就是documentElement屬性,該屬性始終指向 HTML 頁面中的<html>元素。另一個就是通過childNodes列表訪問文檔元素,但通過documentElement屬性則能更快捷、更直接地訪問該元素。如下所示:
var html = document.documentElement;
alert(html === document.childNodes[0]); //true(false ?)
alert(html === document.firstChild); //true (false ?)
作為HTMLDocument的實例,document對象還有一個body屬性,直接指向<body>元素。開發(fā)人員經(jīng)常要使用這個元素。
var body = document.body; //取得引用
所有瀏覽器都支持document.documentElement和document.body屬性。
另一個可能的子節(jié)點是DocumentType。通常將<!DOCTYPE>標(biāo)簽看成一個與文檔其他部分不同的實體,可以通過doctype屬性來訪問它的信息。
var doctype = document.doctype;
由于瀏覽器對document.doctype的支持不一致,因此這個屬性的用處很有限。
從技術(shù)上說,出現(xiàn)在<html>元素外的注釋應(yīng)該算是文檔的子節(jié)點。然而,不同的瀏覽器在是否解析這些注釋以及能否正確處理它們等方面,也存在很大差異。
- 文檔信息
作為HTMLDocument的一個實例,document對象還有一些標(biāo)準(zhǔn)的Document對象所沒有的屬性。這些屬性提供一些網(wǎng)頁的一些信息。
//取得文檔標(biāo)題
var originalTitle = document.title;
//設(shè)置文檔標(biāo)題
document.title = "New page title";
//取得完整的 URL
var url = document.URL;
//取得域名
var domain = document.domain;
//取得來源頁面的 URL
var referrer = document.referrer;
只有domain是可以設(shè)置的。如果 URL 中包含一個子域名,例如 p2p.wrox.com,那么就只能將domain設(shè)置為"wrox.com"(URL 中包含“www”,如 www.wrox.com 時,也是如此)。不能將這個屬性設(shè)置為 URL 中不包含的域。
當(dāng)頁面中包含來自其他子域的框架或內(nèi)嵌框架時,能夠設(shè)置document.domain就非常方便了。由于跨域安全限制,來自不同子域的頁面無法通過 JavaScript 通信。而通過將每個頁面的document.domain設(shè)置為相同的值,這些頁面就可以互相訪問對方包含的JavaScript對象了。
- 查找元素
第一個方法,getElementById,通過 ID 獲取該元素。
另一個常用于取得元素引用的方法是getElementsByTagName,通過標(biāo)簽名取得元素,返回的是包含零或多個元素的NodeList。在 HTML 文檔中,這個方法會返回一個 HTMLCollection 對象。
var images = document.getElementByTagName('img');
alert(images.length);
alert(images[0].src);
alert(images.item(0).src);
HTMLCollection對象還有一個方法,namedItem(),使用這個方法可以通過元素的name特性取得集合中的項。
var myImage = images.namedItem("myImage");
還支持按名稱訪問項。
var myImage = images["myImage"];
對 HTMLCollection而言,我們可以向方括號中傳入數(shù)值或字符串形式的索引值。在后臺,對數(shù)值索引就會調(diào)用item(),而對字符串索引就會調(diào)用namedItem()。
要想取得文檔中的所有元素,可以向getElementsByTagName()中傳入"*"。在 JavaScript 及 CSS 中,星號(*)通常表示“全部”。
var allElements = document.getElementsByTagName("*");
第三個方法,也是只有HTMLDocument類型才有的方法,是getElementsByName()。
- 特殊集合
還有一些特殊的集合,這些集合都是HTMLCollection對象,為訪問文檔常用的部分提供了快捷方式。 - DOM 一致性檢測
由于 DOM 分為多個級別,也包含多個部分,因此檢測瀏覽器實現(xiàn)了 DOM 的哪些部分就十分必要。DOM1 級只為document.implementation規(guī)定了一個方法,即hasFeatrue()。
var hasXmlDom = document.implementation.hasFeature("XML", "3.0");
建議在多數(shù)情況下,在使用 DOM 的某些特殊功能之前,最好除了檢測hasFeature()之外,還同時使用能力檢測。
- 文檔寫入
這個能力體現(xiàn)在下列 4 個方法中:write()、writeln()、open()、close()。writeln()會在字符串的末尾添加一個換行符(\n)。
方法open和close()分別用于打開和關(guān)閉網(wǎng)頁的輸出流。
嚴(yán)格型 XHTML 文檔不支持文檔寫入。對于那些按照 application/xml + xhtml 內(nèi)容類型提供的頁面,這兩個方法也同樣無效。
10.1.3 Element 類型
Element類型用于表現(xiàn) XML 或 HTML 元素。提供了對元素標(biāo)簽名、子節(jié)點及特性的訪問。Element節(jié)點具有以下特征:
-
nodeType的值為 1; -
nodeName的值為元素的標(biāo)簽名; -
nodeValue的值為null; -
parentNode可能是Document或Element; - 其子節(jié)點可能是
Element、Text、Comment、ProcessingInstruction、CDATASection或EntityReference。
要訪問元素的標(biāo)簽名,可以使用nodeName屬性,也可以使用tagName屬性。
var div = document.getElementById('myDiv');
alert(div.tagName); //"DIV"
alert(div.tagName == div.nodeName); //true
在 HTML 中,標(biāo)簽名始終都以全部大寫表示;而在 XML中,標(biāo)簽名則始終會與源代碼中的保持一致。最好是在比較之前將標(biāo)簽名轉(zhuǎn)換為相同的大小寫形式。
if (element.tagName.toLowerCase() == "div") {}
- HTML 元素
所有 HTML 元素都由HTMLElement類型表示,不是直接通過這個類型,也是通過它的子類型來表示。HTMLElement類型直接繼承自Element并添加了一些屬性。添加的這些屬性分別對應(yīng)于每個 HTML 元素中都存在的下列標(biāo)準(zhǔn)特性。
-
id,元素在文檔中的唯一標(biāo)識符。 -
title,有關(guān)元素的附加說明信息,一般通過工具提示條顯示出來。 -
lang,元素內(nèi)容的語言代碼,很少使用。 -
dir,語言的方向,很少使用。 -
className,與元素的class特性對應(yīng),即為元素指定的 CSS 類。class是 ECMAScript 的保留字。
上述屬性都可以用來取得或修改相應(yīng)的特性值。
var div = document.getElementById('myDiv');
alert(div.id);
alert(div.className);
alert(div.title);
alert(div.lang);
alert(div.dir);
div.id = 'someOtherId';
div.className = 'ft';
div.title = 'Some Other text';
div.lang = 'fr';
div.dir = 'rtl';
- 取得特性
每個元素都有一或多個特性,這些特性的用途是給出相應(yīng)元素或其內(nèi)容的附加信息。操作特性的 DOM 方法主要有三個,分別是getAttribute()、setArribute和removeAttribute()。
var div = document.getElementById('myDiv');
alert(div.getAttribute('id'));
alert(div.getAttribute('class'));
alert(div.getAttribute('title'));
alert(div.getAttribute('lang'));
alert(dib.getAttribute('dir'));
注意,傳遞給getAttribute()的特性名與實際的特性名相同。因此要想得到class特性值,應(yīng)該傳入class而不是className,后者只有在通過對象屬性訪問特性時采用。
通過getAttribute()方法也可以取得自定義特性(即標(biāo)準(zhǔn) HTML 語言中沒有的特性)的值。不過,特性的名稱是不區(qū)分大小寫的。另外,根據(jù) HTML5 規(guī)范,自定義特性應(yīng)該加上data-前綴以便驗證。
只有公認(rèn)的(非自定義)特性才會以屬性的形式添加到 DOM 對象中。
有兩類特殊的特性,它們雖然有對應(yīng)的屬性名,但屬性的值與通過getAttribute()返回的值并不相同。第一類特性就是style,用于通過 CSS 為元素指定樣式。使用getAttribute()返回的是 CSS 文本,而通過屬性訪問則會返回一個對象。
第二類是onclick這樣的事件處理程序。通過getAttribute()訪問時返回的是相應(yīng)代碼的字符串。而在訪問onclick屬性時,則會返回一個 JavaScript 函數(shù)。
由于存在這些差別。經(jīng)常不使用getAttribute(),而是只使用對象的屬性。只有在取得自定義特性值的情況下,才會使用getAttribute()方法。
- 設(shè)置特性
setAttribute()該方法接受兩個參數(shù):要設(shè)置的特性名和值。如果特性已經(jīng)存在,setAttribute()會以指定的值替換現(xiàn)有的值;如果特性不存在,setAttribute()則創(chuàng)建該屬性并設(shè)置相應(yīng)的值。
div.setAttribute('id', 'someOtherId');
div.setAttribute('title', 'Some other text');
div.setAttribute('lang','fr');
直接給屬性賦值也可以設(shè)置特性的值。
div.id = "someOtherId";
不過,像下面這樣為 DOM 元素添加一個自定義的屬性,該屬性不會自動成為元素的特性。
div.mycolor = "red";
alert(div.getAttribute('mycolor')); //null
要介紹的最后一個方法是removeAttribute(),這個方法用于徹底刪除元素的特性。調(diào)用這個方法不僅會清除特性的值,而且也會從元素中完全刪除特性。
- attributes 屬性
Element類型是使用attributes屬性的唯一一個 DOM 節(jié)點類型。一般不使用。可以用來遍歷節(jié)點的特性。 - 創(chuàng)建元素
使用document.createElement()方法可以創(chuàng)建新元素。這個方法只接受一個參數(shù),即標(biāo)簽名。
var div = document.createElement('div');
div.id = "myNewDiv";
div.className = "box";
document.body.appenChild(div);
- 元素的子節(jié)點
如果需要通過childNodes屬性遍歷子節(jié)點,需要檢查一下nodeType屬性。
for (var i=0, len = element.childNodes.length; i++) {
if (element.childNodes[i].nodeType == 1) {
//執(zhí)行某些操作
}
}
10.1.4 Text 類型
文本節(jié)點由Text類型表示,包含的是可以照字面解釋的純文本內(nèi)容。具有以下特征:
-
nodeType的值為3; -
nodeName的值為“ #text ”; -
nodeValue的值為節(jié)點所包含的文本; -
parentNode是一個Element; - 不支持(沒有)子節(jié)點。
使用下列方法可以操作節(jié)點中的文本。
-
appendData(text):將text添加到節(jié)點的末尾。 -
deleteData(offset, count):從offset指定的位置開始刪除count個字符。 -
insertData(offset, text):在offset指定的位置插入text。 -
replaceData(offset, count, text):用text替換從offset指定的位置開始到offset+count為止處的文本。 -
splitText(offset):從offset指定的位置將當(dāng)前文本節(jié)點分成兩個文本節(jié)點。 -
subStringData(offset, count):提取從offset指定的位置開始到offset+count為止的字符串。
文本節(jié)點和還有一個length屬性,保存著節(jié)點中字符的數(shù)目。
- 創(chuàng)建文本節(jié)點
可以使用document.createTextNode()創(chuàng)建文本節(jié)點,這個方法接受一個參數(shù)--要插入節(jié)點中的文本。
var textNode = document.createTextNode("<strong>hello</strong>world!");
var element = document.createElement('div');
element.className = "message";
element.appendChild(textNode);
document.body.appendChild(element);
- 規(guī)范化文本節(jié)點
DOM 文檔中存在相鄰的同胞文本節(jié)點很容易導(dǎo)致混亂,因為分不清哪個文本節(jié)點表示哪個字符串。在一個包含兩個或多個文本節(jié)點的父元素上調(diào)用normalize()方法,則會將所有文本節(jié)點合并成一個節(jié)點,結(jié)果節(jié)點的nodeValue等于將合并前每個文本節(jié)點的nodeValue值拼接起來的值。 - 分割文本節(jié)點
splitText()方法會將一個文本節(jié)點分成兩個文本節(jié)點,即按照指定位置分割nodeValue值。原來的文本節(jié)點將包含從開始到指定位置之前的內(nèi)容,新文本節(jié)點將包含剩下的文本。該方法會返回一個新文本節(jié)點,該節(jié)點與原節(jié)點的parentNode相同。
var element = document.createElement('div');
element.className = 'message';
var textNode = document.createTextNode('hello world!');
element.appendChild(textNode);
document.body.appendChild(element);
var newNode = element.firstChild.splitText(5);
alert(element.firstChild.nodeValue); //"hello"
alert(newNode.nodeValue); //"world"
alert(element.childNodes.length); //2
10.1.5 Comment 類型
注釋在 DOM 中是通過Comment類型來表示的。Comment節(jié)點具有下列特征:
-
nodeType的值為 8; -
nodeName的值為“ #comment”; -
nodeValue的值是注釋的內(nèi)容; -
parentNode可能是Document或Element; - 不支持(沒有)子節(jié)點。
使用document.createComment()并為其傳遞注釋文本也可以創(chuàng)建注釋節(jié)點。
顯然,開發(fā)人員很少會創(chuàng)建和訪問注釋節(jié)點。
10.1.6 CDATASection 類型
CDATASection類型只針對基于 XML 的文檔,表示的是 CDATA 區(qū)域。與Comment類似,CDATASection類型繼承自Text類型,因此擁有除splitText()之外的所有字符串操作方法。
在真正的 XML 文檔中,可以使用document.createCDataSection()來創(chuàng)建 CDATA 區(qū)域,只需為其傳入節(jié)點的內(nèi)容即可。
10.1.7 DocumentType 類型
該類型并不常用。
10.1.8 DocumentFragment 類型
在所有節(jié)點類型中,只有DocumentFragment在文檔中沒有對應(yīng)的標(biāo)記??梢詫⑺鳛橐粋€“倉庫”使用。
如果逐個地添加表項,將會導(dǎo)致瀏覽器反復(fù)渲染(呈現(xiàn))新信息??梢韵裣旅孢@樣使用一個文檔片段來保存創(chuàng)建的列表項,然后再一次性將它們添加到文檔中。
var fragment = document.createDocumentFragment();
var ul = document.getElementById('myList');
var li = null;
for (var i=0; i < 3; i++) {
li = document.createElement('li');
li.appendChild(document.createTextNode('item'+(i+1)));
fragment.appendChild(li);
}
ul.appendChild(fragment);
10.1.9 Attr 類型
元素的特性在 DOM 中以Attr類型來表示。從技術(shù)角度講,特性就是存在于元素的attributes屬性中的節(jié)點。
盡管它們也是節(jié)點,但特性卻不認(rèn)為是 DOM 文檔樹的一部分。最常使用的是getAttribute()、setAttribute()和removeAttribute()方法,很少直接引用特性節(jié)點。
10.2 DOM 操作技術(shù)
10.2.1 動態(tài)腳本
創(chuàng)建動態(tài)腳本也有兩種方式:插入外部文件和直接插入 JavaScript 代碼。
var script = document.createElement('script');
script.type = "text/javascript";
script.src = "client.js";
document.body.appendChild(script);
整個過程可以使用下面的函數(shù)來封裝:
function loadScript(url) {
var script = document.createElement("script");
script.type = 'text/javascript';
script.src = url;
document.body.appendChild(script);
}
function loadScriptString(code) {
var script = document.createElement('script');
script.type = 'text/javascript';
try {
script.appendChild(document.createTextNode(code));
} catch (ex) {
script.text = code;
}
document.body.appendChild(script);
}
loadScriptString('function sayHi(){alert('hi');}');
10.2.2 動態(tài)樣式
動態(tài)樣式是在頁面加載完成后動態(tài)添加到頁面中的。
function loadStyles(url) {
var link = document.createElement('link');
link.rel = 'stylesheet';
link.type = 'text/css';
link.href = url;
var head = document.getElementsByTagName('head')[0];
head.appendChild(link);
}
function loadStylesString(css) {
var style = document.createElement('style');
style.type = 'text/css';
try {
style.appendChild(document.createTextNode(css));
} catch (ex) {
style.styleSheet.cssText = css;
}
var head = document.getElementsByTagName('head')[0];
head.appendChild(style);
}
loadStyleString('body{background-color:red}');
10.2.3 操作表格
為了方便構(gòu)建表格,HTML DOM 還為<table>、<tbody>和<tr>元素添加了一些屬性和方法。
10.2.4 使用 NodeList
理解NodeList及NamedNodeMap和HTMLCollection,是從整體上透徹理解 DOM 的關(guān)鍵所在。這三個集合都是“動態(tài)的”,每當(dāng)文檔結(jié)構(gòu)發(fā)生變化時,它們都會得到更新。
10.3 小結(jié)
DOM 是語言中立的 API,用于訪問和操作 HTML 和 XML 文檔。 DOM 1 級將 HTML 和 XML 文檔形象地看作一個層次化的節(jié)點樹,可以使用 JavaScript 來操作這個節(jié)點樹,進(jìn)而改變底層文檔的外觀和結(jié)構(gòu)。
DOM 由各種節(jié)點構(gòu)成,簡要總結(jié)如下。
- 最基本的節(jié)點類型是
Node,用于抽象地表示文檔中一個獨立的部分;所有其他類型都繼承自Node。 -
Document類型表示整個文檔,是一組分層節(jié)點的根節(jié)點。在 JavaScript 中,document對象是Document的一個實例。使用document對象,有很多種方式可以查詢和取得節(jié)點。 -
Element節(jié)點表示文檔中的所有 HTML 或 XML 元素,可以用來操作這些元素的內(nèi)容和特性。 - 另外還有一些節(jié)點類型,分別表示文本內(nèi)容、注釋、文檔類型、CDATA 區(qū)域和文檔片段。
DOM 操作往往是 JavaScript 程序中開銷最大的部分,而因訪問NodeList導(dǎo)致的問題為最多。NodeList對象都是“動態(tài)的”,這就意味著每次訪問NodeList對象,都會運行一次查詢。所以,最好的方法就是盡量減少 DOM 操作。