文檔對象模型 (DOM) 是HTML和XML文檔的編程接口。它提供了對文檔的結構化的表述,並定義了一種方式可使從程序中對該結構進行訪問,從而改變文檔的結構,樣式和內容。
文檔對象模型 (DOM) 是對HTML文件的另外一種展現,通俗地說,一個HTML 文件,咱們能夠用編輯器以代碼的形式展現它,也能夠用瀏覽器以頁面的形式展現它,同一份文件經過不一樣的展現方式,就有了不同的表現形式。而DOM 將文檔解析爲一個由節點和對象(包含屬性和方法的對象)組成的結構集合。簡言之,它會將web頁面和腳本或程序語言鏈接起來,咱們可使用腳本或者程序語言經過DOM 來改變或者控制web頁面。javascript
咱們能夠經過JavaScript 來調用document
和window
元素的API來操做文檔或者獲取文檔的信息。css
Node 是一個接口,有許多接口都從Node 繼承方法和屬性: Document
, Element
, CharacterData (which Text, Comment, and CDATASection inherit)
, ProcessingInstruction
, DocumentFragment
, DocumentType
, Notation
, Entity
, EntityReference
。 Node 有一個nodeType
的屬性表示Node 的類型,是一個整數,不一樣的值表明不一樣的節點類型。具體以下表所示:html
節點類型常量java
常量 | 值 | 描述 |
---|---|---|
Node.ELEMENT_NODE |
1 | 一個元素節點,例如 <p> 和 <div> |
Node.TEXT_NODE |
3 | Element 或者 Attr 中實際的文字 |
Node.PROCESSING_INSTRUCTION_NODE |
7 | 一個用於XML文檔的 ProcessingInstruction ,例如 <?xml-stylesheet ... ?> 聲明 |
Node.COMMENT_NODE |
8 | 一個 Comment 節點 |
Node.DOCUMENT_NODE |
9 | 一個 Document 節點 |
Node.DOCUMENT_TYPE_NODE |
10 | 描述文檔類型的 DocumentType 節點。例如 <!DOCTYPE html> 就是用於 HTML5 的 |
Node.DOCUMENT_FRAGMENT_NODE |
11 | 一個 DocumentFragment 節點 |
已棄用的節點類型常量node
常量 | 值 | 描述 |
---|---|---|
Node.ATTRIBUTE_NODE |
2 | 元素的耦合屬性。在DOM4 規範裏Node 接口將再也不實現這個元素屬性 |
Node.CDATA_SECTION_NODE |
4 | 一個CDATASection 。在DOM4 規範裏被移除 |
Node.ENTITY_REFERENCE_NODE |
5 | 一個XML 實體引用節點。在DOM4 規範裏被移除 |
Node.ENTITY_NODE |
6 | 一個XML <!ENTITY ...> 節點。在DOM4 規範中被移除 |
Node.NOTATION_NODE |
12 | 一個XML <!NOTATION ...> 節點。在DOM4 規範裏被移除 |
假設咱們要判斷一個Node 是否是一個元素
,經過查表可知元素
的nodeType
屬性值爲1,代碼能夠這麼寫:web
if(X.nodeType === 1){
console.log('X 是一個元素');
}
複製代碼
在Node 類型中,比較經常使用的就是element
,text
,comment
,document
,document_fragment
這幾種類型。編程
Element提供了對元素標籤名,子節點和特性的訪問,咱們經常使用HTML元素好比div
,span
,a
等標籤就是element
中的一種。Element有下面幾條特性:
(1)nodeType爲1
(2)nodeName爲元素標籤名,tagName也是返回標籤名
(3)nodeValue爲null
(4)parentNode多是Document或Element
(5)子節點多是Element,Text,Comment,Processing_Instruction,CDATASection或EntityReferenceapi
Text表示文本節點,它包含的是純文本內容,不能包含html代碼,但能夠包含轉義後的html代碼。Text有下面的特性:
(1)nodeType爲3
(2)nodeName爲#text
(3)nodeValue爲文本內容
(4)parentNode是一個Element
(5)沒有子節點瀏覽器
Comment表示HTML文檔中的註釋,它有下面的幾種特徵:
(1)nodeType爲8
(2)nodeName爲#comment
(3)nodeValue爲註釋的內容
(4)parentNode多是Document或Element
(5)沒有子節點
緩存
Document表示文檔,在瀏覽器中,document
對象是HTMLDocument的一個實例,表示整個頁面,它同時也是window對象的一個屬性。Document有下面的特性:
(1)nodeType爲9
(2)nodeName爲#document
(3)nodeValue爲null
(4)parentNode爲null
(5)子節點多是一個DocumentType或Element
DocumentFragment是全部節點中惟一一個沒有對應標記的類型,它表示一種輕量級的文檔,可能看成一個臨時的倉庫用來保存可能會添加到文檔中的節點。DocumentFragment有下面的特性:
(1)nodeType爲11
(2)nodeName爲#document-fragment
(3)nodeValue爲null
(4)parentNode爲null
用如其名,這類API是用來建立節點的
createElement
經過傳入指定的一個標籤名來建立一個元素,若是傳入的標籤名是一個未知的,則會建立一個自定義的標籤,注意:IE8如下瀏覽器不支持自定義標籤。
語法
let element = document.createElement(tagName);
複製代碼
使用createElement
要注意:經過createElement
建立的元素並不屬於HTML文檔,它只是建立出來,並未添加到HTML文檔中,要調用appendChild
或insertBefore
等方法將其添加到HTML文檔樹中。
例子:
let elem = document.createElement("div");
elem.id = 'test';
elem.style = 'color: red';
elem.innerHTML = '我是新建立的節點';
document.body.appendChild(elem);
複製代碼
運行結果爲:
createTextNode
用來建立一個文本節點
語法
var text = document.createTextNode(data);
複製代碼
createTextNode
接收一個參數,這個參數就是文本節點中的文本,和createElement
同樣,建立後的文本節點也只是獨立的一個節點,一樣須要appendChild
將其添加到HTML文檔樹中
例子:
var node = document.createTextNode("我是文本節點");
document.body.appendChild(node);
複製代碼
運行結果爲:
cloneNode
返回調用該方法的節點的一個副本
語法
var dupNode = node.cloneNode(deep);
複製代碼
node
將要被克隆的節點 dupNode
克隆生成的副本節點 deep
(可選)是否採用深度克隆,若是爲true,則該節點的全部後代節點也都會被克隆,若是爲false,則只克隆該節點自己.
這裏有幾點要注意:
(1)和createElement
同樣,cloneNode
建立的節點只是遊離有HTML文檔外的節點,要調用appendChild
方法才能添加到文檔樹中
(2)若是複製的元素有id
,則其副本一樣會包含該id
,因爲id
具備惟一性,因此在複製節點後必需要修改其id
(3)調用接收的deep
參數最好傳入,若是不傳入該參數,不一樣瀏覽器對其默認值的處理可能不一樣
注意
若是被複制的節點綁定了事件,則副本也會跟着綁定該事件嗎?這裏要分狀況討論:
(1)若是是經過addEventListener或者好比onclick進行綁定事件,則副本節點不會綁定該事件
(2)若是是內聯方式綁定好比:<div onclick="showParent()"></div>
,這樣的話,副本節點一樣會觸發事件。
例子:
<body>
<div id="parent">
我是父元素的文本
<br/>
<span>
我是子元素
</span>
</div>
<button id="btnCopy">複製</button>
</body>
<script>
var parent = document.getElementById("parent");
document.getElementById("btnCopy").onclick = function(){
var parent2 = parent.cloneNode(true);
parent2.id = "parent2";
document.body.appendChild(parent2);
}
</script>
複製代碼
運行結果爲:
DocumentFragments
是DOM節點。它們不是主DOM樹的一部分。一般的用例是建立文檔片斷,將元素附加到文檔片斷,而後將文檔片斷附加到DOM樹。在DOM樹中,文檔片斷被其全部的子元素所代替。
由於文檔片斷存在於內存中,並不在DOM樹中,因此將子元素插入到文檔片斷時不會引發頁面迴流(reflow)(對元素位置和幾何上的計算)。所以,使用文檔片斷document fragments
一般會起到優化性能的做用。
語法
let fragment = document.createDocumentFragment();
複製代碼
例子:
<body>
<ul id="ul"></ul>
</body>
<script>
(function()
{
var start = Date.now();
var str = '', li;
var ul = document.getElementById('ul');
var fragment = document.createDocumentFragment();
for(var i=0; i<1000; i++)
{
li = document.createElement('li');
li.textContent = '第'+(i+1)+'個子節點';
fragment.appendChild(li);
}
ul.appendChild(fragment);
})();
</script>
複製代碼
運行結果爲:
節點建立型API主要包括createElement
,createTextNode
,cloneNode
和createDocumentFragment
四個方法,須要注意下面幾點:
(1)它們建立的節點只是一個孤立的節點,要經過appendChild
添加到文檔中
(2)cloneNode
要注意若是被複制的節點是否包含子節點以及事件綁定等問題
(3)使用createDocumentFragment
來解決添加大量節點時的性能問題
前面咱們提到節點建立型API,它們只是建立節點,並無真正修改到頁面內容,而是要調用·appendChild·來將其添加到文檔樹中。我在這裏將這類會修改到頁面內容歸爲一類。
修改頁面內容的api主要包括:appendChild
,insertBefore
,removeChild
,replaceChild
。
appendChild
咱們在前面已經用到屢次,就是將指定的節點添加到調用該方法的節點的子元素的末尾。
語法
parent.appendChild(child);
複製代碼
child節點將會做爲parent節點的最後一個子節點。
appendChild
這個方法很簡單,可是還有有一點須要注意:若是被添加的節點是一個頁面中存在的節點,則執行後這個節點將會添加到指定位置,其本來所在的位置將移除該節點,也就是說不會同時存在兩個該節點在頁面上,至關於把這個節點移動到另外一個地方。
若是child綁定了事件,被移動時,它依然綁定着該事件。
例子:
<body>
<div id="child">
要被添加的節點
</div>
<br/>
<br/>
<br/>
<div id="parent">
要移動的位置
</div>
<input id="btnMove" type="button" value="移動節點" />
</body>
<script>
document.getElementById("btnMove").onclick = function(){
var child = document.getElementById("child");
document.getElementById("parent").appendChild(child);
}
</script>
複製代碼
運行結果:
insertBefore
用來添加一個節點到一個參照節點以前
語法
parentNode.insertBefore(newNode,refNode);
複製代碼
parentNode
表示新節點被添加後的父節點 newNode
表示要添加的節點 refNode
表示參照節點,新節點會添加到這個節點以前
例子:
<body>
<div id="parent">
父節點
<div id="child">
子元素
</div>
</div>
<input type="button" id="insertNode" value="插入節點" />
</body>
<script>
var parent = document.getElementById("parent");
var child = document.getElementById("child");
document.getElementById("insertNode").onclick = function(){
var newNode = document.createElement("div");
newNode.textContent = "新節點"
parent.insertBefore(newNode,child);
}
</script>
複製代碼
運行結果:
關於第二個參數參照節點還有幾個注意的地方:
(1)refNode是必傳的,若是不傳該參數會報錯
(2)若是refNode是undefined或null,則insertBefore會將節點添加到子元素的末尾
刪除指定的子節點並返回
語法
var deletedChild = parent.removeChild(node);
複製代碼
deletedChild
指向被刪除節點的引用,它等於node
,被刪除的節點仍然存在於內存中,能夠對其進行下一步操做。
注意:若是被刪除的節點不是其子節點,則程序將會報錯。咱們能夠經過下面的方式來確保能夠刪除:
if(node.parentNode){
node.parentNode.removeChild(node);
}
複製代碼
運行結果:
replaceChild
用於使用一個節點替換另外一個節點
語法
parent.replaceChild(newChild,oldChild);
複製代碼
newChild
是替換的節點,能夠是新的節點,也能夠是頁面上的節點,若是是頁面上的節點,則其將被轉移到新的位置 oldChild
是被替換的節點
例子:
<body>
<div id="parent"> 父節點 <div id="child"> 子元素 </div> </div>
<input type="button" id="insertNode" value="替換節點" />
</body>
<script>
var parent = document.getElementById("parent");
var child = document.getElementById("child");
document.getElementById("insertNode").onclick = function(){
var newNode = document.createElement("div");
newNode.textContent = "新節點"
parent.replaceChild(newNode,child)
}
複製代碼
運行結果:
頁面修改型API主要是這四個接口,要注意幾個特色:
(1)不論是新增仍是替換節點,若是新增或替換的節點是本來存在頁面上的,則其原來位置的節點將被移除,也就是說同一個節點不能存在於頁面的多個位置
(2)節點自己綁定的事件會不會消失,會一直保留着。
這個接口很簡單,根據元素id返回元素,返回值是Element類型,若是不存在該元素,則返回null
語法
var element = document.getElementById(id);
複製代碼
使用這個接口有幾點要注意:
(1)元素的Id是大小寫敏感的,必定要寫對元素的id
(2)HTML文檔中可能存在多個id相同的元素,則返回第一個元素
(3)只從文檔中進行搜索元素,若是建立了一個元素並指定id,但並無添加到文檔中,則這個元素是不會被查找到的
例子:
<body>
<p id="para1">Some text here</p>
<button onclick="changeColor('blue');">blue</button>
<button onclick="changeColor('red');">red</button>
</body>
<script>
function changeColor(newColor) {
var elem = document.getElementById("para1");
elem.style.color = newColor;
}
</script>
複製代碼
運行結果:
返回一個包括全部給定標籤名稱的元素的HTML集合HTMLCollection。 整個文件結構都會被搜索,包括根節點。返回的 HTML集合是動態的, 意味着它能夠自動更新本身來保持和 DOM 樹的同步而不用再次調用document.getElementsByTagName()
語法
var elements = document.getElementsByTagName(name);
複製代碼
(1)若是要對HTMLCollection集合進行循環操做,最好將其長度緩存起來,由於每次循環都會去計算長度,暫時緩存起來能夠提升效率
(2)若是沒有存在指定的標籤,該接口返回的不是null,而是一個空的HTMLCollection
(3)name
是一個表明元素的名稱的字符串。特殊字符 "*" 表明了全部元素。
例子:
<body>
<div>div1</div>
<div>div2</div>
<input type="button" value="顯示數量" id="btnShowCount"/>
<input type="button" value="新增div" id="btnAddDiv"/>
</body>
<script>
var divList = document.getElementsByTagName("div");
document.getElementById("btnAddDiv").onclick = function(){
var div = document.createElement("div");
div.textContent ="div" + (divList.length+1);
document.body.appendChild(div);
}
document.getElementById("btnShowCount").onclick = function(){
alert(divList.length);
}
</script>
複製代碼
這段代碼中有兩個按鈕,一個按鈕是顯示HTMLCollection元素的個數,另外一個按鈕能夠新增一個div標籤到文檔中。前面提到HTMLCollcetion元素是即時的表示該集合是隨時變化的,也就是是文檔中有幾個div,它會隨時進行變化,當咱們新增一個div後,再訪問HTMLCollection時,就會包含這個新增的div。
運行結果:
getElementsByName
主要是經過指定的name
屬性來獲取元素,它返回一個即時的NodeList對象
語法
var elements = document.getElementsByName(name)
複製代碼
使用這個接口主要要注意幾點:
(1)返回對象是一個即時的NodeList,它是隨時變化的
(2)在HTML元素中,並非全部元素都有name
屬性,好比div
是沒有name
屬性的,可是若是強制設置div
的name
屬性,它也是能夠被查找到的
(3)在IE中,若是id
設置成某個值,而後傳入getElementsByName
的參數值和id值同樣,則這個元素是會被找到的,因此最好很差設置一樣的值給id
和name
例子:
<script type="text/javascript">
function getElements()
{
var x=document.getElementsByName("myInput");
alert(x.length);
}
</script>
<body>
<input name="myInput" type="text" size="20" /><br />
<input name="myInput" type="text" size="20" /><br />
<input name="myInput" type="text" size="20" /><br />
<br />
<input type="button" onclick="getElements()" value="How many elements named 'myInput'?" />
</body>
複製代碼
運行結果:
這個API是根據元素的class返回一個即時的HTMLCollection
語法
var elements = document.getElementsByClassName(names); // or:
var elements = rootElement.getElementsByClassName(names);
複製代碼
elements
是一個實時集合,包含了找到的全部元素names
是一個字符串,表示要匹配的類名列表;類名經過空格分隔getElementsByClassName
能夠在任何元素上調用,不只僅是document
。調用這個方法的元素將做爲本次查找的根元素這個接口有下面幾點要注意:
(1)返回結果是一個即時的HTMLCollection,會隨時根據文檔結構變化
(2)IE9如下瀏覽器不支持
(3)若是要獲取2個以上classname
,可傳入多個classname
,每一個用空格相隔,例如
var elements = document.getElementsByClassName("test1 test2");
複製代碼
例子:
class
爲 'test' 的元素var elements = document.getElementsByClassName('test');
複製代碼
class
同時包括 'red' 和 'test' 的元素var elements = document.getElementsByClassName('red test');
複製代碼
id
爲'main'的元素的子節點中,獲取全部class
爲'test'的元素var elements = document.getElementById('main').getElementsByClassName('test');
複製代碼
Array.prototype
的方法,調用時傳遞HTMLCollection 做爲方法的參數。這裏咱們將查找到全部class
爲'test'的div
元素:var testElements = document.getElementsByClassName('test');
var testDivs = Array.prototype.filter.call(testElements, function(testElement){
return testElement.nodeName === 'DIV';;
});
複製代碼
這兩個API很類似,經過css選擇器來查找元素,注意選擇器要符合CSS選擇器的規則
document.querySelector
返回第一個匹配的元素,若是沒有匹配的元素,則返回null
語法
var element = document.querySelector(selectors);
複製代碼
注意,因爲返回的是第一個匹配的元素,這個api使用的深度優先搜索來獲取元素。
例子:
<body>
<div>
<div>
<span class="test">第三級的span</span>
</div>
</div>
<div class="test">
同級的第二個div
</div>
<input type="button" id="btnGet" value="獲取test元素" />
</body>
<script>
document.getElementById("btnGet").addEventListener("click",function(){
var element = document.querySelector(".test");
alert(element.textContent);
})
</script>
複製代碼
兩個class
都包含「test」的元素,一個在文檔樹的前面,可是它在第三級,另外一個在文檔樹的後面,但它在第一級,經過querySelector
獲取元素時,它經過深度優先搜索,拿到文檔樹前面的第三級的元素。 運行結果:
語法
var elementList = document.querySelectorAll(selectors);
複製代碼
elementList
是一個靜態的NodeList
類型的對象selectors
是一個由逗號鏈接的包含一個或多個CSS選擇器的字符串selectors
參數中包含CSS僞元素,則返回一個空的elementList
例子:
var matches = document.querySelectorAll("div.note, div.alert");
複製代碼
返回一個文檔中全部的class
爲"note"
或者"alert"
的div
元素
<body>
<div class="test">
class爲test
</div>
<div id="test">
id爲test
</div>
<input id="btnShow" type="button" value="顯示內容" />
</body>
<script>
document.getElementById("btnShow").addEventListener("click",function(){
var elements = document.querySelectorAll("#test,.test");
for(var i = 0,length = elements.length;i<length;i++){
alert(elements[i].textContent);
}
})
</script>
複製代碼
這段代碼經過querySelectorAll
,使用id選擇器和class選擇器選擇了兩個元素,並依次輸出其內容。要注意兩點:
(1)querySelectorAll也是經過深度優先搜索,搜索的元素順序和選擇器的順序無關
(2)返回的是一個非即時的NodeList,也就是說結果不會隨着文檔樹的變化而變化
兼容性問題:querySelector
和querySelectorAll
在ie8如下的瀏覽器不支持。
運行結果:
在html文檔中的每一個節點之間的關係均可以當作是家譜關係,包含父子關係,兄弟關係等等
每一個節點都有一個parentNode屬性,它表示元素的父節點。Element的父節點多是Element,Document或DocumentFragment
返回元素的父元素節點,與parentNode的區別在於,其父節點必須是一個Element,若是不是,則返回null
返回一個即時的NodeList,表示元素的子節點列表,子節點可能會包含文本節點,註釋節點等
一個即時的HTMLCollection,子節點都是Element,IE9如下瀏覽器不支持 children
屬性爲只讀屬性,對象類型爲HTMLCollection,你可使用elementNodeReference.children[1].nodeName
來獲取某個子元素的標籤名稱
只讀屬性返回樹中節點的第一個子節點,若是節點是無子節點,則返回 null
返回當前節點的最後一個子節點。若是父節點爲一個元素節點,則子節點一般爲一個元素節點,或一個文本節點,或一個註釋節點。若是沒有子節點,則返回null
返回一個布爾值,代表當前節點是否包含有子節點.
返回當前節點的前一個兄弟節點,沒有則返回null
Gecko內核的瀏覽器會在源代碼中標籤內部有空白符的地方插入一個文本結點到文檔中.所以,使用諸如Node.firstChild
和Node.previousSibling
之類的方法可能會引用到一個空白符文本節點, 而不是使用者所預期獲得的節點
previousElementSibling
返回當前元素在其父元素的子元素節點中的前一個元素節點,若是該元素已是第一個元素節點,則返回null
,該屬性是隻讀的。注意IE9如下瀏覽器不支持
Node.nextSibling
是一個只讀屬性,返回其父節點的childNodes
列表中緊跟在其後面的節點,若是指定的節點爲最後一個節點,則返回null
Gecko內核的瀏覽器會在源代碼中標籤內部有空白符的地方插入一個文本結點到文檔中.所以,使用諸如Node.firstChild
和Node.previousSibling
之類的方法可能會引用到一個空白符文本節點, 而不是使用者所預期獲得的節點
nextElementSibling
返回當前元素在其父元素的子元素節點中的後一個元素節點,若是該元素已是最後一個元素節點,則返回null
,該屬性是隻讀的。注意IE9如下瀏覽器不支持
設置指定元素上的一個屬性值。若是屬性已經存在,則更新該值; 不然將添加一個新的屬性用指定的名稱和值
語法
element.setAttribute(name, value);
複製代碼
其中name
是特性名,value
是特性值。若是元素不包含該特性,則會建立該特性並賦值。
例子:
<body>
<div id="div1">ABC</div>
</body>
<script>
let div1 = document.getElementById("div1");
div1.setAttribute("align", "center");
</script>
複製代碼
運行結果:
若是元素自己包含指定的特性名爲屬性,則能夠世界訪問屬性進行賦值,好比下面兩條代碼是等價的:
element.setAttribute("id","test");
element.id = "test";
複製代碼
getAttribute()
返回元素上一個指定的屬性值。若是指定的屬性不存在,則返回null
或""
(空字符串)
語法
let attribute = element.getAttribute(attributeName);
複製代碼
attribute
是一個包含attributeName
屬性值的字符串。attributeName
是你想要獲取的屬性值的屬性名稱
例子:
<body>
<div id="div1">ABC</div>
</body>
<script>
let div1 = document.getElementById("div1");
let align = div1.getAttribute("align");
alert(align);
</script>
複製代碼
運行結果:
removeAttribute()
從指定的元素中刪除一個屬性
語法
element.removeAttribute(attrName)
複製代碼
attrName
是一個字符串,將要從元素中刪除的屬性名
例子:
<body>
<div id="div1" style="color:red" width="200px">ABC
</div>
</body>
<script>
let div = document.getElementById("div1")
div.removeAttribute("style");
</script>
複製代碼
在運行以前div
有個style="color:red"
的屬性,在運行以後這個屬性就被刪除了
運行結果:
Window.getComputedStyle()
方法給出應用活動樣式表後的元素的全部CSS屬性的值,並解析這些值可能包含的任何基本計算 假設某個元素並未設置高度而是經過其內容將其高度撐開,這時候要獲取它的高度就要用到getComputedStyle
語法
var style = window.getComputedStyle(element[, pseudoElt]);
複製代碼
element
是要獲取的元素,pseudoElt
指定一個僞元素進行匹配。
返回的style
是一個CSSStyleDeclaration對象。
經過style
能夠訪問到元素計算後的樣式
getBoundingClientRect
用來返回元素的大小以及相對於瀏覽器可視窗口的位置
語法
var clientRect = element.getBoundingClientRect();
複製代碼
clientRect
是一個DOMRect對象,包含left,top,right,bottom,它是相對於可視窗口的距離,滾動位置發生改變時,它們的值是會發生變化的。除了IE9如下瀏覽器,還包含元素的height和width等數據
例子:
elem.style.color = 'red';
elem.style.setProperty('font-size', '16px');
elem.style.removeProperty('color');
複製代碼
例子:
var style = document.createElement('style');
style.innerHTML = 'body{color:red} #top:hover{background-color: red;color: white;}';
document.head.appendChild(style););
複製代碼
JavaScript中的API太多了,將這些API記住並熟練使用對JavaScript的學習是有很大的幫助