JavaScript高級程序設計(第3版)學習筆記15——DOM基礎


  從這篇筆記開始整理JavaScript的第三部分:文檔對象模型DOMDocument Object Model)。DOM是針對HTMLXML文檔的一個API,脫胎於DHTML,由W3C負責制定相關標准,現在已經成為表現和操作頁面標記的真正的跨平台、語言中立的一種標准,除了JavaScript外,其它一些語言比如SVGMathML等也不同程度的實現了各自的DOM

1、DOM組成和級別

  DOM分為三個組成部分和三個級別:

組成部分 說明
核心DOM 用於任何結構化文檔的標准模型
XML DOM 用於XML文檔的標准模型,定義了所有XML元素的對象和屬性,以及訪問它們的方法(接口),換句話說,XML DOM是用於獲取、更改、添加或刪除XML元素的標准
HMTL DOM 用於HTML文檔的標准模型,定義了所有HTML元素的對象和屬性,以及訪問它們的方法(接口)
DOM級別 瀏覽器支持情況 功能模塊 說明
0級 IE4、Netscape4 W3C標准中,是沒有0級的,通常所謂DOM 0級指的就是在DOM 1級規范之前的在IE4Netscape Navigator4中支持的DHTML
1級 幾乎所有現代瀏覽器 DOM核心(DOM Core) 規定的是如何映射基於XML的文檔結構,以便簡化對文檔中任意部分的訪問和操作
DOM HTML DOM核心基礎上加以擴展,添加了針對HTML的對象和方法
2級 IE 9+
Opera 7-9.9
(部分支持),OPera 10+
Safari 2+
(部分支持)
Chrome 1+
(部分支持)
Firefox 1+
(幾乎全部)
DOM視圖(DOM Views) 定義了跟蹤不同文檔(例如,應用CSS前后的文檔)視圖的接口
DOM事件(DOM Events) 定義了事件和事件處理的接口
DOM樣式(DOM Style) 定義了基於CSS為元素應用樣式的接口
DOM遍歷和范圍(DOM Traversal and Range) 定義了遍歷和操作文檔樹的接口
DOM核心和HTML擴展 開始支持XML命名空間等
3級 IE9+
Opera9+
(部分支持)
Firefox1+
(部分支持)
DOM加載和保存(DOM Load and Save 引入了以統一方式加載和保存文檔的方法
DOM驗證(DOM Validation 驗證文檔的方法
DOM核心和HTML擴展 開始支持XML1.0規范,涉及XML InfosetXPathXMLBase

2、文檔映射

DOMHTMLXML文檔映射成一個由不同節點組成的樹型機構,每種節點都對應於文檔中的信息或標記,節點有自己的屬性和方法,並和其他節點存在某種關系,節點之間的關系構成了節點層次,例如:

<html>
    <head>
        <title>標題</title>
    </head>
    <body>
        <p>測試</p>
    </body>
</html>

1)文檔節點Document是每個文檔的根節點
2)文檔元素:如右圖,文檔節點只有一個子節點,即<html>元素,稱之為文檔元素。文檔元素是文檔的最外層元素,其他所有元素都包含在文檔元素中,每個文檔只能有一個文檔元素。在HTML中,文檔元素永遠是<html>元素,但是在XML中,沒有預定義元素,因此任何元素都可能成為文檔元素。
3)文檔中每一段標記都通過樹中的一個節點來表示,比如HTML元素通過元素節點表示,特性(attribute)通過特性節點表示,文檔類型通過文檔類型節點表示,而注釋則通過注釋節點表示。在DOM1中,總共有12種節點類型,他們都繼承自一個基類型。

 

3、Node接口

