通俗易懂的來說講DOM

DOM是全部前端開發天天打交道的東西,可是隨着jQuery等庫的出現,大大簡化了DOM操做,致使你們慢慢的「遺忘」了它的原本面貌。不過,要想深刻學習前端知識,對DOM的瞭解是不可或缺的,因此本文力圖系統的講解下DOM的相關知識,若有遺漏或錯誤,還請你們指出一塊兒討論^ ^。css

 

1、DOM是什麼?html

DOM(文檔對象模型)是針對HTML和XML文檔的一個API,經過DOM能夠去改變文檔。前端

這個說法很官方,你們確定仍是不明白。node

舉個例子:咱們有一段HTML,那麼如何訪問第二層第一個節點呢,如何把最後一個節點移動到第一個節點上面去呢?chrome

DOM就是定義了若是作相似操做,那麼應該怎麼作的標準。好比用getElementById來訪問節點,用insertBefore來插入節點。跨域

當瀏覽器載入HTML時,會生成相應的DOM樹。數組

簡而言之,DOM能夠理解爲一個訪問或操做HTML各類標籤的實現標準。瀏覽器

 

對於一個HTML來講,文檔節點Document(看不到的)是它的根節點,對應的對象即是document對象(嚴格講是子類HTMLDocument對象,下面單獨介紹Document類型時會指出)。app

換句話說存在一個文檔節點Document,而後它有子節點,好比經過document.getElementsByTagName("html"),獲得類型爲元素節點的Element html。dom

 

每一段HTML標記均可以用相應的節點表示,例如:

HTML元素經過元素節點表示,註釋經過註釋節點表示,文檔類型經過文檔類型節點表示等。

一共定義了12種節點類型,而這些類型又都繼承自Node類型。

因此咱們首先講Node類型,由於這個類型的方法是全部節點都會繼承的。

 

2、Node類型(基類,全部節點都繼承了它的方法)

Node是全部節點的基類型,全部節點都繼承自它,因此全部節點都有一些共同的方法和屬性。

 

先講Node類型的屬性

首先是nodeType屬性,用來代表節點類型的,例如:

document.nodeType;    // 返回 9 ,其中document對象爲文檔節點Document的實例

這裏面,9表明的就是DOCUMENT_NODE節點的意思,能夠經過Node.DOCUMENT_NODE查看節點對應的數字

document.nodeType === Node.DOCUMENT_NODE;    // true

至於一共有哪些節點,每一個節點對應的數字又是多少,這個能夠問谷歌就知道了。反正經常使用的就是元素節點Element(對應數字爲1)和文本節點Text(對應數字爲3) 

 

而後經常使用的還有nodeName和nodeValue

對於元素節點 nodeName就是標籤名,nodeValue就是null

對於文本節點 nodeName爲"#text"(chrome裏面測試的),nodeValue就是實際的值

 

每一個節點還有childNodes屬性,這是個十分重要的屬性,它保存了這個節點全部直接子元素

調用childNodes返回的是一個NodeList對象,它極其像數組,可是有一個最關鍵的地方,它是動態查詢的,也就是說每次調用它都會對DOM結構查詢,因此對它的使用須要慎重,注意性能。

 

訪問childNodes可使用數組下表或者item方法

而後各個節點還存在各類屬性讓它們能夠相互訪問,下圖很好的總結了

比較有用的方法和屬性:

一、hasChildNodes()

      若是包含子節點就返回true,比查詢childNodes的length來的簡單。

二、ownerDocument

      返回文檔節點的引用(在html裏面也就是document對象)

 

再介紹下Node類型經常使用的方法

appendChild()方法能夠在節點的childNodes的末尾增長一個節點,值得注意的是若是這個節點是已經存在在文檔中的,那麼便會刪除原節點,感受上就像是移動節點同樣。

 

insertBefore()方法接受兩個參數,一個是插入的節點,另一個是參照的節點。若是第二個參數爲null,則insertBefore和appendChild效果同樣。不然便會把節點插入到參照節點以前。這裏要注意的是,若是第二個參數不爲null,那麼插入的節點不能是已經存在的節點。

 

replaceChild()方法能夠替換節點,接受兩個參數,須要插入的節點和須要替換的節點。返回被替換掉的節點。

 

removeChild()移除節點。這裏有個常見需求,好比我有一個節點 #waste-node ,那麼如何移除它呢?

var wasteNode =  document.getElementById("waste-node");
wasteNode.parentNode.removeClhid(wasteNode);    // 先拿到父節點,再調用removeClild刪除本身

