第十章 DOM
DOM是針對XML和HTML文檔的一個API:即規定了實現文本節點操控的屬性、方法,具體實現由各自浏覽器實現。
1. 節點層次
1) 文檔節點:document,每個文檔的根節點。
2) 文檔元素:即<html>元素,文檔最外層元素,文檔節點第一個子節點。
3) Node類型:
①Node是DOM中各種節點類型的基類型,共享相同的基本屬性和方法。
□ Node.Element_NODE(1);
□ Node.ATTRIBUTE_NODE(2);
□ Node.TEXT_NODE(3);
□ Node.CDATA_SECTION_NODE(4);
□ Node.ENTITY_REFERENCE_NODE(5);
□ Node.ENTITY_NODE(6);
□ Node.PROCESSING_INSTRUCTION_NODE(7);
□ Node.COMMENT_NODE(8);
□ Node.DOCUMENT_NODE(9);
□ Node.DOCUMENT_TYPE_NODE(10);
□ Node.DOCUMENT_FRAGMENT_NODE(11);
□ Node.NOTATION_NODE(12);
每種節點的nodeType屬性返回上述類型之一,為一常量。
通過節點nodeType屬性與數字值比較,可得節點類型。
②nodeName和nodeVlue屬性。
③每個節點的子節點信息保存在childNodes屬性中,childNodes屬性中保存一個NodeList對象。
□ NodeList對象,類數組對象,有length屬性,但非Array的實例。
□ 訪問NodeList中的節點,可以通過方括號,也可以使用item()方法。
var firstChild = someNode.ChildNodes[0];
var secondChild = someNode.ChildNodes.item(1);
var count = someNode.childNodes.length;
□ 將NodeList轉為數組對象。
function convertToArray(nodes){
var array = null;
try{
array = Array.prototype.slice.call(nodes,0); //非IE
}catch(ex){
array = new Array();
for(var i = 0,len = nodes.length; i < len; i++){
array.push(nodes[i]);
}
}
return array;
}
④parentName屬性:指向文檔樹中父節點。
⑤previousSibling屬性和nextSibling屬性:前一個/下一個同胞節點。
⑥firstChild屬性和lastChild屬性:前一個/後一個子節點。
⑦hasChildNodes()方法:含子節點返回true,反之返回false。
⑧appendChild()方法:向childNodes列表末尾添加一個子節點,返回新增節點。
⑨insertBefore()方法:兩個參數:要插入的節點和作為參照的節點。返回新增節點。
⑩replaceChild()方法:兩個參數:要插入的節點和要替換節點。返回新增節點。
⑾removeChild()方法:移除節點。
⑿cloneNode()方法:接受一個布爾值。true為深復制,復制節點及子節點。false為淺復制,僅復制本身節點。
⒀nomalize()方法:處理文檔樹中文本節點。
4) Document類型(針對document對象)
①Document類型表示文檔,是HTMLDocument類型的一個實例,表示整個HTML頁面。document對象是window對象的一個屬性,可作全局對象訪問。
②documentElement屬性;該屬性始終指向HTML頁面中的<html>元素。
③body屬性;直接指向<body>元素。
④doctype屬性:訪問<!DOCTYPE>,各浏覽器支持不一致。用處有限。
⑤title屬性:可獲得和設置title的文本。
⑥URL屬性:地址欄中的URL。
⑦domain屬性:頁面的域名(可設置,有限制)
⑧referrer屬性:保存鏈接到當前頁面的那個頁面的URL
⑨getElementById()方法:傳入元素的ID,返回元素節點。
⑩getElementsByTagName()方法:傳入元素名,返回NodeList。
□ 在HTML中返回一個HTMLCollection對象,與NodeList類似。
□ 訪問HTMLCollection對象:方括號語法,item()方法,namedItem()方法,HTMLCollection對象還可通過元素的name特性取得集合中的項。
⑾getElementsByName()方法:返回帶有給定name特性的所有元素。
⑿特殊集合,這些集合都是HTMLCollection對象。
□ document.anchors:包含文檔中所有帶name特性的<a>元素。
□ document.applets:包含文檔中所有的<applet>元素。
□ document.forms:包含文檔中所有的<form>元素。
□ document.images:包含文檔中所有的<img>元素。
□ document.links:包含文檔中所有帶href屬性的<a>元素。
⒀DOM一致性檢測:document.implementation屬性就為此提供相應信息和功能的對象。
□ 此對象有一個方法:hasFeature():兩個參數:要檢測的DOM功能的名稱及版本號。
□ 功能:Core、XML、HTML、Views、StyleSheets、CSS、CSS2、Events、UIEvents、MouseEvents、MutationEvents、HTMLEvents、Range、Traversal、LS、LS-Async、Validation
⒁將輸出流寫入網頁:write()、writeln()、open()和close()。
5) Element類型
①用於表現XML或HTML元素,提供了對元素標簽名、子節點及特性的訪問。
②可用nodeName屬性或tagName屬性訪問元素標簽名。tagName會在HTML中返回大寫標簽名,在XML中會原樣返回。
(1)HTML元素
■所有HTML元素都由Element類型子類HTMLElement類型表示,其屬性如下:
□id:元素在文檔中的唯一標識符。
□title:有關元素的附加說明信息,一般通過工具提示條顯示出來。
□lang:元素內容的語言代碼,很少使用。
□dir:語言的方向,值為“ltr”或“rtl”,很少使用。
□className:與元素的class特性對應,即為元素指定的css類。
上述這些屬性都可以用來取得或修改相應的特性值。
(2)取得特性
①getAttribute()方法:可以取得公認特性和自定義特性的值。
□任何元素的所有特性,也可以通過DOM元素本身的屬性來訪問。alert(div.id);alert(div.align);
□只有公認的(非自定義的)特性才會以屬性的形式添加到DOM對象中。
□style特性通過getAttribute()訪問得特性值CSS文本。通過屬性訪問得一個對象。
□onclick特性:getAttribute(),返回JS代碼字符串。通過屬性訪問返回一個函數。
□一般通過屬性取得特性,只有在取自定義特性值時,才用getAttribute()。
(3)設置特性
①setAttribute():兩個參數,要設置的特性名和值。如果特性已經存在,setAttribute()會以指定的值替換現有的值;如果特性不存在,setAttribute()則創建該屬性並設置相應的值。
□此方法既可以操作HTML特性也可以操作自定義特性。
□所有公認特性都是屬性,因此直接給屬性賦值可以設置特性的值。
□IE6、IE7中,setAttribute()設置class、style,事件處理程序會每沒有任何效果。但不建議通過屬性設置特性。
②removeAttribute():徹底刪除元素的特性(IE6不支持)。
(4)attributes屬性
①此屬性包含一個NameNodeMap對象(類似NodeList的“動態”集合)。元素的每一個特性有一個Attr節點表示,每個節點保存在NameNodeMap對象中。
□getNameItem(name):返回返回nodeName屬性等於name的節點
□removeNameItem(name):從列表中移除nodeName屬性等於name的節點。
□setNameItem(node):向列表中添加節點,以節點的nodeName屬性為索引。
□item(pos):返回位於數字pos位置的節點。
(5)創建元素
①使用document.createElement()方法可以創建新元素。參數:標簽名。
②IE中createElement()可傳入完整的元素標簽,防止IE7以及IE6問題:
□不能設置動態創建的<iframe>元素的name特性
□不能通過表單的reset()方法重設動態創建的<input>元素。
□動態創建的type特性值為“reset”的<button>元素重設不了表單。
□動態創建的一批name相同的單選按鈕彼此毫無關系。
if(client.browser.ie&&client.brower.ie<=7){
var iframe = document.createElement("<iframe name = \"myframe\"></iframe>");
}
(6)元素的子節點
①元素的childNode屬性中包含了它的所有子節點:元素、文本節點、注釋或處理指令。
□一般浏覽器解析childNodes時會把元素節點空間的空白符解析為文本節點,而IE不會。因此遍歷執行操作需要檢查nodeType屬性。
For(var i= 0, len = element.childNode.length; i> len; i++){
If(element.childNodes[i].nodeType == 1){
//執行某些操作
}
}
□元素節點支持getElementByTagName()方法。
6) Text類型
①文本節點由Text類型表示,包含純文本內容,不含HTML代碼。
□不支持(沒有)子節點
□nodeValue屬性、data屬性可訪問Text節點中包含的文本。
②操作文本節點
□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為止處的字符串。
□文本節點有一個length屬性,保存節點中字符的數目。而nodeValue.length和data.length中也保存著同樣的值。
○修改文本節點值時字符串經HTML編碼,特殊符號轉義:
div.firstChild.nodeValue = "Some<strong>other</strong>"; //輸出Some<Strong>other</strong>
③創建文本節點
□使用document.createTextNode()創建新文本節點。參數:要插入的文本(自動按HTML或XML格式編碼)
④規范化文本節點
□DOM操作中可向同一父元素插入多個文本節點,但會造成混亂。
□通過node類型繼承的normalize()方法可合並同一父元素上的所有文本節點。
⑤分割文本節點
□Text類型方法splitText():與node方法normalize()相反傳入一個位置參數,從該位置拆分成兩個文本節點,返回後面的文本節點。
□分割文本節點是從文本節點中提取數據的一種常用DOM解析技術。
7) Comment類型
□Comment類型與Text類型繼承自相同的基類,因此它擁有除splitText()之外所有字符串操作方法。
□可使用document.createComment()創建注釋節點。
8) CDATASection類型
□CDATASection類型只針對基於XML的文檔表示CDATA區域。
□CDATASection類型繼承自Text類型,擁有除splitText()之外所有方法。
□各大浏覽器均無法正確解析這種類型。
9) DocumentType類型
□IE不支持
□在DOM1級中DocumentType對象不能動態創建。
□DocumentType對象保存在document.doctype中。
◇name屬性:文檔類型名稱。
◇entities屬性:文檔類型描述的實體的NamedNodeMap對象。
◇notation屬性:文檔類型描述的符號的NamedNodeMap對象。
10) DocumentFragment類型
□DOM規定文檔片段(document fragment)是一種“輕量級”的文檔,可以包含和控制節點,但不會像完整的文檔那樣占用額外資源。
□文檔片段不能添加到文檔中,只會添加文檔片段作為“倉庫”保存的節點。
var fragment = document.createDomFragment();
var ul = document.getElementById("myList");
var li = null;
for(var i= 0; i<3; i++){
li = document.createElement("li");
li.appendChild(document.createTextNode("haha"));
fragment.appendChild(li);
}
ul.appendChild.appendChild(li);
□可以通過appendChild()或insertBefore()將文檔片段中內容添加到文檔中。
11) Attr類型
□是節點,但特性不被認為是DOM文檔樹的一部分。很少使用這類節點。
□Attr對象3個屬性:
◇name屬性:特性名稱(如nodeName)
◇value屬性:特性的值(如nodeValue)
◇specified屬性:布爾值,用以區別特性是在代碼中指定的還是默認的。
□不建議直接訪問特性節點。建議用getAttribute()
setAttribute()、removeAttribute()方法。0
二、DOM擴展
1. 呈現模式
①document對象的compatMode屬性
□值為“CSS1Compat”即標准模式。
□值為“BackCompact”即混雜模式。
②IE8為document對象引入documentMode新屬性,返回數值:5(混雜模式)、7(仿真模式)、8(標准模式)
2. 滾動
□滾動方法都是作為HTMLElement類型的擴展存在,可再元素節點上使用。
□scrollIntoView(bool):滾動浏覽器窗口或容器元素,以便在視口(viewport)中看到元素。(參數為true或省略,則滾動到頂部)。
□scrollIntoViewIfNeeded(bool):只在當前元素在視口中不可見的情況下,才滾動浏覽器窗口或容器元素,讓當前元素可見。(Safari和Chorme實現)。
□scrollByLines(lineCount):將元素的內容滾動指定的行數的高度,lineCount可為正/負數。(safari和Chrome可用)
3. children屬性
□children屬性中只包含元素的子節點中那些也是元素的節點。
□作為HTMLCollection對象。
□Firefox不支持,IE中的children集合會包含注釋節點。
4. contains()方法
□應在作搜索起點的祖先節點上調用,傳入要檢測的節點作參數。返回true則傳入節點作為當前節點後代。
□Firefox不支持contains()方法,在DOM3級實現替代的compareDocumentPosition()方法,確定兩個節點間關系。Safari2.x以下版本不支持。
兼容各浏覽器代碼:
function contains(refNode, otherNode){
if(typeof refNode.contains == "function" && (!client.engine.webkit || client.engin.webkit >=552)){
return refNode.contains(otherNode);
}else if(tyepof refNode.compareDocumentPosition =="function"){
return !!(refNode.compareDocumentPosition(otherNode)&16);
}else{
var node = otherNode.parentNode;
do{
if(node === refNode){
Return true;
}else{
node = node.parentNode;
}
}while(node !== null);
return false;
}
}
5. 操作內容
①innerText屬性
□可讀取節點文檔書中由淺入深的所有文本。
□通過inner.Text屬性設置節點文本,會移除先前存在的所有子節點,完全改變了DOM樹。
□賦給此屬性的文本將自動進行HTML編碼。
□Firefox不支持innerText,但支持作用類似的textContent屬性。
□innerText:支持IE、Safari、Opera、Chrome
□textContent:支持Safari、Firefox、Opera、Chrome
兼容代碼:
function getInnerText(element){
return (typeof element.textContent == "string")?element.textContent:element.innerText;
}
function setInnerText(element, text){
if(typeof element.textContent == "string"){
element.textContent = text;
}else{
element.innerText = text;
}
}
②innerHTML屬性
□讀取信息時,返回當前元素所有子節點的HTML表現。
□寫入信息時,會按照指定的值創建新的DOM子樹,並以該子樹替換當前元素的所有子節點。
□innerHTML限制:插入<script>元素不會被執行。插入<style>也有類似問題。
□並不是所有元素都有innerHTML屬性。不支持的元素有<col>、<colgroup>、<frameset>、<head>、<html>、<style>、<table>、<tbody>、<thead>、<tfoot>、<title>和<tr>。
③outerText屬性
□與innerText基本相同,區別在於寫入信息時,會替換包含節點
□Firefox不支持
□不建議使用。
④outerHTML屬性
□與innerHTML基本相同,區別在於寫入信息時,會替換包含節點。
□Firefox不支持
□不建議使用
⑤內存與性能問題
□innerText、innerHTML、outerText、outerHTML替換子節點可能會導致浏覽器的內存問題。被刪除的子樹中的元素設置的事件處理程序或具有值為JS對象的屬性,依舊存在於內存中。
□插入大量新HTML時,使用HTML要比通過多次DOM操作有效率得多。
三、DOM操作技術
1. 動態腳本
①插入外部文件:
function loadScript(url){
var script = document.createElement("script");
script.type = "text/javascript";
script.src = url;
document.body.appendChild(script);
}
②指定JavaScript代碼方式
□IE中將<script>視為一個特殊元素,不允許DOM訪問其子節點。
兼容方式:
function loadScriptString(code){
var script = document.createElement("script");
script.type = "text/javascript";
try{
script.appendChild(document.createTextNode(code));
}catch(ex){
script.text = code;
}
document.body.appendChild(script);
}
2. 動態樣式
①外部鏈接
function loadStyles(url){
var link = document.createElement("link");
link.rel = "stylesheet";
link.href = url;
var head = document.getElementsByTagName("head")[0];
head.appendChild(link);
}
②使用<style>元素來包含嵌入式CSS
□IE將<style>視為一個特殊的、與<script>類似的節點,不允許訪問其子節點。
□在重用一個<style>元素並再次設其styleSheet.cssText屬性,有可能導致浏覽器崩潰。
fucnction loadStyleString(css){
var style = document.createElement("style");
style.type = "text/css";
try{
style.appendChild(document.createTextNode(css));
}catch(ex){
style.styleSheet.cssText = css;
}
var head = document.getElementsByTagName("head")[0];
head.appendChild(style);
}
3. 操作表格
①為<table>元素添加的屬性和方法
□caption:保存著對<caption>元素(如果有)地指針;
□tBodies:是一個<tobdy>元素的 HTMLCollection;
□tFoot:保存著對<tfoot>元素(如果有)的指針;
□tHead:保存著對<thead>元素(如果有)的指針;
□rows:是一個表格中所有行的HTMLCollection;
□createTHead():創建<thead>元素,將其放到表格中,返回引用。
□createCaption():
□deleteTHead():刪除<thead>元素。
□deleteTFoot():
□deleteCaption():
□deleteRow(pos):
□insertRow(pos):向rows集合中指定位置插入一行。
②為<tbody>元素添加的屬性和方法有:
□rows:保存著<tbody>元素中行的HTMLCollection。
□deleteRow(pos):刪除指定位置的行;
□insertRow(pos):向rows集合中的指定位置插入一行,返回插入行的引用。
③為<tr>元素添加的屬性和方法
□cells:保存著<tr>元素中單元格的HTMLCollection;
□deleteCell(pos):刪除指定位置的單元格。
□insertCell(pos):向cells集合中指定位置插入一單元格,返回插入的單元格引用。
4. 使用NodeList
□NodeList及其“近親”NamedNodeMap(Element類型中的attribute屬性)和HTMLCollection,這三個集合都是“動態的”,每當文檔結構發生變化時,它們都會得到更新。
□盡量減少訪問NodeList的次數。因每次訪問NodeList,都會運行一次基於文檔的查詢。可考慮將從NodeList取得的值緩存起來。
第十一章 DOM2和DOM3
一、DOM變化
1. 針對XML命名空間的變化
①Node類型的變化
■DOM2級中,Node類型包含下列特定於命名空間的屬性。
□localName:不帶命名空間前綴的節點名稱。
□namespaceURI:命名空間URI,未指定則為null。
□prefix:命名空間前綴,未指定則為null。
■DOM3級
□isDefaultNamespace(namespaceURI):指定的namespaceURI在當前的默認命名空間的情況下返回true。
□lookupNamespaceURI(prefix):返回給定prefix的命名空間。
□lookupPrefix(namespaceURI):返回給定namespaceURI的前綴。
②Document類型變化
■DOM2級中,與命名空間有關
□createElementNS(namespaceURI, tagName):使用給定的tagName創建一個屬於命名空間namespaceURI的新元素。
□createAttributeNS(namespaceURI, attributeName):
□getElementBytagNameNS(namespaceURI, tagName):
③Elment類型的變化
■DOM2級核心中
□getAttributeNS(namespaceURI, localName)
□getAttributeNodeNS(namespaceURI, localName)
□getElementsByTagNameNS(namespaceURI, localName)
□hasAttributeNS(namespaceURI, localName)
□removeAttributeNS(namespaceURI, localName)
□setAttributeNodeNS(attrNode):設置屬於命名空間namespaceURI的特性節點。
④NamedNodeMap類型的變化
□getNamedItemNS(namespaceURI, localName)
□removeNamedItemNS(namespaceURI, localName)
□setNamedItemNS(node):添加node,這個節點已事先指定了命名空間信息。
2. 其他方面的變化
①DocumentType類型的變化
□新增3個屬性:publicId、systemId和internalSubset
□publicId、systemId表示文檔類型聲明中的兩個信息段。
□internalSubset:用於訪問包含在文檔類型聲明中的額外定義。
②Document類型的變化
□importNode()方法:從一個文檔中取得一個節點,然後將其導入到另一個文檔,使其成為文檔結構的一部分。
◇每個節點都有一個ownerDocument屬性,表示所屬文檔。
◇appendChild()傳入節點屬於不同文檔,會導致錯誤。
◇importNode()方法與Element的cloneNode方法非常相似,接受兩個參數:要復制的節點和一個表示是否復制子節點的布爾值。返回原節點的副本,但能夠在當前文檔中使用。
□defaultView屬性:保存著一個指針,指向擁有給定文檔的窗口(或框架)。
◇IE不支持此屬性,但有等價屬性parentWindow。
◇確定文檔歸屬窗口兼容代碼:
var parentWindow = document.defaultView || document.parentWindow;
□createDocuemntType():創建一個新的DocumentType節點,接受三個參數:文檔類型名稱、publicId、systemId。
□createDocument()方法:創建新文檔。三個參數:針對文檔中元素的namespaceURI、文檔元素的標簽名、新文檔的文檔類型。
□“DOM2級HTML”模塊為document.implementation新增一個方法,名叫createHTMLDocument()。只接受title中的文本作為參數。返回新HTML文檔。包括<html>、<head>、<title>和<body>。
③Node類型的變化
□isSupported()方法:與DOM1級中document.implementation引入的hasFeature()方法類似。用於確定當前節點具有什麼能力。兩個參數:特性名和特性版本號。
□isSameNode()方法:傳入節點,與引用節點為同一個節點則返回true。
□isEqualNode()方法:傳入節點,與引用節點相等返回true。
□setUserData()方法:將數據指定給節點。三個參數:要設置的鍵,實際的數據和處理函數。
◇處理函數接受5個參數:表操作類型的數值(1復制2導入3刪除4重命名)、數據鍵、數據值、源節點和目標節點。
var div = document.createElement("div");
div.setUserData("name","Nicholas",fucntion(operation, key, value, src, dest){
if(operation == 1){
dest.setUserData(key,value);
}
});
④框架的變化
□框架和內嵌框架分別用HTMLFrameElement和HTMLIFrameElement表示。
□contentDocument屬性:DOM2級中框架、內框架的新屬性。此屬性包含一個指針,指向表示框架內容的文檔對象。
□contentDocument屬性是Document類型實例,因此可以像使用其他HTML文檔一樣使用它,包括所有屬性和方法。
□IE8之前不支持框架中的contentDocument屬性,但支持一個叫contentWindow屬性,返回框架的window對象。
□訪問內嵌框架文檔對象兼容代碼:
var iframe = document.getElementById("myIframe");
var iframeDoc = iframe.contentDocument || iframe.contentWindow.document;
二、樣式
1. 訪問元素的樣式
①style屬性
□支持style特性的HTML元素在JS中都有一個對應的style屬性。
□這style對象是cssStyleDeclaration的實例,包含通過style特性指定的樣式。
□在style特性指定的任何CSS屬性都將表現為這個style對象的相應屬性。對於使用短劃線的CSS屬性名,必須轉換成駝峰大小寫形式才能通過js訪問。其中float屬性不可直接使用。IE用styleFloat訪問,其他浏覽器用cssFloat訪問。
□如果沒有為元素設置style特性,那style對象中可能會包含一些默認值,但這些值不反映元素正確的樣式信息。
②DOM樣式屬性和方法
1)DOM2級還為style對象定義了一些屬性和方法。在提供特性值時也可修改樣式。
□cssText:訪問style特性中的css代碼。
□length:應用給元素的css屬性數量。
□parentRule:表示css信息的CSSRule對象。
□getPropertyCSSValue(propertyName):返回包含給定屬性值的CSSValue對象,含兩個屬性cssText和cssValueType。
□getProperytPriority(propertyName):如果給定的屬性使用了!important設置,則返回“important”,否則返回空字符串。
□item(index):返回給定位置的CSS屬性的名稱。
□removeProperyt(propertyName):從樣式中刪除給定屬性。
□setProperty(propertyName, value, priority):將給定屬性設置為相應的值,並加上優先權標志(“important”或者一個空字符串)。
2)使用
□設計length屬性的目的,就是將其與item()方法配套使用。以便迭代元素中定義的css屬性。
□Firefox、Safari、Opera9及更高、chrome均支持這些屬性和方法。IE只支持cssText。
□getPropertyCSSValue()只有Safari和chrome支持。
③計算的樣式
□“DOM2級樣式”增強了document.defaultView,提供了getComputedStyle()方法。
□getComputedStyle():2個參數:要取得計算樣式的元素和一個偽元素字符串(如:after)。如果不需要偽元素信息,第二個參數可以為null。返回一個CSSStyleDeclaration對象(與style屬性類型相同)。
□IE不支持getComputedStyle()方法。但IE中有style特性的元素均有一個currentStyle屬性(CSSStyleDeclaration的實例)包含當前元素全部計算後的樣式。
□計算樣式兼容代碼:
var myDiv = document.getElementById("myDiv");
var computedStyle = document.defaultView.getComputedStyle(mydiv, null) || myDiv.currentStyle;
alert(computedStyle.width);
alert(computedStyle.height);
2. 操作樣式表
①CSSStyleSheet類型
1)操控<link>元素樣式表和<style>元素樣式表。
2)確定浏覽器是否支持DOM2級樣式表。
var supportDOM2StyleSheets = document.implementation.hasFeature("StyleSheet","2.0");
3)CSSStyleSheet繼承自StyleSheet,後者可以作為一個基礎接口來定義非CSS樣式表。從StyleSheet接口繼承而來的屬性如下:
□disabled:表示樣式表是否被禁用的布爾值。可讀/寫。設為true則禁用。
□href:如果樣式表是通過<link>包含的,則是樣式表的URL,否則是null。
□media:當前樣式表支持的所有媒體類型的集合。與所有DOM集合一樣,有length屬性和item()方法。
□ownerNode:指向擁有當前樣式表的節點的指針。若通過@import導入則屬性為null。IE不支持此屬性。
□parentStyleSheet:當前樣式表是通過@import導入的情況下,這個屬性指向導入它的樣式表的指針。
□title:ownerNode中title屬性的值。
□type:表示樣式表類型的字符串。對css樣式表而言,這個字符串是“type/css”。
■以上屬性除disabled外,全部只讀。
4)CSSStyleSheet在上述基礎上添加以下屬性和方法:
□cssRule:樣式表中包含的樣式規則的集合。IE不支持,但有類似的rules屬性。
□ownerRule:如果樣式表是通過@import導入的,這個屬性就是一個指針,指向表示導入的規則;否則為null。IE不支持這個屬性。
□deleRule(index):刪除cssRules集合中指定位置的規則。IE不支持,但支持一類似的removeRule()方法。
□insertRule(rule,index):向cssRules集合中指定的位置插入rule字符串。IE不支持,但支持類似的addRule()方法。
5)獲取樣式表對象
□應用於文檔的所有樣式表是通過document.styleSheets集合來表示的。通過這個集合的length屬性可以獲取文檔中樣式表的數量,而通過方括號語法或item()方法可以訪問每一個樣式表。
var sheet = null;
for(var i = 0, len = document.styleSheets.length; i < len; i++){
sheet = document.styleSheets[i];
alert(sheet.href);
}
□不同浏覽器的document.styleSheets返回的樣式表也不同。所有浏覽器都會包含<style>元素和rel特性被設置為“stylesheet”的<link>元素引入的樣式表。
□可直接通過<link>或<style>元素取得CSSStyleSheet對象。DOM規定了一個包含CSSStyleSheet對象的屬性,名叫sheet;IE不支持,但IE支持類似的styleSheet屬性。
function getStyleSheet(element){
return element.sheet || element.styleSheet;
}
//取得第一個<link>元素引入的樣式表
var link = document.getElementsByTagName("link")[0];
var sheet = getStylesheet(link);
②CSS規則
1)CSSRule對象表示樣式表中的每一條規則,是一個供其他多種類型繼承的基類型,其中最常見的就是CSSStyleRule類型,表示樣式信息。
2)CSSStyleRule對象包含下列屬性:
□cssText:返回整條規則對應的文本,IE不支持。
□parentRule:如果當前規則是導入的規則,這屬性引用的就是導入規則;否則這個值為null。IE不支持。
□parentStyleSheet:當前規則所屬的樣式表。IE不支持。
□selectorText:返回當前規則的選擇符文本。
□style:一個CSSStyleDeclaration對象,可以通過它設置和取得規則中特定的樣式值。
□type:表示規則類型的常量值。對於樣式規則,這個值是1。IE不支持。
■其中最常用的屬性是cssText、selectorText和style。
□cssText屬性與style.cssText屬性類似但並不相同。前者包含選擇符文本和圍繞樣式信息的花括號,後者只含樣式信息(類似於元素的style.cssText)。而cssText只讀,style.cssText可讀/寫。
var sheet = document.styleSheets[0];
var rules = sheet.cssRules || sheet.rules;
var rule = rules[0];
alert(rule.style.backgroundColor);
rule.style.backgroundColor="red";
③創建規則
□insertRule()方法:接受兩個參數:規則文本和表達在哪裡插入規則的索引。//IE不支持
sheet.insertRule("body{background-color:silver}",0); //DOM方法。
□addRule():IE中支持。兩個必選參數:選擇符文本和CSS樣式信息。一個可選參數:插入規則的位置。//僅IE支持
Sheet.addRule("body","background-color:silver",0);
□跨浏覽器兼容
function insertRule(sheet, selectorText, cssText, position){
if(sheet.insertRule){
sheet.insertRule(selectorText + "{" + cssText + "}", position);
}else if(sheet.addRule){
sheet.addRule(selectorText,cssText,position);
}
}
調用:insertRule(document.styleSheets[0], "body", "background-color:red", 0);
□當需要添加的規則多時,操作繁瑣。建議采用動態加載樣式表。
④刪除規則
□sheet.deleteRule(0); //DOM方法,IE不支持
Sheet.removeRule(0); //IE方法,兩者都是傳入要刪除的規則的位置。
□跨浏覽器兼容:
function deleteRule(sheet, index){
if(sheet.deleteRule){
Sheet.deleteRule(index);
}else if(sheet.removeRule){
Sheet.removeRule(index);
}
}
調用方式:deleteRule(document.styleSheets[0],0);
□這種做法不是實際web開發中常見做法。刪除規則會影響CSS層疊效果。慎用。
3. 元素大小(非DOM2級樣式,但所有浏覽器支持)
①偏移量(offset dimension)
定義:包括元素在屏幕上占用的所有可見的空間。元素的可見大小由其高度、寬度決定,包括所有內邊距、滾動條和邊框大小(注意,不包括外邊距)
□offsetHeight:元素在垂直方向上占用的空間大小,以像素計。
□offsetWidth:元素在水平方向上占用的空間大小,以像素計。
□offsetLeft:元素左邊框至包含的左內邊框之間的像素距離。
□offsetTop:元素的上邊框至包含元素的上內邊框之間的像素距離。
△其中offsetLeft和offsetTop屬性與包含元素有關,包含元素的引用保存在offsetParent屬性中。offsetParent屬性不一定與parentNode的值相等。
△這些屬性是只讀的,每次訪問需要重新計算。若重復使用,應保存在局部變量中。
②客戶區大小(client dimension)
元素內容及其內邊距所占大小。滾動條占用空間不算在內。
□clientWidth屬性:元素內容區寬度加上左右內邊距寬度。
□clientHeight屬性:元素內容區高度加上上下內邊距高度。
□確定浏覽器視口大小可用document.documentElement或document.body(IE6的混雜模式中)。
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
};
}
}
③滾動大小
指包含滾動內容的元素的大小。
□scrollHeight:在沒有滾動條的情況下,元素內容的總高度。
□scrollWidth:在沒有滾動條的情況下,元素內容的總寬度。
□scrollLeft:被隱藏在內容區域左側的像素數。通過設置這個屬性可以改變元素的滾動位置。
□scrollTop:被隱藏在內容區域上方的像素數。通過設置這個屬性可以改變元素的滾動位置。
◇在確定文檔的總高度時(包括基於視口的最小高度時),必須取得scrollWidth/clientWidth和scrollHeight/clientHeight中的最大值,保證跨浏覽器。
Var docHeight = Max.max(document.documentElement.scrollHeight,document.documentElement.clientHeight);
Var docWidth = Max.max(document.documentElement.scrollWidth,document.docuemntElement.clientWidth);
注:對於運行在混雜模式下的IE,則需要用document.body代替document.documentElement。
◇通過scrollLeft和scrollTop屬性既可以確定元素當前的滾動狀態,也可以設置元素滾動位置。
function scrollToTop(element){
if(element.scrollTop != 0){
element.scrollTop = 0;
}
}
④確定元素大小
□IE、Firefox3及更高和Opera9.5及更高為每一個元素提供了一個getBoundingClientRect()方法。這個方法返回一個矩形對象,含4個屬性:left、top、right和bottom。這些屬性給出了元素在頁面中相對於視口的位置。但IE認為左上角坐標為(2,2),其他浏覽器認為是(0,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
}
}
}
三、遍歷
①概況:“DOM2級遍歷和范圍”模塊定義了兩個用於輔助完成順序遍歷DOM結構的類型:NodeIterator和TreWalker。這兩個類型能夠基於給定的起點對DOM結構執行深度優先(depth-first)的遍歷操作。
□在與DOM兼容版本中可訪問這些對象。IE不支持遍歷。
□檢查浏覽器對DOM2級遍歷能力的支持。
var supportsTraversals = document.implementation.hasFeature("Traversal","2.0");
var supportsNodeIterator = (typeof document.createNodeIterator == "function");
var supportsTreeWalker = (typeof document.createTreeWalker == "function");
②NodeIterator
1)可以使用document.createNodeIterator()方法創建它的新實例。
接受4個參數:
□root:想要作為搜索起點的樹種的節點。
□whatToShow:表示要訪問哪些節點的數字代碼。
□filter:是一個NodeFilter對象,或者一個表示應該接受還是拒絕某種特定節點的函數。
□entityReferenceExpansion:布爾值,表示是否要擴展實體引用。此參數在HTML頁面中沒有用。
■whatToShow參數是一個位掩碼,其值以常量形式在NodeFilter類型中定義。
□NodeFilter.SHOW_ALL:顯示所有類型的節點。
□NodeFilter.SHOW_ELEMENT:顯示元素節點。
□NodeFilter.SHOW_ATTRIBUTE:顯示特性節點。由於DOM結構原因,實際上不能使用這個值。
□NodeFilter.SHOW_TEXT:顯示文本節點。
□NodeFilter.SHOW_CDATA_SECTION:顯示CDATA節點。對HTML無用。
□NodeFilter.SHOW_ENTITY_REFERENCE:顯示實體引用節點。對HTML無用。
□NodeFilter.SHOW_ENTITYPE:顯示實體節點。對HTML無用。
□NodeFilter.SHOW_PROCESSING_INSTRUCTION:顯示處理指令節點。對HTML無用。
□NodeFilter.SHOW_COMMENT:顯示注釋節點。
□NodeFilter.SHOW_DOCUMENT:顯示文檔節點。
□NodeFilter.SHOW_DOCUMENT_TYPE:顯示文檔類型節點。
□NodeFilter.SHOW_DOCUMENT_FRAGMENT:顯示文檔片段節點。對HTML無用。
□NodeFilter.SHOW_NOTATION:顯示符號節點。對HTML無用。
◇除了NodeFilter_SHOW_ALL外,可以使用按位或操作符來組合多個選項。
var whatToShow = NodeFilter.SHOW_ELEMENT | NodeFilter.SHOW_TEXT;
◇用filter參數自定義NodeFilter對象或一個過濾器函數。
NodeFilter對象只有一個方法,即acceptNode(),訪問則放回NodeFilter.FILTER_ACCEPT,不訪問則返回NodeFilter.FILTER_SKIP
例1:迭代器
var filter= {
acceptNode:function(node)P{
return node.tagName.toLowerCase()=="p" ?
NodeFilter.FILTER_ACCEPT : NodeFilter.FILTER_SKIP;
}
}
};
var iterator = document.createNodeIterator(root,NodeFilter.SHOW_ELEMENT,filter,false);
例2:filtr參數也可以是一個與acceptNode方法類似的函數。
var filter = function(node){
return node.tagName.toLowerCase() == "p" ?
NodeFilter.FILTER_ACCEPT : NodeFilter.FILTER_SKIP;
};
var iterator = document.createNodeIterator(root,NodeFilter.SHOW_ELEMENT,filter,false);
2)NodeIterator類型主要兩個方法:nextNode()和previousNode()。
var div = document.getElemetnById("div1");
var iterator = document.createNodeIterator(div,NodeFilter.SHOW_ELEMENT,null,false);
var node = iterator.nextNode();
while(node !== null){
alert(node.tagName);
node = iterator.nextNode();
}
③TreeWalker
1)TreeWalker是NodeIterator的一個更高級版本。除了有nextNode()和previousNode()外,還有下列用於在不同方向上遍歷DOM結構的方法:
□parentNode():遍歷到當前節點的父節點。
□firstChild():遍歷到當前節點的第一個子節點。
□lastChild():便歷到當前節點的最後一個子節點。
□nextSibling():遍歷到當前節點的下一個同輩節點。
□previousSibling():遍歷到當前節點的上一個同輩節點。
2)創建TreeWalker對象要使用document.createTreeWalker()方法,接受4個參數,與document.createNodeIterator()方法相同。
□其中第3個參數filter返回值除了可以返回NodeFilter.FITER_ACCEPT和NodeFilter.FILTER_SKIP外,還可以使用NodeFILTER_REJECT(作用:跳過相應的節點及該節點的整個子樹)。
□TreeWalker強大之處在於能夠在DOM結構中沿任何方向移動。
◇例:遍歷DOM樹,即使不定義過濾器,也可以取得所有<li>元素。
var div = document.getElementById("div1");
var walker = document.createTreeWalker(div,NodeFilter.SHOW_ELEMENT,null,false);
walker.firstChild();
walker.nextSibling();
var node = walker.firstChild();
while(node !== null)}
alert(node.tagName);
node = walker.nextSibling();
}
□TreeWalker類型還有一個屬性,名叫currentNode,表示任何遍歷方法在上一次遍歷中返回的節點。
四、范圍
基本定義:
通過范圍可以選擇文檔中的一個區域,而不必考慮節點的界限(選擇在後台完成,對用戶看不見)。在常規DOM操作中不能更有效地修改文檔時,使用范圍往往可以達到目的。
1.DOM中的范圍
①概況:
1)DOM2級在Document類型中定義了createRange()方法。
□檢查是否支持范圍:
var supportsRange = document.implementation.hasFeature("Range","2.0");
var alsoSupportsRange = (typeof document.createRange == "function");
□創建DOM范圍: var range = document.createRange();
2)Range類型實例中,提供當前范圍在文檔中的位置信息的屬性:
□startContainer:包含范圍起點的節點(即選取中第一個節點的父節點)
□startOffset:范圍在startContainer中起點的偏移量。如StartContainer是文本節點,則startOffset就是范圍起點之前跳過的字符數量。否則,startOffset就是范圍中第一個子節點在父節點中的索引。
□endContainer:包含范圍終點的節點(即選取最後一個節點的父節點)。
□endOffset:范圍在endContainer中終點的偏移量(規則如startoffset)
□commonAncestorContainer:startContainer和endContainer共同的祖先節點在文檔樹種位置最深的那個。
②用DOM范圍實現簡單選擇:
1)使用范圍選擇文檔中的一部分:
□selectNode():傳入一個DOM節點,選擇整個節點,包括子節點作范圍。
□selectNodeContents():傳入一個DOM節點,只選擇子節點作范圍。
2)更精細地控制范圍:
□setStartBefore(refNode):將范圍的起點設置在refNode之前,因此refNode也就是范圍選取中第一個子節點。范圍的位置屬性自動更新。
□setStartAfter(refNode):
□setEndBefore(refNode):
□setEndAfter(refNode):
③用DOM范圍實現復雜選擇:
□setStart():傳入一個參照點和一個偏移量。參照節點成startContainer,而偏移量成startOffset。
□setEnd():傳入一參照點和一個偏移量。參照節點成endContainer,而偏移量成endOffset。
<p id="p1"><b>Hello</b> world!</p> //使用范圍選擇l到o。
var p1 = document.getElementById("p1");
var helloNode = p1.firstChild.firstChild;
var worldNode = p1.lastChild;
var range = document.createRange();
range.setStart(helloNode,2);
range.setEnd(worldNode,3);
④操作DOM范圍中的內容
□在調用操作方法時,後台會為范圍創建有效的文檔片段及DOM結構。P275
□deleteContents():從文檔中刪除范圍所包含的內容。
□extractContents():從文檔中刪除范圍內容,返回范圍文檔片段。
⑤插入DOM范圍中的內容
□insertNode():向范圍選區的開始處插入一個節點。
□surroundContents():接受一個參數,即環繞范圍內容的節點。
a.提取出范圍中的內容(類似執行extractContent())
b.將給定節點插入到文檔中原來范圍所在的位置上。
c.將文檔片段的內容添加到給定節點中。
⑥折疊DOM范圍
□所謂折疊范圍,就是指范圍中未選擇文檔的任何部分。
□collapse()方法:一個參數布爾值。true折疊到范圍起點,false折疊到范圍終點。可以用collapsed屬性檢查是否已經折疊。
range.collapse(true); //折疊到起點
alert(range.collapsed); //輸出true
⑦比較DOM范圍
在有多個范圍的情況下,可以使用compareBoundaryPoints()方法來確定這些范圍是否有公共的邊界(起點或終點)。兩個參數:表示比較方式的常量和要比較的范圍。
比較方式常量值:
□Range.START_TO_START(0):比較第一個范圍和第二個范圍起點。
□Range.START_TO_END(1):
□Range.END_TO_END(2):
□Range.END_TO_STRAT(3):
⑧復制DOM范圍
□cloneRange()方法:var newRange = range.cloneRange();
⑨清理DOM范圍
□調用detach()方法,從文檔分離出范圍。
range.detach(); //從文檔中分離
range = null; //解除引用
2.IE中的范圍
①概況:
□IE不支持DOM范圍。支持類似的文本范圍(text range)。
□文本范圍處理的主要是文本(不一定是DOM節點)。通過<body>、<button>、<input>和<textarea>等元素調用createTextRange()方法。
□通過document創建的范圍可以在頁面中的任何地方使用。
var range = document.body.createTextRange();
②用IE范圍實現簡單選擇
□findText()方法:找到第一次出現的給定文本,並將范圍移過來以環繞該文本。返回布爾值,表示是否找到文本。
◇使用text屬性可返回范圍中文本。
var range = document.body.createTextRange();
var found = range.findText("Hello");
◇可為findText傳入另一個參數,負值即當前位置向後搜,正值則向前搜。
□moveToElementText()方法:類似DOM中selectNode()方法。接受一個DOM元素,並選擇該元素的所有文本,包括HTML標簽。
◇范圍可用htmlText屬性取得范圍全部內容,包括HTML。
③使用IE范圍實現復雜選擇
□4個方法:move()、moveStart()、moveEnd()、expand();兩個參數:移動單位和移動單位數量。移動單位為以下一種字符串:
◇"character":逐個字符地移動。
◇"word":逐個單詞(一系列非空格字符地移動)
◇"sentence":逐個句子(一系列句號、問好或感歎號結尾的字符地移動)
◇"textedit":移到當前范圍選取的開始或結束位置。
□moveStart():移動到范圍起點;moveEnd()移動到范圍終點。
□expand():將任何部分選擇的文本全部選中。
□move()方法:首先會折疊當前范圍(起點終點相等),然後將范圍移動指定的單位數量。
④操作IE范圍中的內容
□范圍僅含文本可通過text屬性讀寫文本。
□pasteHTML()方法:向范圍插入HTML代碼。
⑤折疊IE范圍
□collapse()方法:傳入布爾值,true折疊到起點,false折疊到終點。
□檢查折疊是否完畢:boundingWidth屬性等於0
var isCollapse = (range.boundingWidth == 0);
⑥比較IE范圍
□compareEndPoints()方法:兩個參數:比較類型和要比較的范圍。
◇比較類型取值字符串:“startToStart”“StartToEnd”、“EndToEnd”和“EndToStart”。
◇如果第一個范圍邊界位於第二個前,返回-1;相等返回0;在後面返回1.
□isEquanl()用於確定兩個范圍是否相等。
□inRange()用於確定一個范圍是否包含另一個范圍。
⑦復制IE范圍
使用duplicate()方法,復制文本范圍,返回副本。
var newRange = range.duplicate();