DOM簡單來說就是一套js訪問Document對象的一套增刪改查規(guī)則,文檔由許多節(jié)點構成,需要重點關注的有Element、Document、Text、Attribute四種節(jié)點。
每種節(jié)點具有一些公共的屬性,包括nodeType、nodeName、nodeValue、parentNode、nextSibling、previousSibling、nextElementSibling、previousElementSibling、ownerDocument、childNodes(類數(shù)組對象),其中 parentNode、nextSibling、previousSibling、firstChild、lastChild、nextElementSibling、previousElementSibling、firstElementChild、lastElementChild 如果不存在則為 null
var parent1 = document.getElementById("parent1");
var div1 = document.getElementById("div1");
var div2 = document.getElementById("div2");
console.log(div1.nodeName); //DIV,自動轉為大寫
console.log(div1.nodeType); //1
console.log(div1.nodeValue); //null
每個節(jié)點都有一個childNodes屬性,保存著一個NodeList對象。NodeList對象是一個類數(shù)組對象,動態(tài)的、實時的
console.log(parent1.childNodes[1]); //<div id="div1">This is a div</div>,在chrome中換行也被當做了一個節(jié)點,所以在獲取node節(jié)點時,最好先判斷一下nodeType
//上面代碼等價于下面代碼
console.log(parent1.childNodes.item(1)); //<div id="div1">This is a div</div>
//非IE8以下的寫法
var arr = Array.prototype.slice.call(someNode.childNodes, 0); //childNodes轉化為數(shù)組對象
var newarr = Array.prototype.slice.call(arguments, 0); //arguments類數(shù)組轉化為數(shù)組對象
//可以用下面的代碼將一個nodeList轉化為一個數(shù)組對象,兼容IE8
function convertToArray(nodes1) {
var array1 = null;
try {
array1 = Array.prototype.slice.call(nodes1, 0); //針對非IE瀏覽器
} catch (ex) {
array1 = new Array();
for (var i = 0; i < array1.length; i++) {
array1.push(nodes1[i]);
}
}
return array1;
}
console.log(div1.nextSibling); //#text
操作節(jié)點的方法有
someNode.appendChild(newNode) 末尾追加節(jié)點,返回追加的節(jié)點
someNode.insertBefore(newNode,null) 在parent節(jié)點的子節(jié)點的某個位置插入節(jié)點,返回插入的節(jié)點,如果后一個參數(shù)為null,則效果等同appendChild()
someNode.replaceChild(newNode, someNode.firstChild) 在parent節(jié)點的子節(jié)點中用新節(jié)點替換子節(jié)點中的一項
someNode.removeChild(someNode.firstChild) 在parent節(jié)點的子節(jié)點中移除
cloneNode(true) 克隆節(jié)點,不克隆事件,IE會復制事件,所以在復制節(jié)點前,先移除事件;當參數(shù)為true時,復制子節(jié)點
var div3 = document.createElement("div");
var returnedNode1 = div1.appendChild(div3);
console.log(returnedNode1 == div3); //true
console.log(div1.lastChild == div3); //true
傳入文檔的新節(jié)點是現(xiàn)在文檔的一部分,則結果是將該節(jié)點從原來的位置轉移到新位置。任何DOM節(jié)點不能同時出現(xiàn)在文檔的多個位置上。
var returnedNode2 = parent1.appendChild(parent1.firstChild);
console.log(returnedNode2 == parent1.firstChild); //false
console.log(returnedNode2 == parent1.lastChild); //true
var returnedNode3 = parent1.insertBefore(div3, null); //插入成為最后一個節(jié)點
console.log(div3 == parent1.lastChild); //true
var returnedNode4 = parent1.insertBefore(div3, parent1.firstChild); //返回插入的元素
console.log(returnedNode4 == div3); //true;
console.log(div3 == parent1.firstChild); //true
//替換節(jié)點replaceChild() 移除節(jié)點removeChild()
var returnedNode5 = parent1.removeChild(div3, parent1.firstChild);
console.log(returnedNode5); //<div></div>
var returnedNode6 = parent1.removeChild(parent1.lastChild);
console.log(returnedNode6); //<div id="div1">This is a div</div>
var ul1 = document.getElementById("ul1");
var deepList = ul1.cloneNode(true);
console.log(deepList); //<ul id="ul1"><li>item1</li><li>item2</li><li>item3</li></ul>
var shallowList = ul1.cloneNode();
console.log(shallowList); //<ul id="ul1"></ul>
Document類型
document實例 》 HTMLDocument類型 》 Document類型
JS通過Document類型表示文檔。在瀏覽器中,document對象是HTMLDocument(繼承自Document類型)的一個實例,表示整個HTML頁面
document也是window的一個屬性,可以直接訪問
var html1 = document.documentElement; //獲取對<html>的引用
var html2 = document.childNodes[0]; //通過childNodes間接獲取
var body1 = document.body; //直接獲取body
var doctype1 = document.doctype; //取得對<!DOCTYPE>的引用,有兼容性問題
var originTitle1 = document.title;
var url1 = document.URL; //獲取完整url
var domain1 = document.domain; //獲取一級域名例:www.baidu.com
var referrer1 = document.referrer;
document.write();
document.writeIn();
Element元素
HTML元素 》 HTML子類型 》 HTMLElement類型 》 Element類型
查找元素的方法
docuement.getElementById();
document.getElementsByTagName(); 返回一個HTMLCollection對象,也是一個動態(tài)集合。HTMLCollection有l(wèi)ength屬性、item()、namedItem()方法
特性
所有HTML元素都由HTMLElement類型表示,不是直接通過這個類型,也是通過它的子類型來表示。HTMLElement類型直接繼承自Element并添加了一些屬性。添加的這
些屬性分別對應于每個HTML元素中都存在的下列標準特性。
id
title
lang
dir
className
取得特性
getAttribute(); 可以取得自定義特性。
setAttribute(); 設置的特性名會自動轉化為小寫
removeAttribute();
根據(jù)HTML5規(guī)范,自定義特性應該加上data-前綴以便驗證。最上面的五種直接用js的方法來操作比較快,其他自定義屬性用getAttribute 或者setAttribute等方法
通過js的方法來操作屬性
var div = document.getElementById("div1");
console.log(div.id, div.className, div.title, div.lang, div.dir);
//設置
div.id = "newDiv";
//通過node節(jié)點的方式來獲取
div.getAttribute("id");
div.getAttribute("class");
//獲取自定義特性
div.getAttribute("data-selfAttribute");
有兩類特殊的屬性,他們雖然有對應的屬性名,但是屬性的值與通過getAttribute()返回的值并不相同,即style和onclick處理函數(shù)
getAttribute()返回的值是字符串;
.style返回的是一個對象
創(chuàng)建元素
var div = document.createElement("div"); //傳入標簽名
var div = document.createElement("<div class=\"aaaa\">hello world!</div>"); //傳入一段html文本
//遍歷時的注意事項
for (var i = 0; var len = element.childNodes.length;i < len;i++){
if (element.childNodes[i].nodeType == 1) {
//do something ,先判斷類型再執(zhí)行操作
}
}
操作文本節(jié)點的方法有
appendData(text):將text添加到節(jié)點末尾
deleteData(offset,count):從offset指定的位置開始刪除count個字符
insertData(offset,text):在offset處插入指定的字符串
replaceData(offset,count,text):用text替換從fosset指定的位置開始到offset+count出的字符串
splitText(offset):從offset指定的位置將當前文本節(jié)點分為兩個文本節(jié)點。
substringData(offset,count):提取從offset指定的位置開始到fooset+count位置處的字符串
//創(chuàng)建文本節(jié)點并添加到div中
var div = document.createElement("div");
div.className = "aaa";
var text = document.createTextNode("Hello world!");
div.appendChild(text);
document.body.appendChild(div);
通過normallize和splitText兩個方法來拼合文本節(jié)點和打斷文本節(jié)點
var element = document.createElement("div");
element.className = "message";
var textNode1 = document.createTextNode("This is the first node, ");
element.appendChild(textNode1);
var textNode2 = document.createTextNode("This is the another node");
element.appendChild(textNode2);
document.body.appendChild(element);
console.log(element.childNodes.length); //2
element.normalize();
console.log(element.childNodes.length); //1
console.log(element.firstChild.nodeValue); //This is the first node, This is the another node,
element.firstChild.splitText(10); //注意操作的是子節(jié)點
console.log(element.childNodes.length); //2
console.log(element.firstChild); //This is th
document.querySelector();
document.querySelectorAll()
//跨瀏覽器遍歷所有元素
var i,
len,
child = element.firstChild;
while (child != element.lastChild) {
if (child.nodeType === 1) {
processChild(child);
}
child = child.nextSibling;
}
//使用element traversal遍歷
var i,
len,
child = element.firstElementChild;
while (child != element.lastElementChild) {
processChild(child);
child = child.nextElementSibling;
}
HTML5新增一種該操作類名的方式classList,它是一個類似于nodelist的對象,有屬性,有方法
length
add(value):將給定的字符串值添加到類表中,如果值已經(jīng)存在了,就不添加了
contains(value):表示列表中是否存在給定的值,如果存在則返回true,否則返回false。
remove(value):從列表中刪除給定的字符串。
toggle(value):
div.classList.remove("disabled");
div.classList.add("current");
div.classList.toggle("user");
確定元素中是否包含既定的類名
if (div.classList.contains("bd") && !div.classList.contains("disabled")) {
//執(zhí)行操作
}
//迭代類名
for (var i = 0; len = div.classList.length; i < len;i++){
doSomthing(div.classList[i]);
}
//動態(tài)腳本設置
function loadScript(url){
var script = document.createElement("script");
script.type = "text/javascript";
script.src = url;
document.body.appendChild(script);
}
loadScript("client.js"); //不知道何時加載完畢
//動態(tài)樣式表
function loadStyles(url) {
var link = document.createElement("link");
link.rel = "stylesheet";
link.type = "text/css";
link.href = url;
var head = document.getElementsByTagName("head")[0];
head.appendChild(link);
}
loadStyles("client.css") //不知道何時加載完畢
HTML5單獨增加的API
document.querySelector() 根據(jù)css規(guī)則來選擇元素,返回第一個匹配元素,沒有則返回null
document.querySelectorAll() 根據(jù)css規(guī)則來選擇元素,返回一個 NodeList 對象,沒有則返回空的 NodeList
matchesSelector() 如果調用元素與該選擇符匹配,則返回true,否則,返回false;例子:
if(document.body.matchesSelector("body.page1")){ //true}
getElementsByClassName("className1 className2 ...") 通過className選取元素,返回nodeList對象
classList屬性 一個class的屬性對象。擁有add(value) contains(value) remove(value) toggle(value) 四個方法,方便操作class
document.activeElement屬性 始終指向DOM當中當前獲得了焦點的元素
默認情況下,文檔剛剛加載完成時,document.activeElement中保存的是document.body元素的引用。文檔加載期間,document.activeElement的值為null。
document.hasFocus() 確定文檔是否獲得了焦點,頁面中的元素獲得了焦點,再調用document.hasFocus() 返回true,代表用戶正在與頁面交互
readyState屬性 文檔是否已經(jīng)加載完畢的指示器,其值有兩個loading和complete 示例:
if(document.readyState == "complete"){ //... }
compatMode兼容模式屬性 標準模式其值為CSS1Compat,混雜模式為BackCompat 示例:
if(document.compatMode == "CSS1Compat"){//...}else {/...}
document.head屬性 直接獲取head引用,示例:
var head = document.head || document.getElementsByTagName("head")[0]
document.charset字符集屬性
data- 自定義數(shù)據(jù)屬性,目的是提供與渲染無關的是信息,或者提供語義信息。 設置好之后,可以通過元素的dataset屬性來訪問
如果是駝峰寫法,則會自動將駝峰分開來,轉變成小寫。
innerHTML 對應元素的子元素,設置值時只會替換子節(jié)點的內容,盡量把所有元素拼接成字符串后再append
outterHTML 設置時,會替換節(jié)點本身
scrollIntoView()方法 針對容器 該方法可以在所有HTML元素上調用,通過滾動瀏覽器窗口或某個容器元素,調用元素就可以出現(xiàn)再是口中。如果傳入true,則調用元素會盡可能頂部與視口平齊。如果傳入false,則調用元素會近可能全部出現(xiàn)在視口中,頂部不一定平齊。
contains方法 判斷某個節(jié)點是不是另一個節(jié)點的后代 示例:
* alert(document.documentElement.contains(document.body)); //true
下面三個方法,safari 和 chrome實現(xiàn)了
scrollIntoViewIfNeeded(alignCenter) 針對容器 只在當前元素再視口中公布可見的情況下,才滾動瀏覽器窗口或容器元素 alignCenter 設置為true時,會盡可能出現(xiàn)在視口中部
scrollByLines(lineCount) 針對元素 將元素的內容滾動指定的行高,正值和負值均可
scrollByPages(pageCount) 針對元素 將元素的內容滾動指定的頁面高度,具體高度有元素的高度決定。
myDiv.style 獲取元素樣式,其中style里面包含的只有行間樣式,沒有嵌套和引用的
document.defaultView.getComputedStyle(element,null).color 獲取計算后的樣式,包括瀏覽器默認樣式, 第二個參數(shù)通常設置為null
cuttentStyle屬性 IE中不支持getComputedStyle 方法,但是有相同的屬性來獲取計算后的樣式,注意border不能一次性獲取,因為有四條邊
offsetHeight 包括上下border padding
offsetWidth
offsetTop 元素的左外邊框至包含元素的左內邊框之間的像素距離,其中offsetLeft 和 offsetTop 屬性與包含元素有關,
包含元素的引用保存再offsetParent屬性中。offsetParent 屬性不一定與parentNode的值相等。例如td元素的offsetParent 是
作為其祖先元素的table,因為table是DOM層次中距離td最近的一個具有大小的元素
offsetLeft
clientHeight 包含內容、上下padding,不包含上下border
clientWidth
scrollHeight 不包含滾動條,元素內容的總高度
scrollWidth
scrollLeft 被隱藏在padding左邊的內容寬度,包含border
scrollTop
//自定義屬性示例:
var div = document.getElementById("mydiv");
//設置自定義屬性
div.dataset.appId = "45124";
//獲取自定義屬性
var appId = div.dataset.appId;
var myName = div.dataset.myName;
焦點管理
HTML5添加了輔助管理DOM焦點的功能。首先就是,這個屬性始終會引用DOM中當前獲得焦點的元素。
元素獲得焦點的方式有頁面載入、用戶輸入和在代碼中調用focus()方法
var button = document.getElementById("myButton");
button.focus();
alert(document.activeElement === button); //true
var button = document.getElementById("myButton");
button.focus();
alert(document.hasFocus()); //true,證明用戶正在與頁面交互
function getElementLeft(element) {
var actualLeft = element.offsetLeft;
var current = element.offsetParent; //offsetParent相鄰父元素
while (current !== null) {
actualLeft += current.offsetLeft;
current = current.offsetParent;
}
return actualLeft;
}
function getElementTop(element) {
var actualTop = element.offsetTop;
var current = element.offsetParent;
while (current !== null) {
actualTop += current.offsetTop;
current = current.offsetParent;
}
return actualTop;
}
/*獲取客戶區(qū)大小*/
function getViewport() {
if (document.compatMode == "BackCompat") {
return {
width: document.body.clientWidth,
height: document.body.clientHeight
}
} else {
return {
width: document.documentElement.clientWidth,
height: document.documentElement.clientHeight
}
}
}
//確定文檔總高度
var docHeight = Math.max(document.documentElement.scrollHeight, document.documentElement.clientHeight);
var docWidth = Math.max(document.documentElement.scrollWidth, document.documentElement.clientWidth);
function scrollToTop(element) {
if (element.screenTop != 0) {
element.screenTop = 0;
}
}
//確定元素大小
function getBoundingClientRect(element) {
var scrollTop = document.documentElement.scrollTop;
var scrollLeft = document.documentElement.scrollLeft;
if (element.getBoundingClientRect) {
if (typeof arguments.callee.offset != "number") {
var temp = document.createElement("div");
temp.style.cssText = "position: absolute; left: 0; top: 0;";
document.body.appendChild(temp);
arguments.callee.offset = -temp.getBoundingClientRect().top - scrollTop;
document.body.removeChild(temp);
temp = null;
}
var rect = element.getBoundingClientRect();
var offset = arguments.callee.offset;
return {
left: rect.left + offset,
right: rect.right + offset,
top: rect.top + offset,
bottom: rect.bottom + offset
}
} else {
var actualLeft = getElementLeft(element);
var actualTop = getElementTop(element);
return {
left: actualLeft - scrollLeft,
right: actualLeft + element.offsetWidth - scrollLeft,
top: actualTop - scrollTop,
bottom: actualTop + element.offsetHeight - scrollTop
}
}
}