亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 開發 > XML > 正文

輕松使用 DOM 的技巧和訣竅

2024-09-05 20:54:32
字體:
來源:轉載
供稿:網友

Dethe Elza (delza@livingcode.org), 高級技術架構師, Blast Radius


文檔對象模型(Document Object Model,DOM)是用于操縱 XML 和 HTML 數據的最常用工具之一,然而它的潛力卻很少被充分挖掘出來。通過利用 DOM 的優勢,并使它更加易用,您將獲得一款應用于 XML 應用程序(包括動態 Web 應用程序)的強大工具。

本期文章介紹了一位客串的專欄作家,同時也是我的朋友和同事 Dethe Elza。Dethe 在利用 XML 進行 Web 應用程序開發方面經驗豐富,在此,我要感謝他對我在介紹使用 DOM 和 ECMAScript 進行 XML 編程這一方面的幫助。請密切關注本專欄,以了解 Dethe 的更多專欄文章。
—— David Mertz

DOM 是處理 XML 和 HTML 的標準 API 之一。由于它占用內存大、速度慢,并且冗長,所以經常受到人們的指責。盡管如此,對于很多應用程序來說,它仍然是最佳選擇,而且比 XML 的另一個主要 API —— SAX 無疑要簡單得多。DOM 正逐漸出現在一些工具中,比如 Web 瀏覽器、SVG 瀏覽器、OpenOffice,等等。

DOM 很好,因為它是一種標準,并且被廣泛地實現,同時也內置到其他標準中。作為標準,它對數據的處理與編程語言無關(這可能是優點,也可能是缺點,但至少使我們處理數據的方式變得一致)。DOM 現在不僅內置于 Web 瀏覽器,而且也成為許多基于 XML 的規范的一部分。既然它已經成為您的工具的一部分,并且或許您偶爾還會使用它,我想現在應該充分利用它給我們帶來的功能了。

在使用 DOM 一段時間后,您會看到形成了一些模式 —— 您想要反復做的事情。快捷方式可以幫助您處理冗長的 DOM,并創建自解釋的、優雅的代碼。這里收集了一些我經常使用的技巧和訣竅,還有一些 JavaScript 示例。

insertAfter 和 prependChild

第一個訣竅就是“沒有訣竅”。DOM 有兩種方法將孩子節點添加到容器節點(常常是一個 Element,也可能是一個 Document 或 Document Fragment):appendChild(node) 和 insertBefore(node, referenceNode)??雌饋硭坪跞鄙倭耸裁础<偃缥蚁朐谝粋€參考節點后面插入或者由前新增(prepend)一個子節點(使新節點位于列表中的第一位),我該怎么做呢?很多年以來,我的解決方法是編寫下列函數:

清單 1. 插入和由前新增的錯誤方法
function insertAfter(parent, node, referenceNode) {
if(referenceNode.nextSibling) {
parent.insertBefore(node, referenceNode.nextSibling);
} else {
parent.appendChild(node);
}
}
function prependChild(parent, node) {
if (parent.firstChild) {
parent.insertBefore(node, parent.firstChild);
} else {
parent.appendChild(node);
}
}

實際上,像清單 1 一樣,insertBefore() 函數已經被定義為,在參考節點為空時返回到 appendChild()。因此,您可以不使用上面的方法,而使用 清單 2 中的方法,或者跳過它們僅使用內置函數:

清單 2. 插入和由前新增的正確方法
function insertAfter(parent, node, referenceNode) {
parent.insertBefore(node, referenceNode.nextSibling);
}
function prependChild(parent, node) {
parent.insertBefore(node, parent.firstChild);
}

如果您剛剛接觸 DOM 編程,有必要指出的是,雖然您可以使多個指針指向一個節點,但該節點只能存在于 DOM 樹中的一個位置。因此,如果您想將它插入到樹中,沒必要先將它從樹中移除,因為它會自動被移除。當重新將節點排序時,這種機制很方便,僅需將節點插入到新位置即可。