這裏先暫停一下,不知道你們注意到沒有,以上的幾個方法都是操做某個節點的子節點,也就是說,操做前必須找到父節點(經過parentNode來找)

 

接下來講下複製節點的方法:

cloneNode();複製節點,接受一個參數 true或者false。若是true就是複製那個節點和它的子節點。若是是false,就是複製節點自己(複製出來的節點就會沒有任何子元素)。這個方法返回複製的節點,若是須要操做它,那麼須要藉助前面講的4個方法來把這個節點放入到html中去。

 

至此,Node類型的常見屬性和方法都介紹完了。結合開頭講的,全部節點類型都繼承自Node類型,因此這些方法是全部節點都有的。

 

 

3、Document類型

最開始講DOM是什麼的時候提到了Document類型。其實關於這個類型最重要的是它的一個子類HTMLDocument有一個實例對象document。而這個document對象是咱們最經常使用的一個對象了。

document對象又掛載在window對象上,因此在瀏覽器就能夠直接訪問document了。

 

老規矩,先講講document對象的屬性,等會講講它的方法。

document對象上的一些屬性

document.childNodes 繼承自上面講的Node類型,能夠返回文檔的直接子節點(一般包括文檔聲明和html節點)

document.documentElement 能夠直接拿到html節點的引用(等價於document.getElementsByTagName("html")[0])。

document.body body節點的引用

 

document.title  頁面的title,能夠修改,會改變瀏覽器標籤上的名字

document.URL 頁面的url

document.referrer 取得referrer,也就是打開這個頁面的那個頁面的地址,作來源統計時候比較有用

document.domain 取得域名,能夠設置,可是一般只能設置爲不包含子域名的狀況,在一些子域名跨域狀況下有效。

 

接下來介紹兩個熟悉的方法

getElementById 和 getElementsByTagName

getElementById,傳入id,獲得元素節點。裏面的參數區分大小寫(IE8-不區分)。注意:若是有多個id相同的元素,則返回第一個IE7-裏面表單元素的name也會被當作id來使用。

getElementsByTagName 根據標籤取得元素,獲得的是HTMLCollection類型。若是傳入的是 "*" ,則能夠取得所有元素。

還有一個是隻有HTMLDocument類型(也就是document對象)纔有的方法 getElementsByName 顧名思義,根據name返回元素。

document對象還有一些集合,例如document.forms 能夠返回全部的form表單。類型也是HTMLCollection。

 

說到HTMLCollection,就再說說它

HTMLCollection就是一個包含一個或多個元素的集合,和上面講的NodeList還挺像的。HTMLCollection這個類型有兩個方法,一個是經過下標(或者.item())獲得具體元素,還有就是經過['name'](或者.namedItem())得到具體元素。

 

最後,關於document對象還有一套重要的方法,那即是

write() writeln() open() close()

open和close分別是打開和關閉網頁的輸出流,在頁面加載過程當中,就至關於open狀態。這兩個方法通常不會去用它。

而後重要的方法就是write和writeln,它們都是向頁面寫入東西,區別就是後者會多加入一個換行符。

注意的是:在頁面加載的過程當中,可使用這兩個方法向頁面添加內容。若是頁面已經加載完了,再調用write,會重寫整個頁面。

還有一點,若是要動態寫入腳本 例如 <script>xxx</script>這樣的 ,那麼要注意把</script>分開來拼裝下,不然會被誤覺得是腳本結束的標誌,致使這個結束符匹配到上面一個開始符。能夠這樣寫"<scr" + "ipt>";

 

4、Element類型

 

接下來說講最重要也是最多見的一個類型,Element類型。

咱們平常所操做的都是Element類型(實質是HTMLElement,這裏爲了方便理解,就簡單這麼說),好比

document.getElementById("test") 

返回的就是Element類型。咱們平常所說的「DOM對象」,一般也就是指Element類型的對象。

 

而後說說這個類型的常見屬性:

首先最開始說的Node類型上的那些屬性方法它都有,這個就再也不重複了,主要說說它本身獨有的。

首先是tagName,這個和繼承自Node類型的nodeName同樣。都是返回標籤名,一般是大寫,結果取決於瀏覽器。因此在作比較

的時候最好是調用下相似toLowerCase()這種方法再作比較。

 

說說上面提到過的HTMLElement類型

HTMLElement類型繼承自Element類型,也是HTML元素的實際類型,咱們在瀏覽器裏用的元素都是這個類型。

這個類型都具備一些標準屬性,好比:

id 元素的惟一標識

title 一般是鼠標移上去時候會顯示的信息

