DOM 中的三個字母,D(文檔)可以理解為整個 Web 加載的網頁文檔;O(對象)可以理解為類似 window 對象之類的東西,可以調用屬性和方法,這里我們說的是 document對象;M(模型)可以理解為網頁文檔的樹型結構。
DOM 有三個等級,分別是 DOM1、DOM2、DOM3,並且 DOM1 在 1998 年 10 月成為W3C 標准。DOM1 所支持的瀏覽器包括 IE6+、Firefox、Safari、Chrome 和 Opera1.7+。
1.節點
加載 HTML 頁面時,Web 瀏覽器生成一個樹型結構,用來表示頁面內部結構。DOM 將這種樹型結構理解為由節點組成。
節點樹
從上圖的樹型結構,我們理解幾個概念,html 標簽沒有父輩,沒有兄弟,所以 html 標簽為根標簽。head 標簽是 html 子標簽,meta 和 title 標簽之間是兄弟關系。如果把每個標簽當作一個節點的話,那么這些節點組合成了一棵節點樹。
PS:后面我們經常把標簽稱作為元素,是一個意思。
2.節點種類:元素節點、文本節點、屬性節點。
二.查找元素
W3C 提供了比較方便簡單的定位節點的方法和屬性,以便我們快速的對節點進行操作。分別為:getElementById()、getElementsByTagName()、getElementsByName()、getAttribute()、setAttribute()和 removeAttribute()。
元素節點方法
1.getElementById()方法
getElementById()方法,接受一個參數:獲取元素的 ID。如果找到相應的元素則返回該元素的 HTMLDivElement 對象,如果不存在,則返回 null。
PS:上面的例子,默認情況返回 null,這無關是否存在 id="box"的標簽,而是執行順序問題。解決方法,1.把 script 調用標簽移到 html 末尾即可;2.使用 onload 事件來處理 JS,等待 html 加載完畢再加載 onload 事件里的 JS。
document.getElementById('box');
};
PS:id 表示一個元素節點的唯一性,不能同時給兩個或以上的元素節點創建同一個命名的 id。某些低版本的瀏覽器會無法識別 getElementById()方法,比如 IE5.0-,這時需要做一些判斷,可以結合上章的瀏覽器檢測來操作。
alert('當前瀏覽器支持 getElementById');
}
當我們通過 getElementById()獲取到特定元素節點時,這個節點對象就被我們獲取到了,而通過這個節點對象,我們可以訪問它的一系列屬性。
元素節點屬性
document.getElementById('box').innerHTML;//測試 Div
HTML 屬性的屬性
document.getElementById('box').id = 'person';//設置 id
document.getElementById('box').title = '標題';//設置 title
document.getElementById('box').style.color;//獲取 style 對象中 color 的值
document.getElementById('box').style.color = 'red'; //設置 style 對象中 color 的值
document.getElementById('box').className = 'box'; //設置 class
2.getElementsByTagName()方法
getElementsByTagName()方法將返回一個對象數組 HTMLCollection(NodeList),這個數組保存着所有相同元素名的節點列表。
PS:IE 瀏覽器在使用通配符的時候,會把文檔最開始的 html 的規范聲明當作第一個元素節點。
document.getElementsByTagName('li')[0];//獲取第一個 li 元素,HTMLLIElement
document.getElementsByTagName('li').item(0);//獲取第一個 li 元素,HTMLLIElement
document.getElementsByTagName('li').length;//獲取所有 li 元素的數目
PS:不管是 getElementById 還是 getElementsByTagName,在傳遞參數的時候,並不是所有瀏覽器都必須區分大小寫,為了防止不必要的錯誤和麻煩,我們必須堅持養成區分大小寫的習慣。
3.getElementsByName()方法
getElementsByName()方法可以獲取相同名稱(name)的元素,返回一個對象數組HTMLCollection(NodeList)。
document.getElementsByName('add')[0].value //獲取 input 元素的 value 值
document.getElementsByName('add')[0].checked //獲取 input 元素的 checked 值
PS:對於並不是 HTML 合法的屬性,那么在 JS 獲取的兼容性上也會存在差異,IE 瀏覽器支持本身合法的 name 屬性,而不合法的就會出現不兼容的問題。
4.getAttribute()方法
getAttribute()方法將獲取元素中某個屬性的值。它和直接使用.屬性獲取屬性值的方法有一定區別。
document.getElementById('box').id;//獲取元素的 id 值
document.getElementById('box').getAttribute('mydiv');//獲取元素的自定義屬性值
document.getElementById('box').mydiv//獲取元素的自定義屬性值, IE 不支持非
document.getElementById('box').getAttribute('class');//獲取元素的 class 值,IE 不支持
document.getElementById('box').getAttribute('className');//非 IE 不支持
PS:HTML 通用屬性 style 和 onclick,IE7 更低的版本 style 返回一個對象,onclick 返回一個函數式。雖然 IE8 已經修復這個 bug,但為了更好的兼容,開發人員只有盡可能避免使用 getAttribute()訪問 HTML 屬性了,或者碰到特殊的屬性獲取做特殊的兼容處理。
5.setAttribute()方法
setAttribute()方法將設置元素中某個屬性和值。它需要接受兩個參數:屬性名和值。如果屬性本身已存在,那么就會被覆蓋。
document.getElementById('box').setAttribute('bbb','ccc');//設置自定義的屬性和值
PS:在 IE7 及更低的版本中,使用 setAttribute()方法設置 class 和 style 屬性是沒有效果的,雖然 IE8 解決了這個 bug,但還是不建議使用。
6.removeAttribute()方法
document.getElementById('box').removeAttribute('style');//移除屬性
PS:IE6 及更低版本不支持 removeAttribute()方法。
三.DOM 節點
1.node 節點屬性
節點可以分為元素節點、屬性節點和文本節點,而這些節點又有三個非常有用的屬性,分別為:nodeName、nodeType 和 nodeValue。
信息節點屬性
2.層次節點屬性
節點的層次結構可以划分為:父節點與子節點、兄弟節點這兩種。當我們獲取其中一個元素節點的時候,就可以使用層次節點屬性來獲取它相關層次的節點。
層次節點屬性
1.childNodes 屬性
childeNodes 屬性可以獲取某一個元素節點的所有子節點,這些子節點包含元素子節點和文本子節點。
alert(box.childNodes.length);//獲取這個元素節點的所有子節點
alert(box.childNodes[0]);//獲取第一個子節點對象
PS:使用 childNodes[n]返回子節點對象的時候,有可能返回的是元素子節點,比如HTMLElement;也有可能返回的是文本子節點,比如 Text。元素子節點可以使用 nodeName或者 tagName 獲取標簽名稱,而文本子節點可以使用 nodeValue 獲取。
//判斷是元素節點,輸出元素標簽名
if (box.childNodes[i].nodeType === 1) {
alert('元素節點:' + box.childNodes[i].nodeName);
//判斷是文本節點,輸出文本內容
} else if (box.childNodes[i].nodeType === 3) {
alert('文本節點:' + box.childNodes[i].nodeValue);
}
}
PS:在獲取到文本節點的時候,是無法使用 innerHTML 這個屬性輸出文本內容的。這個非標准的屬性必須在獲取元素節點的時候,才能輸出里面包含的文本。
PS:innerHTML 和 nodeValue 第一個區別,就是取值的。那么第二個區別就是賦值的時候,nodeValue 會把包含在文本里的 HTML 轉義成特殊字符,從而達到形成單純文本的效果。
abcbox.innerHTML = '<strong>abc</strong>';//結果為:abc
2.firstChild 和 lastChild 屬性
firstChild 用於獲取當前元素節點的第一個子節點,相當於 childNodes[0];lastChild 用於獲取當前元素節點的最后一個子節點,相當於 childNodes[box.childNodes.length - 1]。
alert(box.lastChild.nodeValue);//獲取最后一個子節點的文本內容
3.ownerDocument 屬性
ownerDocument 屬性返回該節點的文檔對象根節點,返回的對象相當於 document。
4.parentNode、previousSibling、nextSibling 屬性
parentNode 屬性返回該節點的父節點,previousSibling 屬性返回該節點的前一個同級節點,nextSibling 屬性返回該節點的后一個同級節點。
alert(box.lastChild.previousSibling);//獲取前一個同級節點
alert(box.firstChild.nextSibling);//獲取后一個同級節點
5.attributes 屬性
attributes 屬性返回該節點的屬性節點集合。
document.getElementById('box').attributes.length;//返回屬性節點個數
document.getElementById('box').attributes[0]; //Attr,返回最后一個屬性節點
document.getElementById('box').attributes[0].nodeType; //2,節點類型
document.getElementById('box').attributes[0].nodeValue; //屬性值
document.getElementById('box').attributes['id']; //Attr,返回屬性為 id 的節點
document.getElementById('box').attributes.getNamedItem('id'); //Attr
6.忽略空白文本節點
alert(body.childNodes.length);//得到子節點個數,IE3 個,非 IE7 個
PS:在非 IE 中,標准的 DOM 具有識別空白文本節點的功能,所以在火狐瀏覽器是 7個,而 IE 自動忽略了,如果要保持一致的子元素節點,需要手工忽略掉它。
var ret = [];//新數組
for (var i = 0; i < nodes.length; i ++) {
//如果識別到空白文本節點,就不添加數組
if (nodes[i].nodeType == 3 && /^\s+$/.test(nodes[i].nodeValue)) continue;
//把每次的元素節點,添加到數組里
ret.push(nodes[i]);
}
return ret;
}
PS:上面的方法,采用的忽略空白文件節點的方法,把得到元素節點累加到數組里返回。那么還有一種做法是,直接刪除空位文件節點即可。
for (var i = 0; i < nodes.length; i ++) {
if (nodes[i].nodeType == 3 && /^\s+$/.test(nodes[i].nodeValue)) {
//得到空白節點之后,移到父節點上,刪除子節點
nodes[i].parentNode.removeChild(nodes[i]);
}
}
return nodes;
}
PS:如果 firstChild、lastChild、previousSibling 和 nextSibling 在獲取節點的過程中遇到空白節點,我們該怎么處理掉呢?
for (var i = 0; i < nodes.childNodes.length; i ++) {
if (nodes.childNodes[i].nodeType === 3 &&
/^\s+$/.test(nodes.childNodes[i].nodeValue)) {
nodes.childNodes[i].parentNode.removeChild(nodes.childNodes[i]);
}
}
return nodes;
}
四.節點操作
DOM 不單單可以查找節點,也可以創建節點、復制節點、插入節點、刪除節點和替換節點。
節點操作方法
1.write()方法
write()方法可以把任意字符串插入到文檔中去。
2.createElement()方法
createElement()方法可以創建一個元素節點。
3.appendChild()方法
appendChild()方法講一個新節點添加到某個節點的子節點列表的末尾上。
var p = document.createElement('p');//創建一個新元素節點<p>
box.appendChild(p);//把新元素節點<p>添加子節點末尾
4.createTextNode()方法
createTextNode()方法創建一個文本節點。
p.appendChild(text);//將文本節點添加到子節點末尾
5.insertBefore()方法
insertBefore()方法可以把節點創建到指定節點的前面。
PS:insertBefore()方法可以給當前元素的前面創建一個節點,但卻沒有提供給當前元素的后面創建一個節點。那么,我們可以用已有的知識創建一個 insertAfter()函數。
//得到父節點
var parent = targetElement.parentNode;
//如果最后一個子節點是當前元素,那么直接添加即可
if (parent.lastChild === targetElement) {
parent.appendChild(newElement);
} else {
//否則,在當前節點的下一個節點之前添加
parent.insertBefore(newElement, targetElement.nextSibling);
}
}
PS:createElement 在創建一般元素節點的時候,瀏覽器的兼容性都還比較好。但在幾個特殊標簽上,比如 iframe、input 中的 radio 和 checkbox、button 元素中,可能會在 IE6,7以下的瀏覽器存在一些不兼容。
if (BrowserDetect.browser == 'Internet Explorer' && BrowserDetect.version <= 7) {
//判斷 IE6,7,使用字符串的方式
input = document.createElement("<input type=\"radio\" name=\"sex\">");
} else {
//標准瀏覽器,使用標准方式
input = document.createElement('input');
input.setAttribute('type', 'radio');
input.setAttribute('name', 'sex');
}
document.getElementsByTagName('body')[0].appendChild(input);
6.repalceChild()方法
replaceChild()方法可以把節點替換成指定的節點。
7.cloneNode()方法
cloneNode()方法可以把子節點復制出來。
var clone = box.firstChild.cloneNode(true);//獲取第一個子節點,true 表示復制內容
box.appendChild(clone);//添加到子節點列表末尾
8.removeChild()方法
removeChild()方法可以把
var oP = document.createElement("p"); var oT = document.createTextNode("寫入的文本"); oP.appendChild(oT); document.body.appendChild(oP);
2、刪除節點
function RemoveNode(){ var oNewP = document.getElementsByTagName("p")[0]; oNewP.parentNode.removeChild(oNewP); }
3、替換
function ReplaceNode(){ var oNewP = document.getElementsByTagName("p")[0]; var oNewDiv = document.createElement("div"); oNewDiv.innerHTML = "<p>文字</p>"; oNewP.parentNode.replaceChild(oNewDiv,oNewP); }
4、插入
function InsertNode(){ var oNewP = document.getElementsByTagName("p")[0]; var oNewDiv = document.crenteElement("div"); oNewDiv.innerHTML = "<br/>文字"; oNewP.parentNode.insertBefoe(oNewDiv,oNewP); }