根據這種機制,若想交換兩個相鄰節點(稱為 node1 和 node2)的位置,可以使用下列方案之一:

node1.parentNode.insertBefore(node2, node1);

node1.parentNode.insertBefore(node1.nextSibling, node1);

還可以使用 DOM 做什么?

Web 頁面中大量應用了 DOM。若訪問 bookmarklets 站點(參閱 參考資料),您會發現很多有創意的簡短腳本,它們可以重新編排頁面,提取鏈接,隱藏圖片或 Flash 廣告,等等。

但是,因為 Internet Explorer 沒有定義 Node 接口常量(可以用于識別節點類型),所以您必須確保在遺漏接口常量時,首先為 Web 在 DOM 腳本中定義接口常量。

清單 3. 確保節點被定義
if (!window['Node']) {
window.Node = new Object();
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;
}

清單 4 展示如何提取包含在節點中的所有文本節點:

清單 4. 內部文本
function innerText(node) {
// is this a text or CDATA node?
if (node.nodeType == 3 || node.nodeType == 4) {
return node.data;
}
var i;
var returnValue = [];
for (i = 0; i < node.childNodes.length; i ) {
returnValue.push(innerText(node.childNodes[i]));
}
return returnValue.join('');
}

快捷方式

人們常常抱怨 DOM 太過冗長,并且簡單的功能也需要編寫大量代碼。例如,如果您想創建一個包含文本并響應點擊按鈕的 <div> 元素,代碼可能類似于:

清單 5. 創建 <div> 的“漫長之路”
function handle_button() {
var parent = document.getElementById('myContainer');
var div = document.createElement('div');
div.className = 'myDivCSSClass';
div.id = 'myDivId';
div.style.position = 'absolute';
div.style.left = '300px';
div.style.top = '200px';
var text = "This is the first text of the rest of this code";
var textNode = document.createTextNode(text);
div.appendChild(textNode);
parent.appendChild(div);
}


若頻繁按照這種方式創建節點,鍵入所有這些代碼會使您很快疲憊不堪。必須有更好的解決方案 —— 確實有這樣的解決方案!下面這個實用工具可以幫助您創建元素、設置元素屬性和風格,并添加文本子節點。除了 name 參數,其他參數都是可選的。

清單 6. 函數 elem() 快捷方式
function elem(name, attrs, style, text) {
var e = document.createElement(name);
if (attrs) {
for (key in attrs) {
if (key == 'class') {
e.className = attrs[key];
} else if (key == 'id') {
e.id = attrs[key];
} else {
e.setAttribute(key, attrs[key]);
}
}
}
if (style) {
for (key in style) {
e.style[key] = style[key];
}
}
if (text) {
e.appendChild(document.createTextNode(text));
}
return e;
}

使用該快捷方式,您能夠以更加簡潔的方法創建 清單 5 中的 <div> 元素。注意,attrs 和 style 參數是使用 JavaScript 文本對象而給出的。

清單 7. 創建 <div> 的簡便方法
function handle_button() {
var parent = document.getElementById('myContainer');
parent.appendChild(elem('div',
{class: 'myDivCSSClass', id: 'myDivId'}
{position: 'absolute', left: '300px', top: '200px'},
'This is the first text of the rest of this code'));
}

在您想要快速創建大量復雜的 DHTML 對象時,這種實用工具可以節省您大量的時間。模式在這里就是指,如果您有一種需要頻繁創建的特定的 DOM 結構,則使用實用工具來創建它們。這不但減少了您編寫的代碼量,而且也減少了重復的剪切、粘貼代碼(錯誤的罪魁禍首),并且在閱讀代碼時思路更加清晰。

接下來是什么?
DOM 通常很難告訴您,按照文檔的順序,下一個節點是什么。下面有一些實用工具,可以幫助您在節點間前后移動:

清單 8. nextNode 和 prevNode
// return next node in document order
function nextNode(node) {
if (!node) return null;
if (node.firstChild){
return node.firstChild;
} else {
return nextWide(node);
}
}
// helper function for nextNode()
function nextWide(node) {
if (!node) return null;
if (node.nextSibling) {
return node.nextSibling;
} else {
return nextWide(node.parentNode);
}
}
// return previous node in document order
function prevNode(node) {
if (!node) return null;
if (node.previousSibling) {
return previousDeep(node.previousSibling);
}
return node.parentNode;
}
// helper function for prevNode()
function previousDeep(node) {
if (!node) return null;
while (node.childNodes.length) {
node = node.lastChild;
}
return node;
}


輕松使用 DOM
有時候,您可能想要遍歷 DOM,在每個節點調用函數或從每個節點返回一個值。實際上,由于這些想法非常具有普遍性,所以 DOM Level 2 已經包含了一個稱為 DOM Traversal and Range 的擴展(為迭代 DOM 所有節點定義了對象和 API),它用來為 DOM 中的所有節點應用函數和在 DOM 中選擇一個范圍。因為這些函數沒有在 Internet Explorer 中定義(至少目前是這樣),所以您可以使用 nextNode() 來做一些
類似的事情。

在這里,我們的想法是創建一些簡單、普通的工具,然后以不同的方式組裝它們來達到預期的效果。如果您很熟悉函數式編程,這看起來會很親切。Beyond JS 庫(參閱 參考資料)將此理念發揚光大。

清單 9. 函數式 DOM 實用工具
// return an Array of all nodes, starting at startNode and
// continuing through the rest of the DOM tree
function listNodes(startNode) {
var list = new Array();
var node = startNode;
while(node) {
list.push(node);
node = nextNode(node);
}
return list;
}
// The same as listNodes(), but works backwards from startNode.
// Note that this is not the same as running listNodes() and
// reversing the list.
function listNodesReversed(startNode) {
var list = new Array();
var node = startNode;
while(node) {
list.push(node);
node = prevNode(node);
}
return list;
}
// apply func to each node in nodeList, return new list of results
function map(list, func) {
var result_list = new Array();
for (var i = 0; i < list.length; i ) {
result_list.push(func(list[i]));
}
return result_list;
}
// apply test to each node, return a new list of nodes for which
// test(node) returns true
function filter(list, test) {
var result_list = new Array();
for (var i = 0; i < list.length; i ) {
if (test(list[i])) result_list.push(list[i]);
}
return result_list;
}

清單 9 包含了 4 個基本工具。listNodes() 和 listNodesReversed() 函數可以擴展到一個可選的長度,這與 Array 的 slice() 方法效果類似,我把這個作為留給您的練習。另一個需要注意的是,map() 和 filter() 函數是完全通用的,用于處理任何 列表(不只是節點列表)?,F在,我向您展示它們的幾種組合方式。

清單 10. 使用函數式實用工具
// A list of all the element names in document order
function isElement(node) {
return node.nodeType == Node.ELEMENT_NODE;
}
function nodeName(node) {
return node.nodeName;
}
var elementNames = map(filter(listNodes(document),isElement), nodeName);
// All the text from the document (ignores CDATA)
function isText(node) {
return node.nodeType == Node.TEXT_NODE;
}
function nodeValue(node) {
return node.nodeValue;
}
var allText = map(filter(listNodes(document), isText), nodeValue);

您可以使用這些實用工具來提取 ID、修改樣式、找到某種節點并移除,等等。一旦 DOM Traversal and Range API 被廣泛實現,您無需首先構建列表,就可以用它們修改 DOM 樹。它們不但功能強大,并且工作方式也與我在上面所強調的方式類似。

DOM 的危險地帶
注意,核心 DOM API 并不能使您將 XML 數據解析到 DOM,或者將 DOM 序列化為 XML。這些功能都定義在 DOM Level 3 的擴展部分“Load and Save”,但它們還沒有被完全實現,因此現在不要考慮這些。每個平臺(瀏覽器或其他專業 DOM 應用程序)有自己在 DOM 和 XML間轉換的方法,但跨平臺轉換不在本文討論范圍之內。