類別 屬性/方法 /類型/返回類型 說明
屬性 nodeName String 節點名字,根據節點類型定義。對於元素節點,就是原始的標簽名
nodeValue String 節點值,根據節點類型定義。對於元素節點為null
nodeType Number 節點類型,返回12種節點類型值之一
ownerDocument Document 指向這個節點所屬的文檔,可以利用它直接訪問文檔節點,而不用層層回溯
parentNode Node 文檔樹中的父節點
childNodes NodeList 所有直接子節點組成的列表,不同瀏覽器對空白字符和<html>外的注釋有不同處理,會導致childNodes不一致
firstChild Node 第一個直接子節點,沒有子節點返回null
lastChild Node 最后一個直接子節點,沒有子節點返回null
previousSibiling Node 前一個兄弟節點,沒有則返回null
nextSibiling Node 后一個兄弟節點,沒有則返回null
方法 hasChildNodes() Boolean 有子節點時返回true,否則返回false
appendChild(node) Node 在末尾添加子節點,返回新添加的節點。如果傳入參數已經是文檔中一部分,結果將是將該節點從原來的位置移向新位置(任何DOM節點不能同時出現在多個位置上)
removeChild(node) Node 移除節點並返回這個節點。刪除后,節點仍然屬於原來的文檔,只是沒有了位置
replaceChild(node,node) Node 傳入新添加的節點和被替換的節點,返回被替換的節點。被替換的節點仍然屬於原來的文檔,只是沒有了位置
insertBefore(node,node) Node 傳入新添加的節點和參照節點,新添加節點會成為參照節點的前一個兄弟節點,如果參照節點為null,則插入到最后,相當於appendChild()。返回新添加的節點
cloneNode(boolean) Node 復制節點,參數為true時,復制節點及其所有子節點樹,為false時,只復制節點本身。返回的節點屬於文檔所有,但沒有指定父節點
normalize()   處理文檔樹中的文本節點,刪除空文檔節點或合並兩個相鄰的文本節點等

說明:

(1)關於節點類型nodeType,在DOM1中定義了12種常量,是作為Node類型構造函數的屬性定義的(靜態屬性),它們對應於各自的節點類型:

節點 節點類型(nodeTypeNode的靜態屬性) 節點名稱(nodeName 節點值(nodeValue 父節點(parentNode 子節點(childNodes 說明
Document Node.DOCUMENT_NODE(9) #document null null DocumentType(最多一個)|Element(最多一個)
|Comment|ProcessingInstruction
下有進一步敘述
Element Node.ELEMENT_NODE(1) 元素的標簽名 null Document|Element Element|Text|Comment|
ProcessingInstruction|
CDATASection|EntityReference
Text Node.TEXT_NODE(3) #text 節點所包含的文本 Element (不支持)沒有子節點
Comment Node.COMMENT_NODE(8) #comment 注釋的內容 Document|Element (不支持)沒有子節點 和Text繼承自相同基類,擁有除splitText()外所有字符串方法,可通過nodeValue或data屬性獲取注釋內容
CDATASection Node.CDATA_SECTION_NODE(4) #cdata-section CDATA區域的內容 Document|Element (不支持)沒有子節點 繼承自Text類型,擁有除splitText()外所有字符串方法
DocumentType Node.DOCUMENT_TYPE_NODE(10) doctype的名稱 null Document (不支持)沒有子節點 在DOM1中不能動態創建,DocumentType對象的3個屬性:
name表示文檔類型名稱
entities表示描述文檔類型實體的NamedNodeMap
notations表示文檔類型描述的符號的NamedNodeMap
DocumentFragment Node.DOCUMENT_FRAGMENT_NODE(11) #document-fragment null null 同Element類型 下有進一步敘述
Attr Node.ATTRIBUTE_NODE(2) 特性名稱 特性值 null HTML中不支持,XML中可以是TextEntityReference 有3個自己的屬性:
name等於nodeName
value等於nodeValue
specified表示是否為默認設置
EntityReference Node.ENTITY_REFERENCE_NODE(5) 引用的實體名稱 null     實體引用節點
Entity Node.ENTITY_NODE(6) entity name null     實體節點
ProcessingInstruction  Node.PROCESSING_INSTRUCTION_NODE(7) ProcessingInstruction.target 相同 ProcessingInstruction.data 相同     處理指令
Notation  Node.NOTATION_NODE(12) notation name null     DTD中定義的符號

這些節點類型都實現了Node接口,因此都可以訪問Node類型的屬性和方法(不支持子節點的節點類型上調用appendChild()、insertBefore()、replaceChild()、removeChild()等方法時會拋出異常)。經過測試在IE9中已經可以直接訪問Node類型中定義的常量值,並且IE9中這些值不能改變,而在Firefox15的版本中仍然可以修改,這應該是Firefox實現的一個Bug(原書說IE中不可訪問Node的論述似有不妥)。

Node.DOCUMENT_NODE = 2;
console.info(Node.DOCUMENT_NODE);//IE9輸出9,而Firefox15輸出的是2

2)關於NodeList類型,它也是一個類數組類型,有length屬性,也可以通過方括號語法訪問,還可以通過item()方法訪問,但並不是Array的實例,如果要對其使用數組方法,必須像轉換arguments對象一樣來轉換NodeList對象:

var node = document;//任意一個節點,這里使用文檔根節點測試
console.info(node.childNodes.length);//2,直接子節點
console.info(node.childNodes[0].nodeName);//通過方括號語法訪問第1個元素,索引從0開始
console.info(node.childNodes.item(1).nodeName);//通過item()方法訪問第2個元素,索引從0開始
var arr = Array.prototype.slice.call(node.childNodes,0);//轉換為真正的數組
console.info(arr.join(','));//可以使用數組方法了

需要特別注意的是,NodeList對象類型是一個有生命、有呼吸的對象,它的屬性和元素是跟隨文檔變化而變化的:

var node = document;
var nodeList = node.childNodes;
var src = nodeList.length;
node.removeChild(nodeList[0]);//修改文檔,會同時修改NodeList對象的屬性和元素,即便是已經將其保存為另外一個變量
console.info(nodeList.length == src - 1);//true

類似NodeList這種動態變化的對象對象還有HTMLCollectionNamedNodeMap等,其中HTMLCollection還有一個namedItem()方法,可以通過元素的name獲取集合中的項。

4、Document類型

  在JavaScript中,通過Document類型表示文檔,而我們通常使用的document對象則是HTMLDocument(繼承自Document類型)的一個實例,表示整個HTML頁面。同時,docuemnt對象也是window對象的一個屬性,可以作為全局對象來訪問。document對象的主要屬性和方法有:

類別 屬性/方法 說明 備注
屬性 documentElement 指向HTML頁面中的<html>元素 作為document的子節點,<html>元素還可以通過document.firstChilddocument.childNodes[0]等方式訪問
body 直接指向HTML頁面中的<body>元素 document.body的使用頻率非常高
doctype 表示<!DOCTYPE>相關信息,不同瀏覽器差異比較大,因此用處不大 有些瀏覽器會把<!DOCTYPE>作為注釋處理
title 包含<title>元素中的文本,顯示在瀏覽器窗口的標題和標簽頁上,可以通過它修改標題 修改title屬性的值不會改變<title>元素
URL 頁面完整的URL,即地址欄中顯示的URL 這些信息都存在於請求的HTTP頭部,這些屬性是為了方便在JavaScript中訪問,domain的設置需要注意:
1
、不能將domain設置為URL不包含的域,如URL='p2p.wrox.com'domain可以設置為wrox.com,但不能設置為ncz.net
2
、不能將domain由松散的設置為緊綳的,如原來domain='wrox.com',不能設置為domain='p2p.wrox.com' 
domain 頁面的域名,可以設置
referrer 鏈接到當前頁面的那個頁面的URL,沒有來源頁面時,為空字符串
implementation 提供瀏覽器對DOM實現情況的描述對象,在DOM1只有一個hasFeature()方法 hasFeature()接受兩個參數:DOM功能和版本號。由於瀏覽器實現問題,這個方法並非百分百准確
文檔元素集合 anchors 包含文檔中所有帶name特性的<a>元素 這些集合都是HTMLCollection對象,集合中的項會隨文檔的變化而更新
applets 包含文檔中所有<applet>元素,已經不推薦使用
forms 包含文檔中所有<form>元素,相當於document.getElementsByTagName('form')
images 包含文檔中所有<img>元素,相當於document.getElementsByTagName('img')
links 包含文檔中所有帶href特性的<a>元素
元素獲取方法 getElementById() 接受一個參數:要獲取元素的ID,匹配大小寫,找不到時返回null,若有多個相同ID元素,返回第一個 IE8-中不區分大小寫,並且表單元素(如<input>)中的name也被作為ID去查詢
getElementsByTagName() 接受一個參數:要獲取元素的標簽名,返回包含0或多個元素的NodeList,在HTML中,返回HTMLCollection 傳入*號時,則返回文檔中所有元素,可以通過索引(調用item())、字符串(調用namedItem())、以及直接使用item()方法訪問結果集
getElementsByName() HTMLDocument特有方法,返回name特性為傳入參數值的元素集合,返回HTMLCollection對象 使用namedItem()時,只返回第一項,因為是按name獲取的集合,集合中的name全部相同
文檔寫入方法 write() 輸出文本,接受一個參數:寫入到輸出流的文本 write()writeln()被用來向頁面動態地輸入內容,需要注意的是:
1
、如果輸出內容中含'</script>',為了防止做為標簽解析而發生錯誤,需要特殊處理,可以寫成'<\/script>'或拆分
2
、如果是頁面加載完成之后調用,會重寫整個頁面
writeln() 輸出文本,並添加換行符(\n),接受一個參數:寫入到輸出流的文本
open() 打開網頁輸出流
close() 關閉網頁輸出流
創建方法 createElement() 創建新元素,並設置ownerDocument屬性,接受一個參數:要創建元素的標簽名(在HTML中不分大小寫,在XML中區分) IE中還可以傳入包含屬性的完整的元素標簽來創建新元素
createTextNode() 創建文本節點,接受一個參數:要插入節點中的文本(會按HTML/XML格式編碼),會同時設置ownerDocument 除非把新節點添加到文檔中,否則不會顯示新節點
createComment() 創建新注釋,接受注釋文本 瀏覽器不會識別<html>后代注釋,如要訪問注釋節點,需要保證是<html>元素的后代 
createCDATASection() 在XML文件中創建CDATA區域,傳入節點內容 CDATA區域只會出現XML文檔中,因此多數瀏覽器會報CDATA區域錯誤地解析為Comment或Element
createDocumentFragment() 創建文檔片段  
createAttribute() 創建新特性節點  

說明:

(1)一般情況下,不用在document對象上調用appendChild()insertBefore()removeChild()replaceChild()等方法,因為文檔類型(如果存在的話)是只讀的,而且它只能有一個元素子節點。

(2)document對象的創建方法,是實現動態加載腳本或樣式的基礎,從而可以進一步對代碼模塊化,實現按需加載,提升性能,這在Ext4庫中已經很好地應用了。動態加載的一般方法:

function loadScript(url){//動態加載外部js文件,也可以類似的加載js代碼
    var script = document.createElement("script");
    script.type = "text/javascript";
    script.src = url;
    document.body.appendChild(script);
}

function loadStyle(url){//動態加載外部css文件,也可以類似的通過style元素加載css代碼
    var link = document.createElement("link");
    link.rel = "stylesheet";
    link.type = "text/css";
    link.href = url;
    var head = document.getElementsByTagName("head")[0];
    head.appendChild(link);
}

5、Element類型

  Element類型用於表現XMLHTML元素,提供了對元素標簽名、子節點以及特性的訪問。在HTML中,元素由HTMLElement類型(或其子類型)表示,HTMLElement繼承了Element類型,並添加了對應每個HTML元素都存在的標准特性的屬性。HTMLElement類型常用屬性和方法總結如下:

類別 屬性/特性 說明
Element類型屬性 tagName 這個屬性是所有Element都有的,值和nodeName相同,表示元素標簽名,在HTML中,返回的標簽名始終大寫,在XML中,和源代碼一致
HTML元素標准特性 id id 元素在文檔中的唯一標識符
title title 有關元素的附件說明信息,一般通過工具條顯示出來
lang lang 元素內容的語言代碼,很少使用
dir dir 語言的方向,ltr:從左至右,rtl:從右至左,也很少使用
className class 元素的CSS類,因為class是保留字,所以屬性命名為className
特性屬性 attributes 這個是屬性只有Element類型使用,是一個NamedNodeMap值,屬於一個動態集合
特性方法 getAttribute() 參數:特性名。參數必須與實際的特性名相同,因此是class而不是className,給定特性不存在時,返回null,這個方法也可以獲取自定義特性,特性名不區分大小寫
setAttribute() 參數:特性名和特性值。如果已存在該特性,替換現有的值,如果不存在,就創建並賦值。通過這個方法設置特性時,會把特性名轉換為小寫形式
removeAttribute() 參數:特性名。徹底刪除元素的特性,不僅會清除特性的值,也會從元素對象中刪除特性
特性節點方法 getAttributeNode() 返回對應特性的Attr節點,element.getAttributeNode('align')相當於element.attributes['align']
setAttributeNode() 設置元素的Attr節點
其它方法 getElementsByTagName() 在當前元素的后代節點中搜索,其它用法和Document類型的同名方法一樣

說明:

(1)特殊特性:

  A、class特性,其對應DOM對象的屬性為className,在特性方法操作中參數需要傳入class,而對象屬性操作需要設置className屬性。

  B、style特性,在通過getAttribute()訪問時,返回的是style特性值中包含的CSS文本,而通過屬性來訪問它則會返回一個對象。

  C、事件處理特性,比如onclick,當在元素上使用時,onclick特性中包含的是JavaScript代碼,如果通過getAttribute()訪問,返回相應代碼的字符串,而在訪問onclick屬性時,會返回一個JavaScript函數(沒有相應特性時返回null)。

(2)關於自定義特性和屬性,在HTML5規范中,需加上“data-”前綴。

  A、給節點設置特性值時,會同步修改相應DOM對象的屬性值,但是設置自定義特性的值時,一般瀏覽器是不會為相應的DOM對象添加屬性的,然而IE也會為自定義特性創建屬性。

  B、直接給DOM對象設置屬性值時,會同步修改相應節點元素的特性值,但是一般瀏覽器中自定義屬性不會成為元素的特性,使用getArrtibute()時會返回null,然而IE會為自定義屬性創建特性。

(3)HTMLElement類型是HTML中元素的基類型,對應不同的標簽,還有很多更加具體的子類型,這些子類型也有與之相關的特性和方法,比如對應<body>標簽有HTMLBodyElement類型、對應<table>標簽有HTMLTableElement類型等。

(4)元素的子節點,可以有任意數目的子節點和后代節點,childNodes屬性則包括了所有直接子節點,但是由於不同瀏覽器在處理注釋、文本等節點的不同,會使得childNodes也不同,因此,如果需要遍歷元素子節點的話,需要添加節點類型判斷:

for(var i=0,l=element.childNodes.length; i < l; i++){
    if(element.childNodes[i].nodeType === 1)//過濾元素子節點
    {
    //對元素子節點做操作
    }
}

(5)元素的每一個特性都由一個Attr節點表示,每個節點都保存在一個NamedNodeMap對象中,這個對象和NodeList與HTMLCollection類似,是一個“動態”的,隨文檔變化而變化,它有下面的一些方法:

方法 說明
getNamesItem(name) 返回nodeName等於name的節點
removeNamedItem(name) 從列表中移除nodeName等於那么的節點,調用removeNamedItem()與在元素上調用removeAttribute()效果相同,只是前者返回被移除的Attr節點
setNamedItem(node) 向列表中添加節點,以節點的nodeName屬性為索引
item(pos) 返回位於數字pos位置處的節點

元素的attributes屬性返回的就是一個NamedNodeMap對象,其中包含一系列Attr節點,每個節點的nodeName就是特性名稱,nodeValue就是特性值,可以使用attributes屬性來遍歷元素的特性(原書第268頁):

function outputAttributes(element){
    var pairs = new Array(),
        attr;
    for(var i=0, len=element.attributes.length; i < len; i++){
        attr = element.attributes[i];
        if(attr.specified){//specified屬性表示特性值是設置還是默認的,為true表示設置的
            pairs.push(attr.nodeName + "=\"" +attr.nodeValue + "\"");
        }    
    }
    return pairs.join(" ");//以空格連接各個特性並返回
}

6、Text類型

  文本節點由Text類型表示,包含的是可以按字面解釋的純文本內容,可以包含轉義后的HTML字符,但不能包含HTML代碼,Text類型的主要屬性和方法有:

類別 屬性/方法 說明
屬性 length 文本字符數
data 文本字符,和nodeValue值相同
方法 appendData(text) 將text添加到文本節點末尾
deleteData(offset,count) 從offset指定的位置開始刪除count個字符
insertData(offset,text) 從offset指定的位置插入text
replaceData(offset,count,text) 用text替換從offset指定的位置開始到offset+count為止處的文本
splitText(offset) 從offset指定的位置將當前文本節點分成兩個文本節點
substringData(offset,count) 提取從offset指定的位置開始到offset+count為止處的字符串

說明:

(1)在Node類型中定義了一個normalize()方法,用於將相鄰的兩個或多個文本子節點合並,需要注意的是這個方法需要在文本節點的父節點上調用;與之相反的是,Text類型提供了splitText()方法,它會將原文本節點分成兩個文本節點,原文本節點將包含從開始到指定位置之前的內容,新文本節點包含剩下的內容,最終返回新文本節點。

(2)默認情況下,每個可以包含內容的元素最多只能有一個文本子節點,而且文本不能為空。通過DOM腳本操作時,可能存在有多個文件子節點的情況。

(3)設置文本節點時需要注意,字符串會經過HTML或XML編碼。

7、DocumentFragment類型

  在所有節點類型中,只有DocumentFragment是沒有對應的標記的,DOM規定DocumentFragment是一種“輕量級”的文檔,可以包含和控制節點,但不會像完整的文檔那樣占用額外的資源。DocumentFragment類型可以作為一個容器來使用,可以把后面要對文檔進行的添加、修改、移除等操作先對DocumentFragment進行,然后再將DocumentFragment添加至文檔中,從而避免DOM視圖的多次渲染。例如:

function addItems(){        
    var fragment = document.createDocumentFragment();//創建一個文檔片段作為容器
    var ul = document.getElementById("myList");
    var li = null;
    
    for (var i=0; i < 3; i++){
        li = document.createElement("li");//創建列表元素
        li.appendChild(document.createTextNode("Item " + (i+1)));//在列表元素中添加文本
        fragment.appendChild(li);//將列表元素添加中容器中,此時不會渲染頁面
    }
    
    ul.appendChild(fragment); //將容器中的節點添加到文檔中(但容器本身不會添加至文檔樹),這樣只需要渲染一次頁面    
}

需要注意的是,在DocumentFragment中的節點不屬於文檔,如果將文本中的節點添加至DocumentFragment中,該節點將會從文檔樹中移除。

8、操作表格

  <table>元素是HTML中最復雜的結構之一,在HTML DOM中還為<table>、<tbody>、<tr>等元素添加了一些屬性和方法(請注意table、tbody、tr及td之間的關系):

元素 類別 屬性/方法 說明
<table> 屬性 caption 保存着對<caption>元素(如果有)的指針
tBodies 是一個<tbody>元素的HTMLCollection
tHead 保存着對<thead>元素(如果有)的指針
tFoot 保存着對<tfoot>元素(如果有)的指針
rows 表格中所有行的HTMLCollection
方法 createTHead() 創建<thead>元素,將其放到表格中,返回引用
createTFoot() 創建<tfoot>元素,將其放到表格中,返回引用
createCaption() 創建<caption>元素,將其放到表格中,返回引用
deleteTHead() 刪除<thead>元素
deleteTFoot() 刪除<tfoot>元素
deleteCaption() 刪除<caption>元素
deleteRow(pos) 刪除指定位置的行
insertRow(pos) 向rows集合中的指定位置插入一行
<tbody> 屬性 rows 保存着<tbody>元素中行的HTMLCollection
方法 deleteRow(pos) 刪除指定位置的行
insertRow(pos) 向rows集合中的指定位置插入一行,返回新插入行的引用
<tr> 屬性 cells 保存着<tr>元素中單元格的HTMLCollection
方法 deleteCell(pos) 刪除指定位置的單元格
insertCell(pos) 想cells集合中的指定位置插入一個單元格,返回新插入單元格的引用

注意,上表中列出的是原書中提及元素的屬性和方法,並不全面,比如對應<td>元素還有HTMLTableCellElement對象,而<tr>對應對象還有rowIndex屬性等,實際使用時可以查閱相關的DOM參考手冊。這里旨在通過主要的一些屬性和方法明確概念,而非參考大全。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM