一個(gè)完整的JavaScript實(shí)現(xiàn)應(yīng)該由下列三個(gè)不同的部分組成:
(1)核心(ECMAScript)
(2)文檔對(duì)象模型(DOM)
(3)瀏覽器對(duì)象模型(BOM)
(一)DOM(Document Object Model)
1.DOM概念
(1)文本對(duì)象模型,是針對(duì)XML,HTML的應(yīng)用程序編程接口。
(2)DOM把整個(gè)頁面映射成一個(gè)多層節(jié)點(diǎn)結(jié)構(gòu)。也就是樹狀結(jié)構(gòu)。
(3)借助DOM提供的接口,可以輕松的刪除,添加,替換和修改任何節(jié)點(diǎn)。
(4)在 DOM中 , 每一個(gè)元素都是節(jié)點(diǎn)。
整個(gè)文檔是一個(gè)文檔節(jié)點(diǎn)。
每個(gè) HTML 標(biāo)簽是一個(gè)元素節(jié)點(diǎn)
包含在 HTML 元素中的文本是文本節(jié)點(diǎn)
每一個(gè) HTML 屬性是一個(gè)屬性節(jié)點(diǎn)
注釋屬于注釋節(jié)點(diǎn)
JavaScript中的所有節(jié)點(diǎn)類型都繼承自Node類型,因此所有節(jié)點(diǎn)類型都共享著相同的基本屬性和方法。
每個(gè)節(jié)點(diǎn)都有一個(gè)nodeType屬性,用于表明節(jié)點(diǎn)的類型。
常見節(jié)點(diǎn)類型:

nodeName和nodeValue屬性
要了解節(jié)點(diǎn)的具體信息,可以使用這兩個(gè)屬性,不過這兩個(gè)屬性的值取決于節(jié)點(diǎn)的類型