DOM 并不是十分安全的工具 —— 特別是使用 DOM API 創建不能作為 XML 序列化的樹時。絕對不要在同一個程序中混合使用 DOM1 非名稱空間 API 和 DOM2 名稱空間感知的 API(例如,createElement 和 createElementNS)。如果您使用名稱空間,請盡量在根元素位置聲明所有名稱空間,并且不要覆蓋名稱空間前綴,否則情況會非常混亂。一般來說,只要按照慣例,就不會觸發使您陷入麻煩的臨界情況。

如果您一直使用 Internet Explorer 的 innerText 和 innerHTML 進行解析,那么您可以試試使用 elem() 函數。通過構建類似的一些實用工具,您會得到更多便利,并且繼承了跨平臺代碼的優越性。將這兩種方法混合使用是非常糟糕的。

某些 Unicode 字符并沒有包含在 XML 中。DOM 的實現使您可以添加它們,但后果是無法序列化。這些字符包括大多數的控制字符和Unicode 代理對(surrogate pair)中的單個字符。只有您試圖在文檔中包含二進制數據時才會遇到這種情況,但這是另一種轉向(gotcha)情況。


結束語
我已經介紹了 DOM 能做的很多事情,但是 DOM(和 JavaScript)可以做的事情遠不止這些。仔細研究、揣摩這些例子,看看是如何使用它們來解決可能需要客戶端腳本、模板或專用 API 的問題。