className 類名

等等,這幾個屬性是可讀寫的,也就是說你改變他們會獲得相應的效果。

 

除了屬性外,還有幾個重要的方法

首先說說操做節點屬性的方法

getAttribute 、setAttribute 、removeAttribute這3個方法。

這些是操做屬性最經常使用的方法了,怎麼用就不說了,很簡單,顧名思義。

還有一個attributes屬性,保存了元素的所有屬性。

 

這裏停下來,出個問題,ele.className 和 ele.getAttribute("class")返回的結果是否是同一個東西?

解答這個問題,我要說一個重要知識點,一個元素的屬性結構是這麼來的,好比一個inpnt元素

<input id="test" checked="checked">

那麼這個元素的屬性被包含在 input.attributes裏面,好比你在html元素上看到的class、id或者你本身定義的data-test這種屬性。

而後 getAttribute 、setAttribute 、removeAttribute這3個方法能夠認爲是快捷的取attributes集合的方法。而直接input.id或者input.className都是直接掛在input下的屬性,和attributes是同級的。因此返回的東西也許看過去同樣,實際是不同的,不信你能夠試試input.checked這input.getAttribute("checked")試試。

關於這個知識點,詳細的說能夠再寫一篇文章,在個人博客 從is(":checked")提及 中有談到過,你們能夠看看這篇文章和文章後的討論,即可以知道是怎麼一回事。

總得來講,這3個方法一般用了處理自定義的屬性,而不是id、class等這種「公認特性」。

 

 

接下來講說建立元素

document.createElement()能夠建立一個元素,好比:

document.createElement("div");

通常以後能夠爲元素設置屬性,兩種方法,一種是直接node.property還能夠node.setAttribute("propertyName","value")。等

可是作完這些以後,這個元素仍是沒有在頁面中,因此你還得經過最上面講的相似appendChild這些方法把元素添加到頁面裏面。

在IE中,還能夠直接穿整個HTML字符串進去,來建立元素,好比

document.createElement("<div>test</div>");

 

最後,元素節點也支持HTMLDocument類型的那些查找方法,好比getElementsByTagName。不過它只會找本身後代的節點。因此能夠這麼寫代碼

document.getElementById("test").getElementsByTagName("div");    // 找到id爲test元素下的全部div節點

 

5、Text類型

這個類型很特殊,也是第三常見類型(第一第二分別就是Document和Element)。

這個節點簡單來講就是一段字符串。

有個很重要的特徵就是,它沒有子元素(不過這個仔細想一想也知道= =)

訪問text節點的文本內容,能夠經過nodeValue或者data屬性。

下面簡單說說它提供的一些方法

appendData();    // 在text末尾加內容
deleteData(offset, count);    // 從offset指定的位置開始刪除count個字符

還有insertDate、replaceData、splitText等方法,就不一一說了,用的機會不多,能夠用的時候再查閱。

而後它還有一個lenght屬性,返回字符長度的。

 

這裏說一個常見的坑。好比下面這個html結構

<ul>
     <li></li>
     <li></li>
</ul>

這裏,ul的第一個子節點(firstChild)是什麼呢?第一眼看過去,確定認爲是li了,可是實際上,你會發現不是li,而是一個文本節點!

這是由於瀏覽器認爲ul和第一個li之間有空白字符,因此就有文本節點了。

這裏一個常見的問題就是遍歷ul的childNodes的時候,遍歷的元素必定要判斷下nodeType是否是等於1(等於1就表明是元素節點),這樣才能跳過這個坑。不然你也能夠刪除全部的空格和換行符。

 

 

建立文本節點的方法是document.createTextNode

而後接下來和操做Element類型同樣,就是再插入到元素中,瀏覽器就能夠看到了。

 

6、其餘的一些類型 Comment、DocumentType和DocumentFragment

這些不經常使用的一句話帶過把

Comment是註釋節點

DocumentType就是doctype節點,經過docment.doctype來訪問

DocumentFragment這個節點是一個文檔片斷,偶爾會用到。

好比一種常見的用法是,在一個ul中插入3個li。

若是你循環插入3次,那麼瀏覽器就要渲染3次,對性能有蠻大的影響。

因此你們通常這麼作

var fragment = document.createDocumentFragment();

而後循環把li,用appendChild插入到fragment裏面

最後在一次把fragment插入到ul裏面。這樣就會很快。

 

 

7、DOM擴展

進過上面講的這麼多節點類型,想必你們對DOM節點已經有了很深的瞭解,下面講一講DOM擴展的一些東西。

 