關(guān)系節(jié)點(diǎn)
- childNodes
每個(gè)節(jié)點(diǎn)都有一個(gè)childNodes屬性,保存著一個(gè)NodeList對(duì)象,是一種類數(shù)組對(duì)象,保存著一組子節(jié)點(diǎn),并且length屬性,特點(diǎn)是可以基于DOM結(jié)構(gòu)動(dòng)態(tài)執(zhí)行查詢的結(jié)果,因此DOM結(jié)構(gòu)的變化能夠自動(dòng)反映在NodeList對(duì)象中
注意:節(jié)點(diǎn)的childNodes包含了節(jié)點(diǎn)內(nèi)的所有節(jié)點(diǎn)類型(不包括屬性節(jié)點(diǎn)),而且標(biāo)簽內(nèi)的換行和空格都會(huì)被當(dāng)成一個(gè)文本包含在childNodes屬性中
- parentNode
每個(gè)節(jié)點(diǎn)都有一個(gè)parentNode屬性,該屬性指向文檔樹種的父節(jié)點(diǎn),因此,包含在childNodes列表中的所有節(jié)點(diǎn)都有相同的父節(jié)點(diǎn),他們的parentNode屬性都指向同一個(gè)節(jié)點(diǎn)
- previousSibling 和nextSibling屬性
childNodes列表中的每個(gè)元素節(jié)點(diǎn)相互之間都是同胞節(jié)點(diǎn),可以通過每個(gè)節(jié)點(diǎn)的previousSibling和nextSibling屬性訪問同一列表的其他節(jié)點(diǎn)
- firstChild 和lastChild屬性
在父節(jié)點(diǎn)下使用這兩個(gè)屬性可以分別獲取childNodes列表下的第一個(gè)和最后一個(gè)節(jié)點(diǎn).為部分需求操作提供的極大的便利
- hasChildNodes()
這個(gè)方法非常簡單實(shí)用,在節(jié)點(diǎn)包含一或多個(gè)子節(jié)點(diǎn)的情況下返回true,而無需查詢childNodes的length大小來判斷
- ownerDocument
所有節(jié)點(diǎn)都有的最后一個(gè)屬性是ownerDocument,該屬性指向整個(gè)文檔的文檔節(jié)點(diǎn),不用層層回溯到頂端
舉例:
<!DOCTYPE html>
<html>
<body>
<p id="demo">請(qǐng)點(diǎn)擊按鈕來獲得 <p> 元素的 ownerDocument 的節(jié)點(diǎn)類型。</p>
<button onclick="myFunction()">試一下</button>
<script>
function myFunction()
{
var x=document.getElementById("demo");
x.innerHTML=x.ownerDocument.nodeType;//9
}
</script>
</body>
</html>
注意:firstChild,lastChild,nextSibling,previousSibling都會(huì)將空格或者換行當(dāng)做節(jié)點(diǎn)處理,(除IE)
所以為了準(zhǔn)確地找到相應(yīng)的元素(其他瀏覽器)
firstElementChild,
lastElementChild,
nextElementSibling,
previousElementSibling
瀏覽器兼容:var test = div1.nextElementSibling || div1.nextSibling;
操作節(jié)點(diǎn)
appendChild(新增的節(jié)點(diǎn))
接收一個(gè)新增節(jié)點(diǎn)作為參數(shù) ,用于向childNodes列表的末尾添加一個(gè)節(jié)點(diǎn),并返回新增的節(jié)點(diǎn)insertBefore(要插入的節(jié)點(diǎn),作為參照的節(jié)點(diǎn))
插入節(jié)點(diǎn)后,被插入的節(jié)點(diǎn)會(huì)變成參照節(jié)點(diǎn)的前一個(gè)同胞節(jié)點(diǎn),同時(shí)被方法返回,如果參照節(jié)點(diǎn)是null,那么insertBefore()和appendChild()是等同的replaceChild(要插入的節(jié)點(diǎn),要替換的節(jié)點(diǎn))
要替換的節(jié)點(diǎn)將由這個(gè)方法返回并從文檔樹種移除,同時(shí)由要插入的節(jié)點(diǎn)占據(jù)其位置-
removeChild(要?jiǎng)h除的子節(jié)點(diǎn))
這個(gè)方法接收一個(gè)參數(shù),即要移除的節(jié)點(diǎn),并且被方法返回前面介紹的四個(gè)操作方法操作的都是某個(gè)節(jié)點(diǎn)的子節(jié)點(diǎn),要使用必須先取得父節(jié)點(diǎn)
其他方法(所有類型節(jié)點(diǎn)都有)
cloneNode(布爾值)
用于創(chuàng)建調(diào)用這個(gè)方法的節(jié)點(diǎn)的一個(gè)完全相同的副本,插入布爾值為true則表示深度復(fù)制,復(fù)制節(jié)點(diǎn)和其整個(gè)節(jié)點(diǎn)樹,否則為淺復(fù)制 ,只復(fù)制節(jié)點(diǎn)本身,復(fù)制后返回的節(jié)點(diǎn)文本屬于文檔所有,但成為一個(gè)"孤兒",需要添加到文檔樹中
注意:cloneNode()方法不會(huì)復(fù)制添加到DOM節(jié)點(diǎn)中的Javascript屬性或者事件處理程序等normalize()
處理文檔樹中的文本節(jié)點(diǎn),如果在節(jié)點(diǎn)的后代節(jié)點(diǎn)中找到空文本節(jié)點(diǎn),則刪除它,如果找到相鄰的文本節(jié)點(diǎn),則將它們合并為一個(gè)文本節(jié)點(diǎn)
<!DOCTYPE html>
<html>
<body>
<p id="demo">點(diǎn)擊按鈕添加文本,點(diǎn)擊另一個(gè)按鈕來正規(guī)化元素。</p>
<button onclick="addTextNode()">添加文本節(jié)點(diǎn)</button>
<button onclick="normPara()">對(duì)段落進(jìn)行正規(guī)化</button>
<script>
function addTextNode()
{
var y=document.createTextNode("請(qǐng)?jiān)俅吸c(diǎn)擊。");
var x=document.getElementById("demo");
x.appendChild(y);
var z=document.getElementById("cc");
z.innerHTML=x.childNodes.length;
}
function normPara()
{
var x=document.getElementById("demo");
x.normalize();
var z=document.getElementById("cc");
z.innerHTML=x.childNodes.length;
}
</script>
<p>上面的段落有 <span id="cc">1</span> 個(gè)子節(jié)點(diǎn)。</p>
</body>
</html>

