麻豆小视频在线观看_中文黄色一级片_久久久成人精品_成片免费观看视频大全_午夜精品久久久久久久99热浪潮_成人一区二区三区四区

首頁 > 開發 > XML > 正文

輕松使用DOM 的技巧和訣竅

2024-09-05 20:53:56
字體:
來源:轉載
供稿:網友
,歡迎訪問網頁設計愛好者web開發。

  dethe elza ([email protected]), 高級技術架構師, blast radius
 
  文檔對象模型(document object model,dom)是用于操縱 xml 和 html 數據的最常用工具之一,然而它的潛力卻很少被充分挖掘出來。通過利用 dom 的優勢,并使它更加易用,您將獲得一款應用于 xml 應用程序(包括動態 web 應用程序)的強大工具。

  本期文章介紹了一位客串的專欄作家,同時也是我的朋友和同事 dethe elza。dethe 在利用 xml 進行 web 應用程序開發方面經驗豐富,在此,我要感謝他對我在介紹使用 dom 和 ecmascript 進行 xml 編程這一方面的幫助。

 —— 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() 函數是完全通用的,用于處理任何 列表(不只是節點列表)。現在,我向您展示它們的幾種組合方式。

  清單 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 時時刻刻就在您的身邊。使用這種被廣泛部署的工具,絕對是您的明智之舉。

  請作者聯系本站,及時附注您的姓名。聯系郵箱:edu#chinaz.com(把#改為@)。

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 亚洲免费看片网站 | 国产亚洲精品久久久久久久久 | 成人在线观看污 | 欧美一级黄色免费 | www.99久 | 亚洲国产精品一区二区久久 | 日本精品视频一区二区三区四区 | 国产在线欧美日韩 | 久久亚洲国产午夜精品理论片 | 亚洲精品av在线 | 国产精选电影免费在线观看 | 久久久久久久高清 | 日本一级黄色毛片 | 91在线视频福利 | 国人精品视频在线观看 | 亚洲欧美成aⅴ人在线观看 av免费在线播放 | 久草在线免费资源站 | 欧美成年性h版影视中文字幕 | 久久艳片 | 国产一区二区三区视频观看 | 欧美日韩亚洲成人 | 色综合欧美 | 欧美成人精品欧美一级乱黄 | 牛牛视频在线 | 福利在线国产 | 国产精选电影免费在线观看 | 13一14毛片免费看 | 美国一级黄色毛片 | 成人短视频在线播放 | 高颜值美女啪啪 | 欧美色另类 | 91午夜在线观看 | 91精品国产成人 | 天天躁狠狠躁夜躁2020挡不住 | 亚洲国产精品久久久 | 国色天香综合网 | 亚洲成人国产综合 | 最新亚洲视频 | 日本娇小18xxxⅹhd | 久久男人视频 | 性片网站|