瀏覽器爲了方便開發者,擴展了一些DOM功能。

由於是瀏覽器本身擴展的,因此使用前兼容性問題必定要注意

 

判斷「標準模式」和「混雜模式」經過 document.compatMode和新的document.documentMode

 

上面不是說了一個文本節點做爲第一子元素的坑嗎,因此瀏覽器又實現了一個children屬性,這個屬性只包含元素節點。

 

爲了方便判斷A節點是否是B節點的子節點,引入了contains方法,好比 

B.contains(A);    // true就表明是,false就表明不是

這個方法有兼容性問題,使用前能夠谷歌解決方法。

 

針對訪問元素,又提供了4個方法innerText/innerHTML/outerTEXT/outerHTML。

經過這些方法,能夠讀和寫元素。

其中,*TEXT是返回文本內容 *HTML是返回html文本。

而outer*則是表明是否包含元素自己。

實際使用來看,在讀內容的時候 inner*和outer*沒有區別。

在把內容寫入元素的時候,就是是否包含元素自己的區別。

 

重要的是,這幾個方法有性能問題,好比在IE中,經過inner*刪除的節點,其綁定的事件依然在內存中,就很容易消耗大量內存。

 

還有一個技巧是,插入大量的html代碼,用innerHTML是很是快的,建議使用。

 

8、總結

首先感謝全部看到這裏的朋友,哈哈,關於DOM的東西實在是太多了,不過這也算是最重要的一個前端知識點之一吧。文章比較長,也許有點乏味,不過但願大家耐着性子看完後能夠有所收貨^ ^。

個人筆記又更新了,此次主要更新了一個 移動端單頁宣傳頁開發方法 ,你們能夠去看看^ ^。

 

 

9、MORE

DOM知識遠不止這些,接下來會簡單說說DOM的一些其他接口
 
首先就是一個經過document查找到窗口(也就是window)的方法
var parentWindow = document.defaultView || document.parentWindow;
defaultView是標準方法,parentWidow是兼容IE用的。
 
而後,Node類型新增了 isSameNode()和isEqualNode()。
它們的區別就是:same是指同一個節點,equal是指「看過去」如出一轍的兩個節點。
 
提供了一個訪問iframe方法
標準的是經過ele.contentDocument 這個會返回iframe裏面的document對象,可是有兼容性問題,ie不支持,可是ie支持contentWindow,至關於拿到window對象,那麼document對象也很容易取到,因此通常這麼寫。
var iframe = document.getElementById("myIframe");
var iframeDoc = iframe.contentDocument || iframe.contentWindow.document;
 
接下來講說處理樣式
設置樣式的基本的方式就是 ele.style.width = "20px";這個你們估計很經常使用。 使用相似這種方法來處理要注意的點就是記得加單位
快捷一點的方法就是 ele.style.cssText = "width: 20px;height:20px;";這個能夠批量設置
 
上面說的是設置,那麼獲取元素所使用的樣式,通常而言會用 document.defaultView.getComputedStyle(ele, null);方法來實現。這個方法會返回這個一個CSSStyleDeclaration對象(就和ele.style返回的同樣),可是會包含這個元素的所有樣式信息,而後直接返回值.width或者別的屬性名就能夠獲得樣式了。
不過IE不支持這種方式,IE更簡單,直接ele.currentStyle就能夠獲得和 document.defaultView.getComputedStyle(ele, null)同樣的返回值了。
 

最後說說元素的大小

取得元素的寬高 就使用 offsetHeight/offsetWidth,注意,不包括margin部分

獲得元素的偏移值就複雜了一點,有兩個方法offsetTop/offsetLeft,可是這兩個方法是指元素針對與它的offsetParent對象而言的,因此,若是你想獲得元素距離視口的位置,那麼還須要找到offsetParent,計算他們的offsetTop/offsetLeft,在找offsetParent的offsetParent,如此循環直到offsetParent爲null。注意,這個計算的位置也不包括margin部分。

 

上面說的是不包括margin,而後還有一組屬性clientWidth/clientHeight,它們也能夠獲得寬高,可是它們是不包括margin和border,也不計算滾動條。

 

最後,滾動的位置與距離,這個很難說清,你們直接看圖比較清楚明瞭。

其中,scrollLeft和scrollTop是能夠設置的,若是你把scrollTop設置成 "10px",那麼元素就是滾動到10px的地方去。

 
 
 
 

轉載本站文章請註明做者和出處 奇葩一朵朵 – http://www.cnblogs.com/season-huang/ ,請勿用於任何商業用途

相關文章
相關標籤/搜索