DOM 有自己的局限性和缺點,但同時也擁有眾多優點:它內置于很多應用程序中;無論使用 Java 技術、Python 或 JavaScript,它都以相同方式工作;它非常便于使用 SAX;使用上述的模板,它使用起來既簡潔又強大。越來越多的應用程序開始支持 DOM,這包括基于 Mozilla的應用程序、OpenOffice 和 Blast Radius 的 XMetaL。越來越多的規范需要 DOM,并對它加以擴展(例如,SVG),因此 DOM 時時刻刻就在您的身邊。使用這種被廣泛部署的工具,絕對是您的明智之舉。

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
欧美成人免费全部观看天天性色| 亚洲国产又黄又爽女人高潮的| 欧美午夜精品久久久久久浪潮| 国产成人高潮免费观看精品| 精品久久久久久亚洲国产300| 少妇高潮久久77777| 第一福利永久视频精品| 精品国产31久久久久久| 日韩欧美中文在线| 欧美激情视频一区二区三区不卡| 欧美视频13p| 日韩成人在线电影网| 在线观看中文字幕亚洲| 久久久久久久久久久久av| 国产日韩精品在线观看| 欧美大学生性色视频| 久久精品美女视频网站| 久久久噜噜噜久久| 国产主播精品在线| 久久精品影视伊人网| 精品动漫一区二区三区| 欧美激情在线一区| 精品中文字幕视频| 97久久伊人激情网| 亚洲丝袜在线视频| 亚洲大胆人体在线| 日本久久久久久久| 亚洲免费av片| 国产剧情日韩欧美| 国产精品美女免费| 国产成人激情小视频| 欧美多人乱p欧美4p久久| 亚洲中国色老太| 国产日本欧美视频| 久久久国产精彩视频美女艺术照福利| 欧美三级免费观看| 日韩视频一区在线| 国产欧洲精品视频| 日韩av最新在线| 亚洲欧美在线免费观看| 91亚洲精品久久久久久久久久久久| 91精品国产自产在线观看永久| 欧美成人中文字幕在线| 深夜精品寂寞黄网站在线观看| 亚洲精品一区久久久久久| 欧美日韩美女视频| 欧美国产高跟鞋裸体秀xxxhd| 国产精品久久久久久久久久久新郎| 一本色道久久综合狠狠躁篇的优点| 亚洲精品欧美一区二区三区| 国产精品电影一区| 亚洲已满18点击进入在线看片| 国产成人精品免费视频| 日韩一区二区精品视频| 欧美精品videos性欧美| 欧美日韩一区二区精品| 亚洲一区免费网站| 日本高清视频一区| 国产精品美女无圣光视频| 欧美在线观看日本一区| 国产日韩欧美视频| 全亚洲最色的网站在线观看| 欧美尤物巨大精品爽| 91久久久久久久久久久久久| 久久人人爽人人爽人人片亚洲| 国产精欧美一区二区三区| 中文一区二区视频| 奇米影视亚洲狠狠色| 久久99精品国产99久久6尤物| 狠狠色噜噜狠狠狠狠97| 人九九综合九九宗合| 欧美成人免费大片| 亚洲国产精彩中文乱码av在线播放| 亚洲国产精品久久91精品| 国产高清在线不卡| 亚洲天堂视频在线观看| 91亚洲精品视频| 国产原创欧美精品| 成人午夜在线视频一区| 91在线高清免费观看| 亚洲精品永久免费精品| 欧美黄网免费在线观看| 日韩亚洲综合在线| 久热精品视频在线观看| 欧美一级黑人aaaaaaa做受| 亚洲毛片在线免费观看| 亚洲va男人天堂| 另类天堂视频在线观看| 中文字幕欧美日韩在线| 久久久久久美女| 中文字幕一区二区三区电影| 国产精品入口日韩视频大尺度| 精品久久久久久电影| 视频在线一区二区| 日韩欧美成人免费视频| 日韩在线中文字| 欧美成人sm免费视频| 国产精品偷伦免费视频观看的| 欧美极品欧美精品欧美视频| 亚洲第一区中文字幕| 欧美日韩国产中文精品字幕自在自线| 久久久电影免费观看完整版| 日韩中文视频免费在线观看| 国产精品一区二区久久精品| 一本色道久久88综合亚洲精品ⅰ| 久久人人爽人人爽爽久久| 韩国美女主播一区| 91精品国产乱码久久久久久蜜臀| 久久精品国产亚洲精品| 亚洲天堂免费视频| 国产精品久久久久久久久久三级| 久久青草精品视频免费观看| 青青青国产精品一区二区| 欧美午夜性色大片在线观看| 在线亚洲午夜片av大片| 国产亚洲免费的视频看| 日韩在线小视频| 国产精品福利观看| 久久久久久中文字幕| 91亚洲精品久久久久久久久久久久| 国产区亚洲区欧美区| 中文字幕视频在线免费欧美日韩综合在线看| 久久久久久久久亚洲| 久久精品视频在线| 欧美丝袜一区二区三区| 欧美第一淫aaasss性| 最近2019免费中文字幕视频三| 91欧美精品午夜性色福利在线| 亚洲美女av电影| 国产欧美日韩免费看aⅴ视频| 狠狠躁夜夜躁人人爽超碰91| 久久久国产视频91| 亚洲国产中文字幕在线观看| 国产精品网站大全| 国产精品自产拍在线观看中文| 亚洲社区在线观看| 日韩成人av网址| 国产成人拍精品视频午夜网站| 欧美精品www在线观看| 国产精品一二三视频| 成人久久精品视频| 精品久久久久久久久久久久久久| 91精品久久久久久久久中文字幕| 性欧美激情精品| 欧美日韩一区二区免费在线观看| 51精品国产黑色丝袜高跟鞋| 亚洲欧美日本另类| 78色国产精品| 懂色av影视一区二区三区| 亚洲色图校园春色| 久久久久久久久久久久久久久久久久av| 日本一欧美一欧美一亚洲视频| 国产在线精品成人一区二区三区| 国产精品美女免费看| 国产精品精品久久久久久| 成人在线视频网站| 中文字幕日韩电影| 色综合男人天堂| 亚洲精品成人免费| 成人妇女淫片aaaa视频| 深夜精品寂寞黄网站在线观看| 国产精品久久久久久av福利软件| 欧美一二三视频| 韩国欧美亚洲国产|