HTML的DOM樹節(jié)點(diǎn)包括:
元素節(jié)點(diǎn):上圖中<html>、<body>、<p>都是元素節(jié)點(diǎn)即標(biāo)簽
文本節(jié)點(diǎn):向用戶展示的內(nèi)容,如<li>...</li>中的javascript、Dom、CSS等文本。
屬性節(jié)點(diǎn):元素屬性,如<a>標(biāo)簽的鏈接屬性
Document對(duì)象:
當(dāng)瀏覽器載入 HTML 文檔, 它就會(huì)成為 document 對(duì)象。
Document 對(duì)象是HTML文檔的根節(jié)點(diǎn)與所有其他節(jié)點(diǎn)(元素節(jié)點(diǎn),文本節(jié)點(diǎn),屬性節(jié)點(diǎn), 注釋節(jié)點(diǎn))。
Document 對(duì)象使我們可以從腳本中對(duì) HTML 頁面中的所有元素進(jìn)行訪問。
總結(jié):
(1)Document 對(duì)象是 Window 對(duì)象的屬性,可通過 window.document 屬性對(duì)其進(jìn)行訪問。
(2)Javascript是通過Document類型表示文檔,在瀏覽器中document對(duì)象是HTMLDocument的一個(gè)實(shí)例,表示整個(gè)頁面,而且document對(duì)象也是window對(duì)象的一個(gè)屬性,因此可以作為全局對(duì)象來訪問
從document節(jié)點(diǎn)中取得頁面標(biāo)簽
- document.documentElement指向HTML頁面中的<html>元素
- document.body指向<body>元素
- document.doctype可以取得對(duì)<!DOCTYPE>的引用
- document.title 包含<title>元素中的文本
從document節(jié)點(diǎn)中取得頁面的請(qǐng)求信息
- document.URL為地址欄中顯示的URL
- document.domain為頁面的域名
-document.referrer為連接到當(dāng)前頁面的那個(gè)頁面的URL
從document節(jié)點(diǎn)中查找元素
- document.getElementById("mydiv")
- document.getElementsByTagName("img"),返回一個(gè)HTMLCollection對(duì)象,和NodeList對(duì)象類似,并且擁有一個(gè)方法namedItem()可以傳入元素的name特征來取得集合中的項(xiàng),可以通過document.getElementsByTagName("*")獲取全部元素
- document.getElementsByClassName("class"):獲取類名相同的元素,Internet Explorer 8 及更早 IE 版本不支持 getElementsByClassName() 方法
- document.getElementsByName():這個(gè)方法會(huì)返回有給定name特征的所有元素,通常用來獲取單選按鈕.
- document.anchors:包含文檔中所有帶name特性的a元素
- document.forms:包含文檔中所有的form元素
- document.images:包含文檔中所有的img元素
- document.links:包含文檔中所有帶有href特性的a元素
文檔寫入
- document.write(寫入字符串):將輸出流寫入到網(wǎng)頁
- document.writeln(寫入字符串):將輸出流寫入到網(wǎng)頁并換行
注意:必要時(shí)候要加轉(zhuǎn)義字符\ - document.open()和document.close()分別用于打開和關(guān)閉網(wǎng)頁的輸出流,如果是在頁面加載期間使用write()或writeln()方法,則不需要用到這兩個(gè)方法,調(diào)用write或writeln方法時(shí)和結(jié)束時(shí),瀏覽器會(huì)隱式調(diào)用這兩個(gè)方法
Element類型
判斷元素標(biāo)簽的最佳實(shí)踐:
if(element.tagName.toLowerCase() == "div"){ //在此執(zhí)行某些操作 }
HTML元素
每個(gè)HTML元素都有下列標(biāo)準(zhǔn)特性:
獲取和修改特性
getAttribute(特性名稱attributename)
參數(shù):需要獲得的屬性值的屬性名稱;
不僅可以取得節(jié)點(diǎn)的默認(rèn)特性也可以取得自定義特性(根據(jù)HTML5規(guī)范,自定義屬性應(yīng)該加上data-前綴以便驗(yàn)證),返回對(duì)應(yīng)特性值
注意:傳入style特性后返回的是包含的CSS文本,而通過屬性來訪問style則返回一個(gè)對(duì)象,因?yàn)橐幌盗械膯栴}原因,平時(shí)開發(fā)只使用對(duì)象的屬性來獲取特性,而只有在取得自定義特性值的情況下,才會(huì)使用getAttribute()方法setAttribute(特性名稱,特性值)
如果特性存在,則用特性值進(jìn)行修改,若不存在,則新建特性并賦予特性值,該方法不僅可以操作HTML特性也可以操作自定義特性,而使用對(duì)象屬性設(shè)置的自定義特性則不會(huì)自動(dòng)成為元素的特性removeAttribute(要?jiǎng)h除的特性名稱)
這個(gè)方法用于徹底刪除元素的特性,不僅清除特性的值也會(huì)從元素中完全刪除特性
attributes屬性
Element類型是使用attributes屬性的唯一一個(gè)DOM節(jié)點(diǎn)類型,elment.attributes實(shí)質(zhì)是該元素節(jié)點(diǎn)的所有屬性節(jié)點(diǎn)的結(jié)合,稱為NamedNodeMap,并且擁有它自己的特殊方法:
- getNamedItem(name) 返回nodeName屬性等于name的節(jié)點(diǎn),即返回屬性名對(duì)應(yīng)的屬性節(jié)點(diǎn)
- removeNamedItem(name):刪除該屬性節(jié)點(diǎn)
- setNamedItem(node) :向列表中添加屬性節(jié)點(diǎn),以nodeName為索引
item(po): 返回位于數(shù)字Pos位置處的節(jié)點(diǎn)
創(chuàng)建元素
- document.createElement(元素標(biāo)簽名)
該方法可以傳入完整的元素標(biāo)簽如
var div = document.createElement("div id="myNewDiv" class = "box"></div>");
注意:這種形式創(chuàng)建的動(dòng)態(tài)元素有很多已知的問題,若要?jiǎng)?chuàng)建一些跟別的元素或者存在相關(guān)事件處理的元素,不推薦用這種方法,如iframe標(biāo)簽 input標(biāo)簽 有name相同的單選按鈕等
元素的子節(jié)點(diǎn)
從上面就可以發(fā)現(xiàn),用元素節(jié)點(diǎn)的childNodes遍歷其子元素節(jié)點(diǎn)會(huì)出現(xiàn)一些換行和空格等形成了文本節(jié)點(diǎn)的問題,這意味著在執(zhí)行某項(xiàng)操作之前,都要檢查一下nodeType屬性,如:
for(var i = 0,len=element.childNodes.length;i<len;i++){
if(element.childNodes[i].nodeType == 1){
//執(zhí)行某些操作
} }
Text類型
- nodeValue和data屬性:均可以用來訪問Text節(jié)點(diǎn)包含的文本
- appendData(text)
- deleteData(offset,count)
- insertData(offset,text)
- replaceData(offset,count,text)使用指定文本來替換此節(jié)點(diǎn)以及所有相鄰的文本節(jié)點(diǎn),W3C標(biāo)準(zhǔn),瀏覽器不支持
- splitText(offset)
- (offset,count)從節(jié)點(diǎn)提取數(shù)據(jù)
length屬性返回文本字符的數(shù)目
創(chuàng)建文本節(jié)點(diǎn)
document.createTextNode(要插入節(jié)點(diǎn)中的文本)
作為參數(shù)的文本將按照HTML或XML的格式進(jìn)行編碼
規(guī)范化文本節(jié)點(diǎn)
normalize()方法
分割文本節(jié)點(diǎn)
與normalize()方法相反的splitText(pos)方法,將一個(gè)文本節(jié)點(diǎn)分成兩個(gè)文本節(jié)點(diǎn)
DocumentFragment類型
在所有的節(jié)點(diǎn)類型中,只有DocumentFragment在文檔中沒有對(duì)應(yīng)的標(biāo)記,DOM規(guī)范規(guī)定文檔片段是一種"輕量級(jí)"的文檔,可以包含和控制節(jié)點(diǎn).雖然不能把文檔片段直接添加到文檔中,但可以將它作為一個(gè)"倉庫"來使用,即可以在里面保存將來可能添加到文檔中的節(jié)點(diǎn)
創(chuàng)建文檔片段
使用document.createDocumentFragment(),返回一個(gè)文檔片段對(duì)象,繼承了Node的所有方法,可以往里面堆積節(jié)點(diǎn),然后一次性添加到文檔樹中,減低瀏覽器的渲染次數(shù)
Attr類型
不常單獨(dú)使用,不過多描述
Comment類型 ,CDATASection類型 ,DocumentType類型
不常單獨(dú)使用,不過多描述
2.DOM級(jí)別
2.1 DOM1級(jí)
DOM1級(jí)由兩個(gè)模塊組成,DOM核心(DOM Core)和DOM HTML。
DOM核心規(guī)定的是如何映射基于XML的文檔結(jié)構(gòu),以便簡化堆文檔中任意部分的訪問和操作。
DOM HTML模塊則在DOM核心的基礎(chǔ)上加以擴(kuò)展,添加了針對(duì)HTML的對(duì)象和方法。
2.2 DOM2級(jí)
DOM2級(jí)在原來的DOM基礎(chǔ)上又?jǐn)U充了鼠標(biāo)和用戶界面事件,范圍,遍歷(迭代DOM文檔的方法)等模塊,而且通過對(duì)象接口增加了對(duì)CSS的支持
DOM視圖(DOM Views):定義了跟蹤不同文檔視圖的接口。
DOM事件:描述事件接口;
DOM樣式:描述處理基于CSS樣式的接口;
DOM遍歷與范圍:描述遍歷和操作文檔樹的接口;
2.3DOM3級(jí)
進(jìn)一步擴(kuò)展了DOM,引入了以統(tǒng)一方式加載和保存文檔的方法-在DOM加載和保存(DOMLoad and Save)模板中定義
新增了驗(yàn)證文檔的方法-在DOM驗(yàn)證(DOM Validation)模板中定義。
DOM3級(jí)也對(duì)DOM核心進(jìn)行了擴(kuò)展,開始支持XML1.0規(guī)范,涉及XML Infoset,XPath和XML Base。
注意:DOM0級(jí)標(biāo)準(zhǔn)是不存在的,所謂的DOM0級(jí)只是DOM歷史坐標(biāo)中的一個(gè)參照點(diǎn)。