DOM
DOM就是Document object Model的縮寫。文檔對象模型是把XML或HTML以樹節點為表現形式的文檔。用DOM方法和屬性,你可以訪問,修改,刪除頁面上任意一個元素,也是可以添加一個元素。DOM是獨立於語言的API,可以被任意語言所實現,當然也包括了Javascript
看看下面的一個文本。
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html> <head> <title>My page</title> </head> <body> <p class="opener">first paragraph</p> <p><em>second</em> paragraph</p> <p id="closer">final</p> </body> </html>
我們來看看第二段
<p><em>second</em> paragraph</p>
可以看到這是個p標簽。它被包含在body標簽中。所以說body是p的父節點,p是子節點。第一段和第三段也是body的子節點。它們都是第二段的兄弟節點。這個em標簽是第二段p的子節點。因此p就是它的父節點。父子節點關系能描繪出像樹一樣的關系。所以叫做DOM結構樹(DOM tree).
Core DOM 和 HTML DOM
我們已經知道DOM可以描繪出HTML和XML文檔。事實上HTML文檔就是XML文檔,只不過更為規范些。因此作為DOM Level 1的一部分,Core DOM規范適用於所有XML文檔以及HTML DOM規范擴展了Core DOM.當然HTML DOM不能適用於所有的XML文檔,僅僅適用於HTML文檔。讓我們來看看Core DOM和HTML DOM的構造器。
構造器關系
訪問DOM節點
當要驗證表單或者更換圖片之前,我們要知道怎麼樣訪問元素(element.)。有很多方法獲取元素。
Document節點
通過document我們可以訪問當前的文檔。我們可以通過firebugs(火狐插件),來查看document的屬性和方法。
所有的節點都有nodeType,nodeName,nodeValue的屬性。我們來看看document的nodeType是什麼
document.nodeType;//9
一共有12節點類型。document是9。常用的是元素(element:1),屬性(attribute:2),文本(text:3)。
節點還有名稱。對於HTML標簽。節點名稱就是標簽名稱。文本節點(text)的名字是#text.文檔節點(document)的名字是#document.
節點也有值。對於文本節點,值就是文本。document的值為null
documentElement
XML都會有個ROOT節點來包文檔包含起來。對於HTML文檔。ROOT節點就是html標簽。訪問root節點。可以用documentElement的屬性。
document.documentElement;//<html> document.documentElement.nodeType;//1 document.documentElement.nodeName;//HTML document.documentElement.tagName;//對於element,nodeName和tagName相同
Child Nodes
為了確定是否包含子節點我們可以用如下方法
document.documentElement.hasChildNodes();//true
HTML有兩個子節點。
document.documentElement.childNodes.length;//2 document.documentElement.childNodes[0];//<head> document.documentElement.childNodes[1];//<body>
也可以通過子節點訪問父節點
document.documentElement.childNodes[1].parentNode;//<html>
我們把body的引用賦值變量
var bd = document.documentElement.childNodes[1]; bd.childNodes.length;//9
讓我們看看body的結構
<body> <p class="opener">first paragraph</p> <p><em>second</em> paragraph</p> <p id="closer">final</p> <!-- and that's about it --> </body>
為什麼子節點個數為9個呢。
首先是4個P還有一個注釋一共4個。
4個節點包含了3個空白的節點。這就7個。
第8個在body和第一個p之間的空白節點。
第9個就是在注釋與</body>之間的空白節點。
一共9個節點。
屬性
因為第一節點是空白節點,所以第二個節點才是第一個p標簽。
bd.childNodes[1];// <p class="opener">
可以查看它是否有屬性
bd.childNodes[1].hasAttributes();//true
也可以查看attributes的個數
bd.childNodes[1].attributes.length;//1 //可以用index和名字來訪問屬性,也可以用getAttribute方法。 bd.childNodes[1].attributes[0].nodeName;//class bd.childNodes[1].attributes[0].nodeValue;//opener bd.childNodes[1].attributes['class'].nodeValue;//opener bd.childNodes[1].getAttribute('class');//opener
訪問標簽裡的內容
讓我們看看第一個標簽p
你可以用textContent屬性來訪問。要注意的是,在IE浏覽器中並不存在textContent,請用innerText來替換,結果都是一樣的。
bg.childNodes[1].textContent;// "first paragraph"
還有一個屬性是innerHTML.這並不是DOM的規范。但是主流浏覽器都支持這個屬性。它返回的是HTML代碼。
bg.childNodes[1].innerHTML;// "first paragraph"
第一段沒有html代碼,所以結果和textContent(IE裡是innerText)是一樣的。來看下第二段包含HTML代碼的標簽
bd.childNodes[3].innerHTML;//"<em>second</em> paragraph" bd.childNodes[3].textContent;//second paragraph
另一種方法可以獲取文本節點,然後再取nodeValue屬性,代碼如下
bd.childNodes[1].childNodes.length;//1 子節點個數 bd.childNodes[1].childNodes[0].nodeName;// 節點名稱 #text bd.childNodes[1].childNodes[0].nodeValue;//節點值 first paragraph
快速訪問DOM
通過childNodes,parentNode,nodeName,nodeValue以及attributes,可以訪問文檔任意的節點了。但是在實際運用過程中,文本節點是比較討厭的。如果文本改變了,有可能就影響腳本了。還有如果DOM樹足夠的深入,那麼訪問起來的確有些不方便。幸好我們可以用更為方便的方法來訪問節點。這些方法是
getElementsByTagName() getElementsByName() getElementById()
首先說下getElementsByTagName()
通過一個標簽名稱(tag name)來獲取一個html元素的集合。例子如下
document.getElementsByTagName('p').length;//3
因為返回的是個集合,我們可以用過數組下標的形式來訪問或者通過item方法。比較一下還是推薦用數組的訪問方法。更簡單一些。
document.getElementsByTagName('p')[0];// <p class="opener"> document.getElementsByTagName('p').item(0);//和上面的結果一樣 document.getElementsByTagName('p')[0].innerHTML;//first paragraph
訪問元素的屬性,可以用attributes集合。但是更簡單的方法是直接作為一個屬性來訪問就行。看個例子
document.getElementsByTagName('p')[2].id;//closer
要注意的是,class屬性不能正常的使用。。要用className。因為class在javascript規范中是保留字。
document.getElementsByTagName('p')[0].className;//opener
我們可以用如下方法訪問頁面所有元素
<span style="color: #ff0000;">document.getElementsByTagName('*').length;//9</span>
注意:在IE早期的版本不支持上述方法。可以用document.all來取代。IE7已經支持了,但是返回的是所有節點(node),而不僅僅是元素節點(element nodes)。
Siblings, Body, First, Last Child
nextSibling和previousSibling是兩個比較方便訪問DOM的方法。用來訪問相鄰的節點的。例子如下
var para = document.getElementById('closer') para.nextSibling;//"\n" para.previousSibling;//"\n" para.previousSibling.previousSibling;//<p> para.previousSibling.previousSibling.previousSibling;//"\n" para.previousSibling.previousSibling.nextSibling.nextSibling;// <p id="closer">
body 用來訪問body元素的。
document.body;//<body>
firstChild 和lastChild 。firstChild是和childNodes[0]一樣.lastChild和 childNodes[childNodes.length - 1]一樣。
遍歷DOM
通過以上的學習,我們可以寫個函數,用來遍歷DOM
function walkDOM(n) { do { alert(n); if (n.hasChildNodes()) { walkDOM(n.firstChild) } } while (n = n.nextSibling) } walkDOM(document.body);//測試
修改節點
下面來看看DOM節點的修改。
先獲取要改變的節點。
var my = document.getElementById('closer');
非常容易更改這個元素的屬性。我們可以更改innerHTML.
my.innerHTML = 'final';//final
因為innerHTML可以寫入html,所以我們來修改html。
my.innerHTML = '<em>my</em> final';//<em>my</em> fnal
em標簽已經成為dom樹的一部分了。我們可以測試一下
my.firstChild;//<em> my.firstChild.firstChild;//my
我們也可以通過nodeValue來改變值。
my.firstChild.firstChild.nodeValue = 'your';//your
修改樣式
大部分修改節點可能都是修改樣式。元素節點有style屬性用來修改樣式。style的屬性和css屬性是一一對應的。如下代碼
my.style.border = "1px solid red";
CSS屬性很多都有破折號("-"),如padding-top,這在javascript中是不合法的。這樣的話一定要省略波折號並把第二個詞的開頭字母大寫,規范如下。 margin-left變為marginLeft。依此類推
my.style.fontWeight = 'bold';
我們還可以修改其他的屬性,無論這些屬性是否被初始化。
my.align = "right"; my.name = 'myname'; my.id = 'further'; my;//<p id="further" align="right" style="border: 1px solid red; font-weight: bold;">
創建節點
為了創建一個新的節點,可以使用createElement和createTextNode.如果新建完成,可以用appendChild()把節點添加到DOM樹中。
創建一個元素P,並把設置innerHTML屬性
var myp = document.createElement('p'); myp.innerHTML = 'yet another';
元素P建完成了,就可以隨意修改添加屬性了
myp.style.border = '2px dotted blue'
接下來可以用appendChild把新的節點添加到DOM樹中的。
document.body.appendChild(myp)
使用DOM的方法
用innerHTML方法的確很簡單,我們可以用純的dom方法來實現上面的功能。
// 創建p var myp = document.createElement('p'); // 創建一個文本節點 var myt = document.createTextNode('one more paragraph') myp.appendChild(myt); // 創建一個STRONG元素 var str = document.createElement('strong'); str.appendChild(document.createTextNode('bold')); // 把STRONG元素添加到P中 myp.appendChild(str); // 把P元素添加到BODY中 document.body.appendChild(myp); //結果<p>one more paragraph<strong>bold</strong></p> cloneNode()
另一種新建節點的方法是,我們可以用cloneNode來復制一個節點。cloneNode()可以傳入一個boolean參數。如果為true就是深度復制,包括他的子節點,false,僅僅復制自己。
首先獲取要復制的元素。
var el = document.getElementsByTagName('p')[1];//<p><em>second</em> paragraph</p>
先不用深度復制。
document.body.appendChild(el.cloneNode(false))
我們發現頁面並沒有變化,因為僅僅復制的是元素p。和下面的效果一樣。
document.body.appendChild(document.createElement('p'));
如果用深度復制,包括p下面所有的子節點都會被復制。當然包括文本節點和EM元素。
document.body.appendChild(el.cloneNode(true))
insertBefore()
用appendChild,就是把元素添加到最後。而insertBefore方法可以更精確控制插入元素的位置。
elementNode.insertBefore(new_node,existing_node)
實例
document.body.insertBefore( document.createTextNode('boo!'), document.body.firstChild );
意思就是新建一個文本節點,把它作為body元素的第一個節點。
刪除節點
要從DOM樹刪除一個節點,我們可以使用removeChild().我們來看看要操作的HTML
<body> <p class="opener">first paragraph</p> <p><em>second</em> paragraph</p> <p id="closer">final</p> <!-- and that's about it --> </body>
來看看下面代碼,刪除第二段
var myp = document.getElementsByTagName('p')[1]; var removed = document.body.removeChild(myp);
removed節點就是刪除的節點。以後還可以用這刪除的節點。
我們也可以用replaceChild()方法。這個方法是刪除一個節點,並用另一個節點替代。當執行上個刪除節點操作之後,結果如下
<body> <p class="opener">first paragraph</p> <p id="closer">final</p> <!-- and that's about it --> </body>
我們來看看replaceChild的使用。我們把上一個刪除節點來替代第二個p
var replaced = document.body.replaceChild(removed, p);
和removeChild返回一樣。replaced就是移除的節點。現在結果為
<body> <p class="opener">first paragraph</p> <p><em>second</em> paragraph</p> <!-- and that's about it --> </body>