文檔對象模型 DOM
DOM 是 JavaScript 操作?頁的接口,全稱為“文檔對象模型”(Document Object Model)。它的作用是將?頁轉為一個 JavaScript 對象,從而可以?腳本進行各種操作 (?如增刪內容)。
DOM 的最小組成單位叫做節點(node)。?檔的樹形結構(DOM樹),就是由各種不同類型的節點組成。
節點的類型有七種
瀏覽器提供?個原生的節點對象 Node ,下面這七種節點都繼承了 Node ,因此具有?些共同的屬性和?法。最常用的是document對象和Element對象。
- Document :整個文檔樹的頂層節點
- DocumentType : doctype 標簽(?如 <!DOCTYPE html> )
- Element :?頁的各種HTML標簽(比如 <body> 、 <a> 等)
- Attribute :?頁元素的屬性(?如 class="right" )
- Text :標簽之間或標簽包含的?本
- Comment :注釋
- DocumentFragment :?檔的?段
document 對象
每個載入瀏覽器的HTML文檔都會成為document對象。document對象表示整個HTML頁面,是window對象的一個屬性。
document對象常用屬性
document.doctype //獲取HTML文檔的doctype聲明,例如<!DOCTYPE html>,沒有則返回null
document.title //返回當前文檔的標題,該屬性是可寫的
document.characterSet //返回渲染當前文檔的字符集,例如"UTF-8"
document.head //獲取文檔中被head節點包裹的內容
document.body //獲取文檔中被body節點包裹的內容
document.images //獲取文檔中所有圖片
document.readyState // 返回當前文檔的狀態,共有三種可能的值
//1. loading:加載HTML代碼階段,尚未完成解析
//2. interactive:加載外部資源階段
//3. complete:全部加載完成
document.compatMode 屬性返回瀏覽器處理文檔的模式,共有兩種可能的值
//1. BackCompat:向后兼容模式,也就是沒有添加DOCTYPE
//2. CSS1Compat:嚴格模式,添加了DOCTYPE
document.activeElement //返回當前文檔中獲得焦點的那個元素
//用戶通常可以使用tab鍵移動焦點,使用空格鍵激活焦點
document.location //返回一個只讀對象,提供了當前文檔的URL信息
document.location === location //true
document.location === window.location //true
// 假定當前網址為http://user:passwd@www.example.com:4097/path/a.html?x=111#part1
document.location.href // "http://user:passwd@www.example.com:4097/path/a.html?x=111#part1"
document.location.protocol // "http:"
document.location.host // "www.example.com:4097"
document.location.hostname // "www.example.com"
document.location.port // "4097"
document.location.pathname // "/path/a.html"
document.location.search // "?x=111"
document.location.hash // "#part1"
document.location.user // "user"
document.location.password // "passed"
document.location.assign('http://www.google.com') //跳轉到另一個網址
document.location.reload(true) // 優先從服務器重新加載
document.location.reload(false) // 優先從本地緩存重新加載(默認值)
document.location.assign('http://www.google.com') // 跳轉到另一個網址,但當前文檔不保留在history對象中,即無法用后退按鈕,回到當前文檔
document.location.toString() // 將location對象轉為字符串,等價于document.location.href
//雖然location屬性返回的對象是只讀的,但是可以將指定的URL賦值給這個屬性!網頁就會自動跳轉到指定網址。
document.location = 'http://www.example.com';// 即跳轉至網頁http://www.example.com
document.cookie //獲取該文檔的cookie信息,cookie是存儲在客戶端的文本
document.getElementById('XXX').innerText //返回元素內包含的文本內容,可寫屬性,在多層次的時候會按照元素由淺到深的順序拼接其內容。
<div>
<p>
123
<span>456</span>
</p>
</div>
外層div的innerText返回內容是 "123456"
document.getElementById('XXX').innerHTML //和inerText類似,但不是返回元素的文本內容,而是返回元素的HTML結構
<div>
<p>
123
<span>456</span>
</p>
</div>
外層div的innerHTML返回內容是<p>123<span>456</span></p>
document.open(); //新建一個文檔,供write方法寫入內容。它實際上等于清除當前文檔,重新寫入內容
document.write("hello"); //向當前文檔寫入內容
document.write("world");
document.close(); //用于關閉open方法所新建的文檔。一旦關閉,write方法就無法寫入內容了
注意:
1.如果頁面已經渲染完成再調用write方法,它會先調用open方法,擦除當前文檔所有內容,然后再寫入。
2.如果在頁面渲染過程中調用write方法,并不會調用open方法。
Element對象
除了document對象,在DOM中最常用的就是Element對象了,Element對象表示HTML元素,提供了對元素標簽名、子節點以及特性的訪問。
Element 對象可以擁有類型為元素節點、文本節點、注釋節點的子節點,DOM提供了一系列的方法可以進行元素的增、刪、改、查操作。
Element對象屬性
nodeName:元素標簽名,還有個類似的tagName
nodeType:元素類型
className:類名
id:元素id
children:子元素列表(HTMLCollection)
childNodes:子元素列表(NodeList)
firstChild:第一個子元素
lastChild:最后一個子元素
nextSibling:下一個兄弟元素
previousSibling:上一個兄弟元素
parentNode、parentElement:父元素
用法舉例:
document.getElementById("MathJax_Message").nodeName //"DIV"
查詢元素 (querySelector()最佳)
getElementById()
返回匹配指定ID屬性的元素節點。
var elem = document.getElementById("test");
getElementsByClassName()
返回一個類數組的對象,包括了所有class名字符合指定條件的元素
var elements = document.getElementsByClassName(names);
document.getElementsByClassName('red test');
getElementsByClassName方法的參數,可以是多個空格分隔的class名字,返回同時具有這些節點的元素。這個方法不僅可以在document對象上調用,也可以在任何元素節點上調用。
getElementsByTagName()
getElementsByTagName方法返回所有指定標簽的元素(搜索范圍包括本身)。這個方法不僅可以在document對象上調用,也可以在任何元素節點上調用。
var paras = document.getElementsByTagName("p");
querySelector()
querySelector方法返回匹配指定的CSS選擇器的元素節點。如果有多個節點滿足匹配條件,則返回第一個匹配的節點。
即通過querySelector()使用css選擇器的方法去操作dom元素,需要注意兼容性,支持IE9及以上,對IE8部分支持
var el1 = document.querySelector(".myclass");
var el2 = document.querySelector('#myParent > [ng-click]');
注意:querySelector方法無法選中CSS偽元素。
querySelectorAll()
querySelectorAll方法返回匹配指定的CSS選擇器的所有節點,返回的是NodeList類型的對象。NodeList對象不是動態集合,所以元素節點的變化無法實時反映在返回結果中。
elementList = document.querySelectorAll(selectors);
querySelectorAll方法的參數,可以是逗號分隔的多個CSS選擇器,返回所有匹配其中一個選擇器的元素。
var matches = document.querySelectorAll("div.note, div.alert");
上面代碼返回class屬性是note或alert的div元素。
創建元素
createElement(標簽名)
createElement方法用來生成HTML元素節點。
var newDiv = document.createElement("div");
createElement方法的參數為元素的標簽名,即元素節點的tagName屬性。
如果傳入大寫的標簽名,會被轉為小寫。如果參數帶有尖括號(即<和>)或者是null,會報錯。
createTextNode()
createTextNode方法用來生成文本節點,參數為所要生成的文本節點的內容。
var newDiv = document.createElement("div");
var newContent = document.createTextNode("Hello");
上面代碼新建一個div節點和一個文本節點
createDocumentFragment()
createDocumentFragment方法生成一個DocumentFragment對象。
var docFragment = document.createDocumentFragment();
DocumentFragment對象是一個存在于內存的DOM片段,但是不屬于當前文檔,常常用來生成較復雜的DOM結構,然后插入當前文檔。這樣做的好處在于,因為DocumentFragment不屬于當前文檔,對它的任何改動,都不會引發網頁的重新渲染,比直接修改當前文檔的DOM有更好的性能表現。
修改元素&刪除元素&clone元素
appendChild()
接受一個節點對象作為參數,將其作為最后一個子節點,插入當前節點。
var newDiv = document.createElement("div");
var newContent = document.createTextNode("Hello");
newDiv.appendChild(newContent);
上面代碼將新建的文本節點插入到新建的div節點的尾部。
var p = document.createElement('p');
document.body.appendChild(p);
上面代碼新建一個<p>節點,將其插入document.body的尾部。
如果appendChild方法的參數是DocumentFragment節點,那么插入的是DocumentFragment的所有子節點,而不是DocumentFragment節點本身。返回值是一個空的DocumentFragment節點。
insertBefore()
insertBefore方法用于將某個節點插入父節點內部的指定位置。
var insertedNode = parentNode.insertBefore(newNode, referenceNode);
insertBefore方法接受兩個參數,第一個參數是所要插入的節點newNode,第二個參數是父節點parentNode內部的一個子節點referenceNode。newNode將插在referenceNode這個子節點的前面。返回值是插入的新節點newNode。
var newDiv = document.createElement("div");
var newContent = document.createTextNode("Hello");
newDiv.insertBefore(newContent, newDiv.firstChild);
newContent將插在newDiv.firstChild的前面
如果insertBefore方法的第二個參數為null,則新節點將插在當前節點內部的最后位置,即變成最后一個子節點。
var newDiv = document.createElement("div");
var newContent = document.createTextNode("Hello");
newDiv.insertBefore(newContent, null);
由于不存在insertAfter方法,如果新節點要插在父節點的某個子節點后面,可以用insertBefore方法結合nextSibling屬性模擬。
parent.insertBefore(s1, s2.nextSibling);
replaceChild()替換指定元素
replaceChild()接受兩個參數:要插入的元素和要替換的元素,返回值是替換走的那個節點oldElement。
newDiv.replaceChild(newElement, oldElement);
removeChild()移除子節點
接受一個子節點作為參數,用于從當前節點移除該子節點。返回值是移除的子節點。
parentNode.removeChild(childNode);
Node.hasChildNodes()判斷是否有子節點
hasChildNodes方法返回一個布爾值,表示當前節點是否有子節點。
var foo = document.getElementById('foo');
if (foo.hasChildNodes()) {
foo.removeChild(foo.childNodes[0]);
}
上面代碼表示,如果foo節點有子節點,就移除第一個子節點。
cloneNode()克隆一個節點
它接受一個布爾值作為參數,表示是否同時克隆子節點,false的時候只復制元素本身。它的返回值是一個克隆出來的新節點。
var cloneUL = document.querySelector('ul').cloneNode(true);
該方法有一些使用注意點。
- 克隆一個節點,會拷貝該節點的所有屬性,但是會喪失addEventListener方法和on-屬性(即node.onclick = fn),添加在這個節點上的事件回調函數。
- 該方法返回的節點不在文檔之中,即沒有任何父節點,必須使用諸如Node.appendChild這樣的方法添加到文檔之中。
- 克隆一個節點之后,DOM 有可能出現兩個有相同id屬性(即id="xxx")的網頁元素,這時應該修改其中一個元素的id屬性。如果原節點有name屬性,可能也需要修改。
contains()判斷是否包含和這個節點
document.body.contains(node)
返回一個布爾值,表示參數節點是否滿足以下三個條件之一。
- 參數節點為當前節點。
- 參數節點為當前節點的子節點。
- 參數節點為當前節點的后代節點。
document.body.contains(node)
上面代碼檢查參數節點node,是否包含在當前文檔之中。
HTMLCollection 和 NodeList
節點都是單個對象,有時需要一種數據結構,能夠容納多個節點。DOM 提供兩種節點集合,用于容納多個節點:NodeList和HTMLCollection。
NodeList 對象代表一個有順序的節點列表,HTMLCollection 是一個接口,表示 HTML 元素的集合,它提供了可以遍歷列表的方法和屬性。都是類數組對象, NodeList 有 forEach 方法,而 HTMLCollection 沒有。
以下方法獲取HTMLCollection對象實例
document.images //所有img元素
document.links //所有帶href屬性的a元素和area元素
document.forms //所有form元素
document.scripts //所有script元素
document.body.children
document.getElementsByClassName("class1")
以下方法獲取NodeList對象實例
Node.childNodes
document.querySelectorAll()
document.getElementsByTagName()
document.getElementsByName("name1")
document.body.childNodes instanceof NodeList // true
NodeList實例是一個類數組對象,具有length屬性和forEach方法(也可以用for遍歷),可以使用方括號運算符取出成員。但不能使用pop或push之類數組特有的方法。
var children = document.body.childNodes;
Array.isArray(children) // false
children.length // 34
children.forEach(console.log)
document.body.childNodes[0] //返回第一個成員
如果NodeList實例要使用數組方法,可以使用Array.prototype.slice.call()將其轉為真正的數組。
var children = document.body.childNodes;
var nodeArr = Array.prototype.slice.call(children);
注意:NodeList 實例可能是動態集合,也可能是靜態集合。所謂動態集合就是一個活的集合,DOM 刪除或新增一個相關節點,都會立刻反映在 NodeList 實例。目前,只有Node.childNodes返回的是一個動態集合,其他的 NodeList 都是靜態集合。
var children = document.body.childNodes;
children.length // 18
document.body.appendChild(document.createElement('p'));
children.length // 19
上面代碼中,文檔增加一個子節點,NodeList 實例children的length屬性就增加了1。
NodeList.prototype.length
length屬性返回NodeList 實例包含的節點數量。
document.getElementsByTagName('xxx').length
NodeList.prototype.forEach()
forEach方法用于遍歷 NodeList 的所有成員。它接受一個回調函數作為參數,每一輪遍歷就執行一次這個回調函數,用法與數組實例的forEach方法完全一致。
var children = document.body.childNodes;
children.forEach(function(item,i,list){})
回調函數的三個參數依次是當前元素、當前元素索引值、整個NodeList 實例
NodeList.prototype.keys(),NodeList.prototype.values(),NodeList.prototype.entries()
keys()返回鍵名的遍歷器,values()返回鍵值的遍歷器,entries()返回的遍歷器同時包含鍵名和鍵值的信息。都可以通過for...of循環遍歷獲取每一個成員的信息。
var children = document.body.childNodes;
for (var key of children.keys()) {
console.log(key);
}
// 0
// 1
// 2
// ...
for (var value of children.values()) {
console.log(value);
}
// #text
// <script>
// ...
for (var entry of children.entries()) {
console.log(entry);
}
// Array [ 0, #text ]
// Array [ 1, <script> ]
// ...
屬性操作
getAttribute()獲取元素的attribute值(一個標簽的屬性名和屬性值可以自己定義)
var node = document.querySelector('p')
node.getAttribute('id');
setAttribute(屬性名,屬性值) 設置元素屬性
var node = document.getElementById("div1");
node.setAttribute("my_attrib", "newVal");
romoveAttribute() 刪除元素屬性
node.removeAttribute('id');
常見使用方式
樣式的改變建議使用 class 的新增刪除來實現
Element.classList方法: 操作元素的class
var nodeBox = document.querySelector('.box')
console.log( nodeBox.classList )
nodeBox.classList.add('active') //新增 class
nodeBox.classList.remove('active') //刪除 class
nodeBox.classList.toggle('active') //新增/刪除切換(即如果類存在,則刪除它并返回false,如果不存在,則添加它并返回true。)
node.classList.contains('active') // 判斷是否存在指定的class
Element.classList.item ( Number ) //按集合中的索引返回class值
頁面寬高
網頁可見區域寬: document.body.clientWidth;
網頁可見區域高: document.body.clientHeight;
網頁可見區域寬: document.body.offsetWidth (包括邊線的寬);
網頁可見區域高: document.body.offsetHeight (包括邊線的寬);
網頁正文全文寬: document.body.scrollWidth;
網頁正文全文高: document.body.scrollHeight;
網頁被卷去的高: document.body.scrollTop;
網頁被卷去的左: document.body.scrollLeft;
網頁正文部分上: window.screenTop;
網頁正文部分左: window.screenLeft;
屏幕分辨率的高: window.screen.height;
屏幕分辨率的寬: window.screen.width;
屏幕可用工作區高度: window.screen.availHeight;
屏幕可用工作區寬度:window.screen.availWidth;
Element.scrollHeight: 只讀屬性,是一個元素內容高度的度量
Element.scrollTop:獲取或設置一個元素的內容垂直滾動的像素數
scrollWidth:獲取對象的滾動寬度
offsetHeight:獲取對象相對于版面或由父坐標 offsetParent 屬性指定的父坐標的高度
offsetTop:獲取對象相對于版面或由 offsetTop 屬性指定的父坐標的計算頂端位置
event.clientX 相對文檔的水平座標
event.clientY 相對文檔的垂直座標
event.offsetX 相對容器的水平坐標
event.offsetY 相對容器的垂直坐標
event.clientX+document.documentElement.scrollTop 相對文檔的水平座標+垂直方向滾動的量