1. 程式人生 > 其它 >前端開發系列032-基礎篇之DOM

前端開發系列032-基礎篇之DOM

title: '前端開發系列032-基礎篇之DOM'
tags:
  - javaScript系列
categories: []
date: 2017-08-16 08:20:13

本文將詳細介紹DOM相關的知識點,包括但不限於Document文件結構、Node節點、Node節點的型別、Node節點的關係以及DOM的基本操作( 節點的獲取節點的建立節點的插入節點的克隆刪除等 )等內容,在文章的最後還以附錄的形式列出了DOM相關的所有屬性和方法。

一、關於DOM

簡單介紹

DOM(全稱為Document Object Model)即文件物件模型是用於表示和操作HTML或XML文件內容的一套基礎API( Application Programming Interface

)。DOM把整個頁面對映為一個多層節點結構,HTML 或 XML頁面中的每個組成部分都是某種型別的節點,這些節點又包含著不同型別的資料。當網頁被載入時,瀏覽器會內部的引擎會根據DOM模型,將結構化文件(比如HTML和XML)解析成一系列的節點,再由這些節點構建出一種樹狀結構(DOM Tree)。

✧ 有時候我們可能會看到DHTML這個專業術語:DHTML是動畫HTML的簡稱,其並不是一項新的技術,而是描述HTML CSS JavaScript技術組合的術語。它曾被認為是HTML/XHTML CSS和JavaScript相結合的產物,像今天的HTML5,但真正凝聚它們的是DOM。

下面給出一段簡單的HTML示例程式碼和對應的DOM樹結構圖。圖示中的的方框代表著文件中的一個個節點,每個方框(節點)暨一個Node物件,所有這些節點組成了DOM樹。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>DOM樹演示程式碼</title>
</head>
<body>
<div class="className">Hi! 文頂頂</div>
<a href="http://www.wendingding.com"></a>
</body>
</html>

<img width="600px src='

http://img.wendingding.vip/blog/dom1.png'>

節點的型別

HTML頁面中擁有眾多型別的節點,不同型別的節點其表示和操作的方式有很大的差異,下面分別列出:

> ❏ Text:標籤之間或標籤包含的文字內容
> ❏ Comment:HTML或XML中的註釋
> ❏ Element:網頁的各種HTML標籤,如a標籤 div標籤等
> ❏ Document:整個DOM樹的根,代表整個文件
> ❏ Attribute:網頁元素的屬性節點
> ❏ DocumentType:文件型別(doctype)標籤
> ❏ DocumentFragment:文件的片段,如果感覺費解請移步[MDN-DocumentFragment](https://developer.mozilla.org/zh-CN/docs/Web/API/DocumentFragment)

儘管在HTML頁面中存在著如此眾多型別的節點,但我們真正需要關注的主要還是:元素節點屬性節點文字節點。在(HTML|XHTML)文件中,文字節點總是被包含在元素節點的內部,屬性節點用來對元素做出更具體的描述。

在上面的圖示中,我們提供了一個div標籤,該標籤擁有“上坡和下坡不就是同一條路嗎”文字內容和兩個屬性節點。
① 個div標籤由開始標籤和結尾標籤組成,本身是Element型別的。
② “上坡和下坡不就是同一條路嗎”作為div標籤的文字內容,本身是Text型別的。
③ div標籤中的class和title是屬性節點(key-value),本身是Attribute型別的。

節點關係

DOM中節點的關係主要有子節點 父節點 後代節點(子孫節點) 祖先節點 兄弟節點這幾種情況,我們可以通過下面的示意圖先對DOM中的節點關係有一個簡單的認識。

DOM標準

從 IE4 和 Netscape Navigator4 開始,它們開始分別支援不同形式的DHTML(Dynamic HTML)為Web技術的發展帶來了很大的便利,但是也因為微軟和 Netscape 在DHTML的技術發展方面各持己見,導致編寫的網頁如果要同時執行在它們的瀏覽器上面需要做大量的適配工作,它們甚至互不相容。因此,負責制定Web通訊標準的 W3C(World Wide Web Consortium)開始著手規劃DOM規範,DOM規範有DOM0、DOM1、DOM2和DOM3等4個級別,具體範圍可以參考下圖。

規範說明

目前DOM規範的0級、1級和2級基本上已經被主流瀏覽器全部支援,DOM3級被部分支援( IE9+已經全部支援DOM1、2、3)。此外需說明的是除DOM規範外還存在一些擴充套件性的規範,比如HTML5Selectors APIElement Traversal以及SVG ( Scalable Vector Graphic)規範等,在支援(相容)這些規範的瀏覽器中都可以使用它們提供的標準API。下面給出相關規範的文件地址,詳情請自行查閱。

DOM1 規範

DOM 2規範

DOM 3 規範

Selectors API規範文件

HTML5 規範文件

Element Traversal 規範文件

Scalable Vector Graphic規範

ECMAScript-262/2018/6

二、Node && Element && nodeType

Node(節點)和 Element(元素節點)是嚴格區分的。也就是說Node和Element不能簡單的混為一談,因為很多人都搞不清楚它們的關係,所以這裡單獨拿出來討論。

Node節點,表示構成DOM樹的最小組成部分。換句話來說,在頁面中不論是元素節點、文字節點還是註釋或者別的東西本質上都是Node節點。

Element元素節點,是Node節點中的一種型別。

通俗的來講,node節點就像人一樣,是一種基本的型別。(大哲學家柏拉圖對人的定義是:人是兩腿無毛會直立行走的動物 :) 而人這種基本型別中,又存在著小孩、中年人、老年人、學生、教師、司機、男人、女人等種種具體的型別。

對應到這裡的關係,那麼Element其實是node的一種更具體的型別。不止Element,像Text、Comment以及Attribute等等這些其實都是特殊的Node,它們擁有自己的型別常量(TEXT_NODE、COMMENT_NODE以及ATTRIBUTE_NODE)用於區分彼此。文件中所有的node節點都擁有nodeType屬性,我們可以通過該屬性的值來確定節點的具體型別,下面列出對應關係。

# 可以直接在開發者工具的控制檯中像下面這樣檢測和驗證節點的型別
document.body.nodeType 				    //輸出結果為1
document.body.ELEMENT_NODE                           //輸出結果為1
document.body.ELEMENT_NODE == document.body.nodeType //輸出結果為true

# 需要注意的是ELEMENT_NODE是常量

NodeList 和 HTMLCollection型別

相信很多開發者都有這樣的經驗,“我們通過節點的childNodes屬性獲取的結果和children屬性獲取的結果是不一樣的”。下面我們通過一段簡短的程式碼來說明它們的不同。

...
<div id="demoID">
    我是測試的文字---A!!
    <div>div1</div>
    <div>div2</div>
    <div class="className">div3</div>
    <!--註釋的內容:後面跟span標籤-->
    <span>我是span</span>
    我是測試的文字---B!!
</div>
<script>
    var oDiv = document.getElementById("demoID");
    console.log("元素節點的children屬性 == HTMLCollection型別");
    console.log(oDiv.children);
    console.log(oDiv.children.length);
    console.log("元素節點的childNodes屬性 ==  NodeList型別");
    console.log(oDiv.childNodes);
    console.log(oDiv.childNodes.length);
</script>
...

通過程式碼的執行情況可以發現,元素節點(這裡為id為demoID的div元素)的children屬性得到的是HTMLCollection型別的偽陣列,而childNodes屬性得到的是NodeList型的偽陣列。[注意:它們是偽陣列的結構,可以遍歷但非真正意義上的陣列]。

NodeList是Node集合,而HTMLCollection可以認為是Element的集合。NodeList、NameNodeMap和HTMLCollection它們的結構類似,儲存的都是基於DOM結構動態查詢的結果,而非快照,故而在執行遍歷操作的時候需要注意無限迴圈的問題。

通常來說Document和HTMLDocument以及Element型別與和HTMLElement型別是嚴格區分的。Document型別代表一個HTML或XML文件,Element型別代表該文件中的一個元素。而HTMLDocument和HTMLElement通常只針對HTML文件和其元素。

三、DOM操作基礎

節點基本操作

節點的獲取

<div id="box">
  <li>測試1</li>
  <li>測試2</li>
</div>
<div class="box1" name="test">box1-1</div>
<div class="box1">box1-2</div>
<form action="" name="formTest"></form>
<script>
  
  /*獲取元素節點的方法
  * [1] document.getElementById(id)     通過id來獲取標籤(1)
  * [2] getElementsByTagName(tagname)   通過標籤名稱來獲取標籤(n)
  * [3] getElementsByClassName()        通過class來獲取標籤(n)
  * [4] document.getElementsByName()    通過name屬性節點來獲取標籤(n)
  * */

  var oDiv  = document.getElementById("box");           //div#box
  var oDivs = document.getElementsByClassName("box1");  //[box1,box1]

  /*提示:直接在已有標籤內部查詢,速度更快*/
  var oLis  = oDiv.getElementsByTagName("li");          //[li,li]
  var oDivN = document.getElementsByName("test")        //[div.box1]

</script>

說明 上面程式碼中展示了獲取節點的常用方式,此外 Selectors API 標準還為我們提供了兩個非常強大的基於CSS選擇器查詢DOM節點的方法。它們分別是querySelector()querySelectorAll()方法,均接收一個CSS選擇器作為引數,目前IE8+\Firefox 3.5+\ Safari 3.1+\Chrome和Opera 10+均支援它們。

節點的屬性

nodeType 獲取節點的型別,元素為1,屬性為2,文字為3

nodeName 獲取節點的名稱,元素節點返回大寫標籤名,屬性節點返回屬性名,文字節點返回#text

nodeValue獲取節點的值,元素節點固定返回 null,屬性節點返回屬性值,文字節點直接返回內容

節點的建立和插入

 /*節點建立的相關方法
  * [1] document.createElement(type)          建立元素節點
  * [2] document.createTextNode(text)         建立文字節點
  * [3] document.createAttribute()            建立屬性節點
  *
  * 節點插入的相關方法
  * [1]  ele.appendChild(node)              把node插入到ele子節點的末尾
  * [2]  ele.insertBefore(newNode,node)     在ele的子節點node前插入新的子節點newNode
  * [3]  ele.setAttributeNode(attrNode)     在指定元素中插入屬性節點
  * */

  /*01-建立div標籤並插入到頁面中*/
  var oDiv = document.createElement("div");

  /*02-給div建立文字節點*/
  var oText = document.createTextNode("滴答滴答~");
  
  /*03-把文字節點插入到div中*/
  oDiv.appendChild(oText);

  /*04-建立test屬性節點*/
  var oAttr = document.createAttribute("test");
  
  /*05-設定屬性節點的值*/
  oAttr.nodeValue = "test的value";
  
  /*06-把屬性節點設定到div標籤中*/
  oDiv.setAttributeNode(oAttr);

  /*07-建立span元素節點*/
  var oSpan = document.createElement("span");
  
  /*08-把span標籤插入到oDiv裡文字節點的前面*/
  oDiv.insertBefore(oSpan,oText);

  /*09-把div標籤插入到body子節點的末尾*/
  document.body.appendChild(oDiv);
  console.log(oDiv);
  
  /*備註:測試各種型別node的nodeType、nodeName和nodeValue屬性*/
  /*備註:nodeName和tagName是等價的,均為全部大寫的標籤名(型別)*/
  console.log("元素節點-oSpan.nodeType = " +oSpan.nodeType);  //oSpan.nodeType = 1
  console.log("屬性節點-oAttr.nodeType = " +oAttr.nodeType);  //oAttr.nodeType = 2
  console.log("文字節點-oText.nodeType = " +oText.nodeType);  //oText.nodeType = 3
  
  console.log("元素節點-oSpan.nodeName = " +oSpan.nodeName);  //oSpan.nodeName = SPAN
  console.log("屬性節點-oAttr.nodeType = " +oAttr.nodeName);  //oAttr.nodeType = test
  console.log("文字節點-oText.nodeName = " +oText.nodeName);  //oText.nodeName = #text
  
  console.log("元素節點-oSpan.nodeValue =" +oSpan.nodeValue);//oSpan.nodeValue = null
  console.log("屬性節點-oAttr.nodeValue =" +oAttr.nodeValue);//oAttr.nodeValue = test的value
  console.log("文字節點-oText.nodeValue =" +oText.nodeValue);//oText.nodeValue = 滴答滴答~

說明 上面的程式碼中我分別演示了建立元素節點(createElement)、文字節點(createTextNode)以及屬性節點(createAttribute)的相關方法,在建立節點後對它們執行了插入操作,主要用到了appendChildinsertBefore方法。這裡需要說明的是,上面的程式碼僅供方法演示用,在實際的開發中用上面的這種方式來處理未免太過麻煩和複雜。通常我們會有更簡單的方式來操作文字節點屬性節點,下面以漸進的方式給出兩種更簡單的實現方案。

  /*01-建立div標籤並插入到頁面中*/
  var oDiv = document.createElement("div");

  /*02-建立文字節點並插入*/
  oDiv.innerText = "滴滴答答~";

  /*03-建立屬性及節點並設定後插入*/
  oDiv.setAttribute("test","test的value值");

  /*04-建立span元素節點*/
  var oSpen = document.createElement("span");
  
  /*05-把span標籤插入到div中文字節點前*/
  oDiv.insertBefore(oSpen,oDiv.childNodes[0]);

  /*06-把div標籤插入到body子節點的末尾*/
  document.body.appendChild(oDiv);
  console.log(oDiv);

說明 上面的程式碼中我們通過 innerText屬性來簡化了建立文字節點並插入的操作,且使用了更方便直接的setAttribute方法來簡化了 建立屬性節點 - 設定 - 插入到標籤 的過程。

  /*01-建立div標籤並插入到頁面中*/
  var oDiv = document.createElement("div");

  /*02-設定標籤的節點和屬性內容*/
  oDiv.innerHTML = "<span></span>滴滴答答~";
  
  /*03-設定div標籤的屬性節點*/
  oDiv.setAttribute("test","test的value值");

  /*04-把div標籤插入到body子節點的末尾*/
  document.body.appendChild(oDiv);
  console.log(oDiv);

說明 上面的程式碼中我們使用innerHTML屬性來完成了設定div標籤子節點工作,該屬性和innerText很相像,區別在於innerHTML在解析的時候會解析元素節點而innerText則解析為純文字形態。

基於節點關係的操作

<body>
<div id="box">
  <span>我是spanA</span>
  <span class="span-class">我是spanB</span>
  我是div標籤的文字
  <span>我是spanC</span>
</div>
<script>

 /* 父節點:parentNode
  * 父元素:parentElement
  *
  * 子節點:childNodes firstChild          lastChild
  * 子元素:children   firstElementChild   lastElementChild
  *
  * 兄弟節點:previousSibling           nextSibling
  * 兄弟元素:previousElementSibling    nextElementSibling
  */

  /*01-獲取外層的div標籤*/
  var oDiv  = document.getElementById("box");

  /*02-對比子節點和子元素*/
 console.log(oDiv.childNodes);          //NodeList(7)
 console.log(oDiv.children);            //HTMLCollection(3)

 console.log(oDiv.firstChild);          //#text
 console.log(oDiv.firstElementChild);   //<span>我是spanA</span>

 console.log(oDiv.lastChild);           //#text
 console.log(oDiv.lastElementChild);    //<span>我是spanC</span>

 /*03-對比兄弟節點和兄弟元素*/
  var oSpan = document.getElementsByClassName("span-class")[0];
 console.log(oSpan.previousSibling);          //#text
 console.log(oSpan.previousElementSibling);   //<span>我是spanA</span>

 console.log(oSpan.nextSibling);              //我是div標籤的文字
 console.log(oSpan.nextElementSibling);       //<span>我是spanC</span>
</script>
</body>

說明 上面程式碼中簡單介紹了基於節點關係來操作標籤的程式碼,我們能看到的是DOM它為我們提供了兩套方法,分別是基於所有node型別的方法和基於ElementNode型別的方法,在開發中具體使用的時候後者居多,且因為我們在操作的時候得到的返回值很多情況下都是偽陣列結構,因此直接使用[ ]語法通過下標( 索引 )方式引用子元素( 節點 )會更方便些。

DOM提供兩套節點關係API是因為在IE9-的版本中childNodes屬性不會返回文字標籤間的空格(文字節點)和其他的瀏覽器中則會返回,它們的實現存在較大的差異性。DOM標準為規範元素遍歷故而新增了firstElementChildlastElementChildpreviousElementSiblingnextElementSibling以及childElementCount這5個API,children的情況類似。

複製 | 刪除 | 檢查 | 替換

<div class="box">
  <span>我是spanA</span>
  <span>我是spanB</span>
</div>
<script>
  /*
  * cloneNode()               克隆(複製)節點,接收布林值引數,true表示深複製,預設為false。
  * hasChildNodes()           判斷當前節點是否擁有子節點,返回布林值。
  * removeChild(ele)          刪除(並返回)當前節點指定子節點ele
  * replaceChild(new,old)     使用新的節點來替換舊的子節點   
  */
  
  var oDiv  = document.getElementsByClassName("box")[0];
  console.log(oDiv.hasChildNodes());              //true 檢查div標籤是否存在子節點
  var oSpanA = oDiv.children[0];                  //獲取spanA標籤
  var oSpanB = oDiv.children[1];                  //獲取spanB標籤
  oDiv.appendChild(oSpanA.cloneNode(true));       //複製spanA標籤並插入到div標籤末尾
  console.log(oDiv.removeChild(oSpanB));          //<span>我是spanB</span>
  console.log(oSpanA.hasChildNodes());            //true  檢查spanA是否存在子節點
  oSpanA.replaceChild(oSpanB,oSpanA.firstChild);  //替換操作
</script>

CSS樣式相關的操作

如果要通過JavaScript程式碼來操作標籤的樣式,可以在獲取指定的DOM標籤後直接通過style屬性來獲取和設定。

<head>
  <meta charset="UTF-8">
  <title>Title</title>
  <style>
    .box{border: 1px solid #0f0f0f;padding: 10px}
  </style>
</head>
<body>
<div class="box" style="background: #efefef">涼風有信,秋月無邊</div>
<script>
  var oDiv = document.getElementsByClassName("box")[0];

  /*A 操作標籤的內聯樣式*/
  /*01-讀取標籤的樣式*/
  console.log(oDiv.style.background);                 //rgb(239, 239, 239)
  console.log(oDiv.style.border);                     //" "

  /*02-設定標籤的樣式*/
  oDiv.style.color = "red";                           //設定標籤內文字顏色(前景色)

  /*B 操作標籤的樣式(包括內聯樣式)*/
  console.log(window.getComputedStyle(oDiv).border);  //1px solid rgb(15, 15, 15)
  console.log(window.getComputedStyle(oDiv).color);   //rgb(255, 0, 0)
</script>
</body>

說明 在上面程式碼中我們用到了getComputedStyle(ele,pseudo) 方法來獲取元素的樣式,其中ele表示要獲取樣式的元素,而pseudo引數是一個可選的偽元素樣式字串。此外,該方法在IE8-中存在相容性問題,可以使用currentStyle處理相容處理。

屬性和屬性節點

實話說屬性和屬性節點這是一對很難描述清楚的概念,因此我將在接下來章節花很多心思來重點講解它們並對對比。需要明確的是它們完全是兩個不同的概念,只是大多數人在大多數情況下總是把它們混為一談,甚至搞不清楚所以然,所以就有了這麼一個章節。

屬性 我們把變數封裝到物件中就成了屬性。因此在討論屬性的時候,必須先把物件的問題搞清楚,因為所謂屬性只能是某個物件的屬性。那誰是物件呢?其實我們在討論DOM的時候,DOM樹的每個node節點都是物件。物件的特點是什麼? 物件是鍵值對的集合,以key-value的特定形式展示,物件是有型別的,譬如Object型別的物件、Array型別的陣列物件等。如何檢測物件的型別?可以通過typeof關鍵字檢視其返回值是否是object,也可以直接呼叫Object.prototype.toString()方法來認祖歸宗,確認其真實型別。

那麼,請看DOM節點的物件特徵。

<div class="box" title="我是標題" xxx="我是xxx">我是div</div>
<script>

  var oDiv = document.getElementsByClassName("box")[0];
  var obj  = {name:"文頂頂",age:18},arr = [1,2,3];

  /*01-檢查物件型別-typeof*/
  console.log(typeof obj,typeof arr, typeof oDiv);  //object object object

  /*02-檢查物件型別-toString()   返回值 [物件型別  建構函式]*/
  console.log({}.toString.call(obj));               //[object Object]
  console.log({}.toString.call(arr));               //[object Array]
  console.log({}.toString.call(oDiv));              //[object HTMLDivElement]

  /*03-物件的訪問 增刪改查操作*/
  console.log(oDiv.wendingding);                    //undefined

  /*03-A 新增*/
  oDiv.wendingding = "文頂頂";                       //增加wendingding屬性
  oDiv.name     = "測試的名字";                       //增加logName屬性
  /*03-B 查詢*/
  console.log(oDiv.wendingding);                    //"文頂頂"
  /*03-C 修改*/
  oDiv.wendingding = "光頭強";
  console.log(oDiv.wendingding);                    //光頭強
  /*03-D 刪除*/
  console.log(delete oDiv.wendingding);             //true
  console.log(oDiv.wendingding);                    //undefined

  /*03-E 在div物件身上新增並呼叫方法*/
  oDiv.logName = function () {
    console.log("logName() => " + this.name);
  }
  oDiv.logName();                                   //"logName() =>測試的名字"
</script>

看到這兒,我想你已經清楚了事實。
需要說明的是,通常我們在開發中很少會像上面這樣來進行操作,而且上面程式碼中的namelogName以及wendingding均不會出現在div的標籤結構中,而是作為物件的屬性和方法直接儲存。下面簡單列出在DOM物件上常用的屬性。

tagName      獲取元素元素的標籤名
id           設定/獲取元素id屬性
name         設定/獲取元素name屬性
style        設定/獲取元素的內聯樣式
className    設定/獲取元素的class屬性
innerHTML    設定/獲取元素的內容(包含html程式碼)
outerHTML    設定或獲取元素及其內容(包含html程式碼)
innerText    設定或獲取位於元素標籤內的文字
outerText    設定(包括標籤)或獲取(不包括標籤)元素的文字

offsetTop    當前元素離<定位父級>元素頂部的距離(如果沒定位的父級,則相對於根元素html的距離)
offsetLeft   當前元素離<定位父級>元素左邊的距離(如果沒定位的父級,則相對於根元素html的距離)
offsetWidth  當前元素的寬度(border + padding + content)
offsetHeight 當前元素的高度(border + padding + content)

屬性節點 的內容和我上文介紹的概念保持一致,我們可以下面的示例圖來進行區分。

屬性節點區別於屬性,它們被儲存在元素節點( 標籤 )物件的attributes屬性下,該屬性對應的是一個物件,裡面儲存著屬性節點的鍵值對(key-value)資訊。
<div class="box" title="我是標題" xxx="我是xxx">我是div</div>
<script>

  /*屬性節點的相關操作*/
  var oDiv = document.getElementsByClassName("box")[0];
  
  /*01-獲取所有的屬性節點*/
  console.log(oDiv.attributes);             //NamedNodeMap["class","title","xxx"]

  /*02-屬性節點的訪問*/
  /*02-A 查詢*/
  console.log(oDiv.getAttribute("class"));  //"box"
  console.log(oDiv.getAttribute("xxx"));    //"我是標題"
  console.log(oDiv.getAttribute("title"));  //"我是xxx"

  /*02-B 新增*/
  oDiv.setAttribute("test","test對應的值");
  console.log(oDiv.getAttribute("test"));   // "test對應的值"

  /*02-C 修改*/
  oDiv.setAttribute("xxx","阿魯巴");
  console.log(oDiv.getAttribute("xxx"));    //"阿魯巴"
  console.log(oDiv["xxx"]);                 //undefined 注意:自定義的屬性節點不支援這樣訪問

  /*02-D 刪除*/
  oDiv.removeAttribute("xxx")               //刪除標籤中的"xxx"屬性節點
  console.log(oDiv.getAttribute("xxx"));    //null
  
  /*備註
  * 建立屬性節點 var attr = createAttribute(attr-key); 
  * 設定屬性節點 attr.value = attr-value; ele.setAttributeNode(attr)
  * 獲取屬性節點 
  * [1] ele.attributes[attr-key].value
  * [2] ele.getAttributeNode[attr-key].value
  * [3] ele.getAttribute(attr-key)
  * */
</script>

上面程式碼中展示了操作屬性節點的相關方法,當然除了這些方法外,其實我們還可以通過attributes的方法來對它們進行操作,標籤的attributes屬性對應的是一個NamesNodeMap型別的物件,它本質上同 NodeList 和 HTMLCollection 型別一樣,是一個動態的集合。需說明的是,雖然標籤的attributes屬性提供了getNameItem( )removeNameItem( )setNameItem( )以及item( )等方法也能操作屬性節點,但是因為操作不是很方便因此很少使用。

關於屬性和屬性節點,在著名的[ jQuery ](http://jquery.com/)框架中封裝了兩對方法來分別進行處理,其中`prop()和removeProp()`方法專門用於對屬性進行操作,而`attr()和removeAttr()`方法則用於操作屬性節點。此外,對DOM物件而言,有時候我們也可以直接通過屬性來操作屬性節點(如`id、value 和 className`等),可以認為這是為方便操作而提供的`天橋`。雖然在寫程式碼時能快速安全的完成功能即可,但有些時候能夠正確的區分物件屬性和屬性節點是意義重大的。

通常,標籤身上的標準屬性節點都會有一個與之對應的屬性,譬如idclass( 對應className )等,但非標準的屬性節點則並非如此。另外,HTML5標準建議我們在給標籤(ElementNode)新增非標準屬性的時候總是新增data-字首,用於說明這些這些非標準屬性提供的是與渲染無關的資訊。在使用data-字首的方式給標籤添加了自定義屬性後,我們可以通過標籤的dataset屬性來訪問它們,該屬性是一個DOMStringMap的例項物件,維護著一個key-value的對映。

  /*<idv id="myDiv" data-info="資訊" data-index="2"></idv>*/
  
  /*00-獲取頁面中指定的div標籤*/
  var oDiv = document.getElementById("myDiv");

  /*01-讀取自定義屬性節點的值*/
  console.log(oDiv.dataset);        //DOMStringMap {index: "2",info: "資訊"}
  console.log(oDiv.dataset.index);  //2
  console.log(oDiv.dataset.info);   //"資訊"

  /*02-設定|修改自定義屬性節點的值*/
  oDiv.dataset.index = 10;          //修改
  oDiv.dataset.des   = "描述資訊";
  /*<idv id="myDiv" data-info="資訊" data-index="10" data-des='描述資訊'></idv>*/

  /*03-其他操作屬性節點的方式(對比)*/
  /*03-A getAttribute()*/
  console.log(oDiv.getAttribute("info"));         //null  注意不能生了data-字首
  console.log(oDiv.getAttribute("data-info"));    //"資訊"

  /*03-B attributes*/
  /*說明:使用nodeName和name可以訪問屬性名,使用nodeValue和value訪問屬性值*/
  console.log(oDiv.attributes.getNamedItem("data-info").nodeName); //"data-info"
  console.log(oDiv.attributes.getNamedItem("data-info").nodeValue);//"資訊"

  /*03-C getAttributeNode()*/
  console.log(oDiv.getAttributeNode("data-info"));                 //data-info="資訊"
  console.log(oDiv.getAttributeNode("data-info").name);            //data-info
  console.log(oDiv.getAttributeNode("data-info").value);           //資訊

  /*04-補充:獲取元素所有屬性節點名稱陣列*/
  console.log(oDiv.getAttributeNames());  //["id", "data-info", "data-index", "data-des"]

classList HTML5新增了一種操作元素類名的方式,即給每個標籤都提供classList屬性,該屬性的值是DOMTokenList型別的例項,通過該屬性來操作標籤的類名更簡單也更安全。classList例項物件擁有add()contains()remove()toggle()等方法來操作具體的class,如果要訪問某個具體的class當然也可以通過[ ]語法或者是item( index )

  /*<div class="box test active" id="divDemo"></div>*/
  /*00-獲取頁面中指定的元素*/
  var oDiv = document.getElementById("divDemo");
  console.log(oDiv.classList);            //DOMTokenList(3) ["box", "test", "active"]

  /*01-讀取元素的class*/
  /*[ ]語法來讀取指定的class*/
  console.log(oDiv.classList[0]);         //"box"
  /*item(index)方法來讀取指定的class*/
  console.log(oDiv.classList.item(1));    //"test"
  /*使用className屬性來讀取元素的全部class*/
  console.log(oDiv.className);            //box test active
  console.log(oDiv.className.split(" ")); //["box", "test", "active"]

  /*02-新增class*/
  oDiv.classList.add("box");              //如class已經存在則不做任何處理
  oDiv.classList.add("test2");            //若class尚不存在則新增
  /*<div class="box test active test2" id="divDemo"></div>*/

  /*03-檢查是否存在指定的class,如果存在那麼就返回true,否則返回false*/
  console.log(oDiv.classList.contains("box"));    //true
  console.log(oDiv.classList.contains("haha"));   //false

  /*04-刪除class*/
  oDiv.classList.remove("test");
  /*05-切換class 如果存在那麼就刪除,否則就新增*/
  oDiv.classList.toggle("box");                   //刪除
  oDiv.classList.toggle("box2");                  //新增
  
  /*<div class="active test2 box2" id="divDemo"></div>*/

四、DOM操作案例

案例 動態建立表格並設定隔行變色

<!DOCTYPE html>
<html lang="en">
<head>
	<meta charset="UTF-8">
	<title></title>
	<style>
		table{
			border:1px solid #ddd;
			border-collapse: collapse;
			width:100%;
			margin-top: 20px;
		}
		td{
			border:1px solid #ddd;
			padding:3px 5px;
		}
		.odd{background-color: #199;}
	</style>
</head>
<body>
<label for="row">請輸入行數:</label><input type="text" id="row">
<label for="col">請輸入列數:</label><input type="text" id="col">
<button class="btn">動態生成表格</button>
<div id="output"></div>
<script>

  /*00-監聽頁面的載入*/
window.onload = function(){

  /*01-獲取頁面中指定的元素*/
  var oRow 	  = document.getElementById('row');
  var oCol 	  = document.getElementById('col');
  var oOutput     = document.getElementById('output');
  var oBtn 	  = output.previousElementSibling;

  /*02-註冊按鈕的點選事件*/
  oBtn.onclick = function(){
	/*03-獲取使用者輸入的行-列資訊*/
	var _row = oRow.value,_col = oCol.value;

	/*04-建立表格標籤*/
	var table = document.createElement('table');
	var tbody = document.createElement('tbody');

	/*05-根據行數來動態的建立tr標籤並新增到tbody標籤中*/
	for(var i=0;i<_row;i++){
	  var tr = document.createElement('tr');

	  /*設定隔行換色*/
	  if(i%2 === 0) tr.className = 'odd';

	  tbody.appendChild(tr);

	  /*06-根據列數來動態建立rd標籤並新增到tr標籤中*/
	  for(var j=0;j<_col;j++){
		 var td = document.createElement('td');
		 td.innerHTML = '單元格 - ' + i + j;
		 tr.appendChild(td);
		}
	}

	/*07-把表格插入到頁面中顯示出來*/
	table.appendChild(tbody);
	oOutput.innerHTML = '';       // 新增前先清空#output
	oOutput.appendChild(table);
  }
}
</script>
</body>
</html>

五、附錄

Node(節點)的屬性和方法

------------------------
####  Node節點的主要屬性
------------------------

baseURI    			// 當前網頁的絕對路徑
children  			// 指定節點的所有Element子節點
childNodes   		        // 當前節點的所有子節點
childElementCount  	        // 當前節點所有Element子節點的數量

nodeName   			// 節點名稱[只讀]
nodeType   			// 節點型別的常數值[只讀]
nodeValue  			// Text或Comment節點的文字值[只讀]
innerText			// 節點的文字內容
nextSibling  		        // 緊跟在當前節點後面的第一個兄弟節點
isConnected			// 布林型別的值,用於檢查當前節點與DOM樹是否連線[只讀]
textContent  		        // 當前節點和它的所有後代節點的文字內容
ownerDocument  		        // 當前節點所在的頂層文件物件,即Document

previousSibling  	        // 當前節點的前一個兄弟節點
parentNode   		        // 當前節點的父節點
parentElement  		        // 當前節點的父Element節點
firstChild  		        // 當前節點的第一個子節點
firstElementChild  	        // 當前節點的第一個Element子節點
lastChild   		        // 當前節點的最後一個子節點
lastElementChild   	        // 當前節點的最後一個Element子節點

------------------------
####  Node節點的主要方法
------------------------

cloneNode(true);  		// 克隆節點,引數傳遞布林型別的值(預設為false)
hasChildNodes()   		// 布林型別的值,表示當前節點是否有子節點
appendChild(node)   	        // 追加新的節點到當前節點中(插入後作為最後一個節點)
removeChild(node)  		// 刪除指定的子節點
isEqualNode(noe)  		// 布林型別的值,用於檢查兩個節點是否(完全)相等
contains(node)  		// 布林型別的值,用於判斷引數節點是否為當前節點的後代節點
normalize()   			// 清理當前節點內的所有Text節點,將去除空的文字節點併合並文字。

insertBefore(newNode,oldNode)  	// 在指定子節點之前插入新的子節點
replaceChild(newChild,oldChild) // 替換節點
compareDocumentPosition(node)   // 比較當前節點與指定節點的位置關係,返回不同的掩碼。

------------------------
####  ChildNode相關的方法
------------------------

ChildNode.replace() 	        // 替換節點
ChildNode.remove()  	        // 將ChildNode從其父節點的子節點列表中移除
ChildNode.before()  	        // 在當前標籤(節點)的前面插入新的節點
ChildNode.after()   	        // 在當前標籤(節點)的後面插入新的節點

Element(元素節點)的屬性和方法

元素節點繼承了Node的所有屬性和方法,Element本身也作為通用的基類來使用。下面列出元素節點的主要屬性和方法,如果沒有特別標註為[讀寫]的,那麼預設為只讀。
------------------------
####  Element主要的屬性
------------------------

id  				// 指定元素的id屬性[讀寫]
attributes  		        // 指定元素節點相關的所有屬性集合(對映)
tagName  			// 指定元素節點的標籤名(大寫)
innerHTML   		        // 指定元素節點包含的節點內容[讀寫],區別於innerText
outerHTML  			// 指定元素節點的所有HTML程式碼,包括它自身和包含的的所有子元素[讀寫]
className  			// 元素節點的class屬性值[讀寫]
classList  			// 元素節點所有的class屬性。
dataset   			// 元素節點中所有的data-*屬性。
localName			// 元素名稱本地化的結果
clientTop   		        // 元素節點距離它頂部邊界的高度
clientLeft   		        // 元素節點距離它左邊界的寬度
clientHeight   		        // 元素節點內部(相對於外層元素)的高度
clientWidth   		        // 元素節點內部(相對於外層元素)的寬度

style  				// 元素節點的行內樣式
scrollHeight  		        // 元素節點滾動檢視的高度
scrollWidth  		        // 元素節點滾動檢視的寬度
scrollLeft   		        // 元素節點橫向滾動條距離左端的位移[讀寫]
scrollTop   		        // 元素節點縱向滾動條距離頂部的位移[讀寫]
offsetHeight   		        // 元素節點相對於版面或由父座標 offsetParent 指定的父座標的高度
offsetWidth    		        // 元素節點相對於版面或由父座標 offsetParent指定的父座標的寬度
offsetLeft    		        // 元素節點相對於版面或由父座標 offsetParent指定的父座標的左側位移
offsetTop   		        // 元素節點相對於版面或由父座標 offsetParent指定的父座標的頂部位移

firstElementChild		// 獲取元素節點的第一個子元素
lastElementChild 		// 獲取元素節點的最後一個子元素
nextElementSibling 		// 獲取元素節點的下一個兄弟節點
previousElementSibling	        // 獲取元素節點的上一個兄弟節點

------------------------
####  Element主要的方法
------------------------

[⦿] 操作屬性節點相關的方法
getAttribute()			// 讀取指定屬性
setAttribute()			// 設定指定屬性
hasAttribute()			// 返回布林型別的值,檢查當前元素節點中是否有指定的屬性
removeAttribute()		// 移除指定屬性

[⦿] 選擇器相關方法
querySelector()  		// 根據引數獲取選中的所有標籤中的第一個返回
querySelectorAll()  	        // 根據引數獲取選中的所有標籤返回
getElementsByTagName()          // 根據標籤名來獲取指定的標籤返回
getElementsByClassName()        // 根據class的名稱來獲取指定的標籤返回

[⦿] 事件相關方法
addEventListener()		// 新增事件監聽的回撥函式
removeEventListener()	        // 移除事件監聽函式
dispatchEvent()			// 觸發事件
attachEvent()			// 新增事件監聽的回撥函式(IE 9-)
detachEvent()                   // 移除事件監聽函式(IE 9-)
insertAdjacentHTML()	        // 指定的文字解析為HTML或XML,並將結果節點插入到DOM樹中的指定位置

Document(文件)的屬性和方法

------------------------
####  Document的主要屬性
------------------------

document.body   			// 當前文件的<body>節點
document.head   			// 當前文件的<head>節點
document.defaultView   		        // 當前文件物件所在的window物件
document.doctype   			// 當前文件關聯的文件型別定義(DTD)
document.documentElement                // 當前文件的根節點
document.activeElement  	        // 當前文件中獲得焦點的那個元素。

document.links  			// 當前文件的所有連線集合
document.forms  			// 頁面中所有表單元素的集合
document.images  			// 頁面中所有圖片元素的集合
document.embeds  			// 網頁中所有嵌入物件的集合
document.scripts  			// 當前文件的所有指令碼的集合
document.styleSheets  		        // 當前網頁的所有樣式表的集合

document.URL  				// 當前文件的URL地址
document.cookie   			// 用來操作Cookie[讀寫]
document.title    			// 當前文件的標題
document.domain  			// 當前文件的域名
document.referrer  			// 當前文件的訪問來源
document.location  			// 獲取location物件,提供與URL相關的資訊
document.readyState  		        // 當前文件的狀態
document.designMode  		        // 控制當前文件是否可編輯[讀寫]
document.compatMode  		        // 返回瀏覽器處理文件的模式
document.documentURI  		        // 當前文件的URI地址
document.lastModified  		        // 當前文件最後修改的時間戳
document.characterSet                   // 當前文件的字符集,比如UTF-8等

------------------------
####  Document的主要方法
------------------------

document.open()   		        // 用於新建並開啟一個文件
document.close()   		        // 不安比open方法所新建的文件
document.write()   		        // 用於向當前文件寫入內容
document.writeIn()  	                // 用於向當前文件寫入內容,尾部新增換行符

document.createEvent(type)   		// 建立事件物件
document.addEventListener()  		// 註冊事件監聽
document.removeEventListener()  	// 登出事件
document.dispatchEvent(event)  		// 觸發事件

document.createElement(tagName)   	// 建立元素節點
document.createTextNode(text)     	// 建立文字節點
document.createAttribute(name)    	// 建立屬性節點
document.createDocumentFragment()       // 生成一個DocumentFragment物件

document.getElementById(id)   		        // 根據id值獲取指定ID的元素節點返回
document.querySelector(selectors)   		// 根據選擇器引數獲取指定的所有標籤中的第一個返回
document.querySelectorAll(selectors)    	// 根據選擇器引數獲取指定的所有標籤返回
document.getElementsByTagName(tagName)  	// 根據標籤名稱獲取指定的所有標籤返回
document.getElementsByClassName(className)      // 根據class名稱獲取指定的所有標籤返回
document.getElementsByName(name)   	        // 根據name獲取擁有指定name屬性的元素節點返回
document.elementFromPoint(x,y) 		        // 返回位於頁面指定位置最上層的Element子節點

原創文章,原創文章,本文作者 文頂頂

版權宣告:著作權歸作者所有,商業轉載請聯絡文頂頂獲得授權,非商業轉載請註明出處。