閱讀341 返回首頁    go 阿裏雲 go 技術社區[雲棲]


Pro Javascript Techniques第五章: 文檔對象模型

 在過去十年裏web開發所取得的所有進步當中,DOM(文檔對象模型)腳本是開發者可用來改進其用戶體驗質量的最重要的技術。
  使用DOM腳本向頁麵加入非侵入的JavaScript(意味著它不會與不支持的瀏覽器或禁用了JavaScript的用戶發生衝突),你將能提供各種你的用戶可享受的現代瀏覽器的增強功能,同時又不會損害那些不能利用它們的用戶。這麼做的一個副作用是,你的所有代碼最終都可以被很好的分離和更容易地管理。
  可喜的是,所有的現代瀏覽器都支持DOM並額外地支持一個當前HTML文檔的內建的DOM表述。所有這些都很容易通過JavaScript訪問,這為現代web開發者帶來巨大的利益。理解怎樣使用這一技術和怎樣最好地發揮它的功效,能夠給這你開發下一個web應用程序的提供一個良好的開端。
  本章中我將討論與DOM相關的一些話題。考慮到你可能對DOM沒有經驗,我將從基礎出發,涵蓋所有的重要概念。對於已經熟悉了DOM的讀者,我保證將會給出一些你肯定會喜歡並開始在自己的頁麵中使用的很酷的技術。

頂部
one by one
[廣告] 【萬網郵箱DIY,靈活購買】| 【西部數碼】480元輕鬆自助建站
mozart0

匪徒田老大



帖子 2326
體力 6628
威望 177
注冊 2003-6-18

發表於 2007-4-13 21:02  資料  短消息  加為好友  QQ
文檔對象模型簡介
  
  DOM是由W3C製定的表示XML文檔的標準方式。它未必是最快的、最輕便的、或者最易使用的,卻是是最普及的,絕大多數web開發語言(如Java,Perl,PHP,Ruby,Python,及Javascript)都實現了對它的支持。DOM旨在為開發者提供一種直觀的方式來導航於XML的層次結構中。即使你並不完全熟悉XML,你也會非常高興地看到所有的HTML文檔(在瀏覽器的眼中也就是XML文檔)都有一個可供使用的DOM表述。

[ 本帖最後由 mozart0 於 2007-4-13 23:54 編輯 ]

頂部
one by one
[廣告] | 優質域名主機首選時代互聯
mozart0

匪徒田老大



帖子 2326
體力 6628
威望 177
注冊 2003-6-18

發表於 2007-4-13 21:02  資料  短消息  加為好友  QQ
導航DOM

  DOM中描述XML結構的方式是作為一種可導航的樹。使用的所有術語與一個家族樹(parents,children,sibling,等等)是近似的。與典型的家族樹不同的是,XML文檔以單個包含指向其子節點的指針的根節點(稱為文檔元素(document element))開始。每一個子結節又包括指回其父結節、兄弟結點和子節點的指針。
  DOM使用特定的術語來代表XML樹中的各種對象。DOM樹中的每一個對象都是一個節點(node)[/i]。每個節點可以擁有不同的[i]類型(type),如元素(element),文本(text),或文檔(document)。為了繼續,我們需要等來了解DOM文檔是什麼樣子的以及怎樣在其中導航(一旦它已經構建完成)。通過一段簡單的HTML片段,我們來考察這一DOM構建工作是怎樣進行的。

<p><strong>Hello</strong> how are you doing?</p>

  這個片斷的每一部分被分解成一個帶有指向基直接親屬(父、子、兄弟)的指針的DOM節點。如果完全描繪出存在的關係,它將會是類似於圖5-1。片段的每一部分(圓角盒子代表元素,方盒子代表文本節節點)與它所有的引用一起顯示。
[attach]33187[/attach]
  圖5-1. 節點間的關係

  每個DOM節點都包含一個指針的集合,它使用這些指針引用其親屬。你將使用這些指針來學習怎樣導航DOM。圖5-2顯示了所有可用的指針。這些屬性對每一個DOM節點都可用,是指向其它DOM元素的指針(或者是null,如果不存在對應元素的話)。
[attach]33188[/attach]
  圖5-2. 使用指針導航DOM

  僅使用指針而導航到頁麵的任何元素元素和文本塊是可能的。理解在現實環境裏這一點怎樣工作的最好的方式是來看一個普通的HTML頁麵,如程序5-1所示:

  程序5-1. 一個簡單的HTML網頁,兼一個簡單的XML文檔

<html>
<head>
    <title>Introduction to the DOM</title>
</head>
<body>
    <h1>Introduction to the DOM</h1>
    <p >There are a number of reasons why the
        DOM is awesome, here are some:</p>
    <ul>
        <li >It can be found everywhere.</li>
        <li >It's easy to use.</li>
        <li >It can help you to find what you want, really quickly.</li>
    </ul>
</body>
</html>

  在這個示例文檔中,根元素是<html>元素。在JavaScript訪問這一個根元素是很輕鬆的:

  document.documentElement

  如同它的DOM節點一樣,根結點擁有用來導航的所有指針。使用這些指針你就能開始瀏覽整個文檔,導航到你想要的任何元素。例如,要得到<h1>元素,你可能使用以下語句:

  //Don't work!
  document.documentElement.firstChild.nextSibling.firstChild

  我們恰好撞上了我們的第一個暗礁:DOM指針既能指向文本節點也能指向元素。於是,上麵的語句實際並不是指向<h1>元素;它反倒指向<title>元素。為什麼會出這種事呢?這要歸咎於XML的最棘手和最受爭議的一個方麵:空白(white space)。你可能會注意到的,在<html>和<head>元素之間,實際上有一個換行符,它被認為是空白,這意味著那裏實際上首先有一個文本節點,而不是<head>元素。我們從中可以學到三件事:
  1. 當嚐試隻使用指針導航DOM的時候,編寫漂亮、整潔的HTML標記可能反會使事情變得非常令人困惑。
  2. 僅僅使用DOM指針導航文檔可能是非常的冗長和不實際。
  3. 通常,你並不需要直接訪問文本節點,而是訪問包繞它們的元素。
  這把我們導向一個問題:有沒有一種更好的方式用來在文檔找到元素呢?有!通過使用工具箱裏的幾個有用的函數,你可以輕易改善現有的方法,把DOM導航變得簡單得多。

  處理DOM中的空白

  讓我們先回到那個示例HTML文檔。先前,你試圖定位那個單獨的<h1>元素卻因無關的文本節點而遇上了困難。這對於單個的元素可能還是好的,但是倘若你想要找到<h1>後麵的那元素呢?你仍然會遭遇那個臭名昭著的空白bug,不得不使用.nextSibling.nextSibling來跳過<h1>和<p>之間的換行符。有一種技巧可以作為這一空白bug的補救辦法,如程序5-2所示。這一特別的技巧去除了DOM文檔所有的空白文本節點,便它變得更加易於穿行。這麼做對你的HTML怎麼渲染並沒有明顯的影響,卻能使用你手工導航變得容易。應該注意的是,這個函數的結果並不是永久性的,每次HTML文檔加載以後都需要重新運行。

  程序5-2. XML文檔中空白bug的補救辦法

function cleanWhitespace( element ) {
    //如果沒有提供element,則處理整個HTML文檔
    element = element || document;
    //使用firstChild作為開始指針
    var cur = element.firstChild;
    
    //遍曆所有子節點
    while ( cur != null ) {
    
        //如果該節點是文本節點,且隻含有空白
        if ( cur.nodeType == 3 && ! //S/.test(cur.nodeValue) ) {
            //刪除些文本節點
            element.removeChild( cur );
        //否則,如果它是一個元素
        } else if ( cur.nodeType == 1 ) {
            //遞歸處理下一級節點
            cleanWhitespace( cur );
        }
        cur = cur.nextSibling; //移動到下一個子節點
    }
}

  比方說你想要在上麵的示例文檔這個函數以找到<h1>後麵的那個元素。完成這一工作的代碼會是類似這樣的:

cleanWhitespace();
//獲得文檔元素
document.documentElement
    .firstChild //找到<head>元素
    .nextSibling //找到<body>元素
    .firstChild //找到<h1>元素
    .nextSibling //取得相鄰的段落

  這是一種既有好處又有缺點的技巧。最大的好處是,當你試圖導航DOM文檔的時候你可以保持某種程度的邏輯清晰。但是,考慮到你必須遍曆所有的DOM元素和文本節點來尋找隻包含空白的節點,這一技巧非常之慢。如果你的文檔包含大量的內容,它會明顯地拖慢你站點的加載。而且,每次你往文檔中注入新的HTML,你都需要重新掃描DOM的那一部分,確保沒有附加的空白文本節點被加入。
  上述函數裏一個重要的方麵就是節點類型的使用。一個節點的類型可以通過檢查其nodeType屬性為特定值來判定。有許多種可能的值,但最經常碰到的是以下三種
  元素(nodeType=1): 匹配XML文檔中的所有元素。例如,<li>,<a>,<p>,和<body>元素的nodeType全都是1。
  文本(nodeType=3): 匹配文檔中所有的文本段。當在一個DOM結構中使用previousSibling的nextSibling導航時,你經常會在元素之間或元素內部遇到文本片段。
  文檔(nodeType=9): 匹配一個文檔的根元素。比如,在一個HTML文檔裏,它就是<html>元素。
  另外,(在非IE瀏覽器中)你可以使用常數來代表不同的節點類型。比如,代替記住1,3或9,你可以簡單地使用document.ELEMENT_NODE,document.TEXT_NODE,或document.DOCUMENT_NODE。既然反複地清除DOM的空白文本節點大有累贅之嫌,我們自然應該尋求其它的方法來導航DOM。

  簡單的DOM導航

  使用純DOM導航的原理(擁有每個方向的導航指針)你可以開發出可能更適合你的導航HTML DOM文檔的函數。這一特殊的原則的依據是:多數web開發者隻針對DOM元素而很少對其間的文本節點導航。下麵提供幾個函數,可以用來代替標準的previousSibling,nextSIbling,firstChild,lastChild以及parentNode。程序5-3展示了一個返回元素的前一個元素的函數。類似於元素的previousSibling屬性,如果沒有前一個元素,該函數返回null。

  程序5-3. 用來查找元素的前一個兄弟元素的函數

function prev( elem ) {
    do {
        elem = elem.previousSibling;
    } while ( elem && elem.nodeType != 1 );
    return elem;
}

  程序5-4展示了一個返回元素的下一個兄弟元素的函數。與元素的nextSibling屬性類似,當沒有下一個元素時,函數返回null。

  程序5-4. 用來查找元素的後一個兄弟元素的函數

function next( elem ) {
    do {
        elem = elem.nextSibling;
    } while ( elem && elem.nodeType != 1 );
    return elem;
}

  程序5-5展示了一個返回元素的第一個子元素的函數,與元素的firstChild屬性類似。

  程序5-5.

function first( elem ) {
    elem = elem.firstChild;
    return elem && elem.nodeType != 1 ?
        next ( elem ) : elem;
}

  程序5-5展示了一個返回元素的最後一個子元素的函數,與元素的lastChild屬性類似。

  程序5-6.

Listing 5-6. A Function for Finding the Last Child Element of an Element
function last( elem ) {
    elem = elem.lastChild;
    return elem && elem.nodeType != 1 ?
        prev ( elem ) : elem;
}

  程序5-7展示了一個返回元素父元素的函數,與元素的parentNode屬性類似。你可以提供一個可選的參數number,以一次向上移動幾層——比如說,parent(elem,2)與parent(parent(elem))等價。

  程序5-7. 用來查找元素父元素的函數

function parent( elem, num ) {
    num = num || 1;
    for ( var i = 0; i < num; i++ )
        if ( elem != null ) elem = elem.parentNode;
    return elem;
}

  使用這些新的函數,你可以快速地瀏覽一個DOM文檔,而無需擔心元素之間的文本。例如,為了找到<h1>元素的下一個元素,像從前一樣,你現在可以像下麵這麼做:

//查找<h1>的下一個元素
next( first( document.body ) )

  注意到這行代碼的兩個特點。第一,有一個新的引用:document.body。所有現代瀏覽器都在HTML DOM文檔的body參數裏提供一個對<body>元素的引用。你可以利用這一點使你的代碼更加簡短和更加可理解。第二,函數的書寫方式是非常地違背直覺的。通常,當你想到導航時你可能會說:從<body>元素開始,得到第一個元素,再得到第二個元素。但是在它實際的書寫方式裏,好像是倒著來的。為了替代這一方式,我將會討論一些使得你定製的導航代碼更加清晰的辦法。

  綁定到每一個HTML元素

  在Firefox和Opera裏,有一種可用的非常強大的對象原型,稱為HTMLElement,它允許你將函數和數據附加到每個單獨的HTML DOM元素上。前麵一節所介紹的函數是很呆板的,可以進行某種清理。一種完美的方式是把你的函數直接綁定到HTMLElement原型上,以此來把它們直接綁定到每一個單獨的HTML元素。為了進行這一工作,對前麵所建立的函數需要作三個更改:
  1. 在函數裏最上麵添加一行將使elem指向this,而不再是從參數列表取得。
  2. 刪除那個你不再需要的元素參數。
  3. 將函數綁定到HTMLElement原型,這樣你才能在第一個DOM中的HTML元素上使用它。
  舉例來說,新的next函數將會是如程序5-8所示的樣子。

  程序5-8. 向所有HTML DOM元素動態地綁定一個新的導航函數

HTMLElement.prototype.next = function() {
    var elem = this;
    do {
        elem = elem.nextSibling;
    } while ( elem && elem.nodeType != 1 );
    return elem;
};

  現在你可以像這樣使用next函數(或者是經過造的前述的第一個函數):

//一個簡單的例子:得到第一個的<p>元素
document.body.first().next()

  這使得你的代碼更加清晰而易於理解,因為你能以自然思考的順序書寫代碼。如果你對這種書寫風格有興趣,我極力推薦你去看看JQuery庫,它極好地利用了這一技術。
  注意:因為HTMLElement隻存在於三種現代瀏覽器中(Firefox,Safari,和Opera),你需要采取特殊的預防措施使它能夠在IE中工作。Jason Karl(https://browserland.org)編寫了一個特別便利的庫,在兩種不支持的瀏覽器中提供了對HTMLElement(及其它相關功能)的訪問。關於此庫的更多信息可以在這裏找到:https://www.browserland.org/scripts/htmlelement/

  標準的DOM方法

  所有的現代DOM實現都包含幾種使工作更加有條理的方法。將它們和一些自定義函數結合使用,DOM導航將會變成一種流暢得多的體驗。首先,我們來看JavaScript DOM中包含的兩個功能強大的函數:
  getElementById("everywhere"):此方法隻能應用於document對象,它在所有元素中查找ID等於everywhere的元素。這一強大的函數是立即訪問一個元素的最快的方式。
  getElementsByTagName("li"):此方法可以在任意元素上使用,它在所有後代元素中查找標簽名為li的,並將它們作為一個(幾乎與數組相同的)節點列表返回。
  警告:對HTML文檔來說,getElementById會如你想象的那樣工作:它檢查所有的元素直到找到id屬性與給定的值相同的那一個。然而,如果你載入一個遠程的XML文檔並使用getElementById(或使用JavaScript以外的另一種語言裏的DOM實現),它默認並不根據id屬性查找。它是由設計決定的;一個XML文檔必須明確地(一般用XML定義或XML模式)指定id屬性是什麼。
  警告:getElementsByName返回一個節點列表。該結構外觀的行為都跟通常的JavaScript數組十分相似,但是有一個重要的例外:它不具有通常的.push(),.pop(),.shift()等等這些JavaScript數組所具有的方法。使用getElementsByName時牢記這一點,會省去你許多的疑惑。
  這兩個方法在所有的現代瀏覽器中都是可用的,且對於定位特定元素極有幫助。回到前麵我們試圖找到<h1>元素的例子,現在我們可以像下麵這麼做:

document.getElementsByTagName("h1")[0]

  這段代碼會有保障地工作並總是返回文檔中的第一個<h1>元素。回到前麵的示例文檔,假設你想到得到所有的<li>元素並給它們加上邊框:

var li = document.getElementsByTagName("li");
for ( var j = 0; j < li.length; j++ ) {
    li[j].style.border = "1px solid #000";
}

  再一次地,我們回頭看查找第一個<h1>元素後麵的元素的問題,完成這一工作的代碼到期可以減短得更多:

//找到第一個<h1>元素的後一個元素
next(tag("h1")[0]);

  這些函數提供了快速得到你想操作的DOM元素的能力。在學習使用這一能力來修改DOM之前,你需要先快速地看看你的腳本第一次執行以後DOM加載的問題。

[ 本帖最後由 mozart0 於 2007-4-15 09:51 編輯 ]



 附件: 您所在的用戶組無法下載或查看附件,您需要注冊/登陸後才能查看!
頂部
one by one
[廣告] 網站博客賣廣告推薦:阿裏媽媽
mozart0

匪徒田老大



帖子 2326
體力 6628
威望 177
注冊 2003-6-18

發表於 2007-4-13 21:03  資料  短消息  加為好友  QQ
等待HTML DOM加載

  操作HTML DOM文檔的一個難題是,你的JavaScript代碼可能在DOM完全載入之前運行,這會導致你的代碼產生一些問題。頁麵加載時瀏覽器內部操作的順序大致是這樣的:
  1. HTML被解析。
  2. 外部腳本/樣式表被加載。
  3. 文檔解析過程中內聯的腳本被執行。
  4. HTML DOM構造完成。
  5. 圖像和外部內容被加載。
  6. 頁麵加載完成。
  頭部包含的和從外部文件中載入的腳本實際上在HTML DOM構造好之前就執行了。正如前麵提到的,這一個問題是很重要的,因為在那兩種地方的執行的所有腳本將不能訪問DOM。可喜的是,存在許多繞開這一問題的辦法。
  
  等待頁麵加載

  到目前為止,最常用的技術是在任何DOM操作之前簡單地等待整個頁麵加載。使用這一技術,可以通過簡單地給window對象的load事件附加一個在頁麵載入後觸發的函數。在第六章中我將討論關於事件的更多細節。程序5-10展示了一個在頁麵加載完成後執行DOM相關代碼的例子。

  程序5-10. 為window.onload屬性附加回調函數的addEvent函數

//等待頁麵加載完成
//(使用了下一章描述的addEvent函數)
addEvent(window, "load", function() {
    //執行HTML DOM操作
    next( id("everywhere") ).style.background = 'blue';
});

  盡管這一操作可能是最簡單的,它也將總是最慢的。從加載操作的順序中,你可能已發現頁麵加載完成絕對是最後一步。這意味著如果在你的頁麵上有大量的圖像、視頻等等,你的用戶在JavaScript最終執行前得等待很大一陣子。

  等待大部分DOM加載

  第二種技術很迂回,不太推薦使用。如果你還記得,我在上一節裏說了,內聯的腳本是在DOM構造以後執行的。這是一個半真半假的說法。那些腳本實際上是在DOM構造時遇上了就執行的。這就是說如果你有一段內聯的腳本嵌在頁麵的中間部分,則該腳本隻能立即擁有前半部分DOM的訪問權。然而,把腳本作為非常靠後的元素嵌入頁麵中,就意味著你能夠有效地對先於它出現的所有的DOM元素進行訪問,獲得一種假冒的模擬DOM加載的方式。這種方法的典型實現通常如程序5-11所示。

  程序5-11. 通過向HTML DOM的結尾置入(包含函數調用的)<script>標簽來判定DOM是否已經加載

<html>
<head>
    <title>Testing DOM Loading</title>
    <script type="text/javascript">
        function init() {
            alert( "The DOM is loaded!" );
            tag("h1")[0].style.border = "4px solid black";
        }
    </script>
</head>
<body>
    <h1>Testing DOM Loading</h1>
    <!--這裏是大量的HTML -->
    <script type="text/javascript">init();</script>
</body>
</html>

  在這個例子裏,一個內聯腳本作為DOM的最後一個元素;它將是最後一個被解析和執行的。它所做的唯一的事情是調用init函數(函數內部應包含你想要處理的任何DOM相關的代碼)。這一解決方案的存在的最大的問題在於,它是混亂的:給你的HTML裏加入了額外的標記,隻為了判定DOM是否已經加載。

  斷定DOM何時加載完成

  最後一種可用來監視DOM加載的技術,可能是最複雜(從實現的角度來看)但也是最有效的。它結合了綁定到window的load事件的簡易性和內聯腳本技術的速度。
  這一技術的原理是在不阻塞瀏覽器的前提下盡可能快地反複檢查HTML DOM是否已經具有了你所需的特性。有幾種東西可以被檢查以判斷HTML文檔是否已經可以操作了:
  1. document: 你需要檢查DOM document是否已經存在。如果你檢查得夠快的話,它一開始可能僅僅是undefined。
  2. document.getElementsByTagName和document.getElementByID: 檢查document是否已經具備了經常使用的getElementsByTagName和getElementById函數;這些函數將在它們準備好被使用以後存在。
  3. document.body: 作為額外的保障,檢查<body>元素是否已完成被載入。理論上講,前麵的檢查應該已經足夠了,但是我發現過它們還不夠好的例子。
  使用這些檢查,你將對DOM何時準備好被使用有一個足夠好的把握(好到可能隻錯過了幾毫秒)。這一方法近乎沒有瑕疵。僅使用前麵的檢查,腳本可以在所有的現代瀏覽器裏運行得相對很好了。然而,Firefox某些新的緩存機製的實現,導致了window的load事件實際上能夠在你的腳本判斷DOM是否就緒之前就觸發。為了利用這一優勢,我也加入了對window的load事件的檢查,希望獲得一些額外的速度。
  最終,domReady函數在DOM就緒之前一直在收集所有的待運行函數的引用。一旦DOM確實準備好了,就遍曆這些引用並一個一個地執行它們。程序5-12展示了一個可用來監視DOM何時完全載入的函數。

程序5-12. 監視DOM直到它準備好的一個函數

function domReady( f ) {
    //如果DOM已經載入,立即執行函數
    if ( domReady.done ) return f();
    //如果我們已經添加過函數
    if ( domReady.timer ) {
        //則將函數添加到待執行的函數列表
        domReady.ready.push( f );
    } else {
        //為頁麵完成加載時附加一個事件,以防它率先發生
        //使用了addEvent函數
        addEvent( window, "load", isDOMReady );
        //初始化待執行函數的數組
        domReady.ready = [ f ];
        //盡可能快地檢查DOM是否已就緒
        domReady.timer = setInterval( isDOMReady, 13 );
    }
}
//檢查DOM是否已經準備好導航
function isDOMReady() {
    //如果我們斷定頁麵已經加載完成了,則返回
    if ( domReady.done ) return false;
    //檢查一些函數和元素是否已可訪問
    if ( document && document.getElementsByTagName &&
        document.getElementById && document.body ) {
        //如果它們已就緒,則停止檢查
        clearInterval( domReady.timer );
        domReady.timer = null;
        
        //執行所有正在等待的函數
        for ( var i = 0; i < domReady.ready.length; i++ )
            domReady.ready[i]();
        //記住現在我們已經完成
        domReady.ready = null;
        domReady.done = true;
    }
}

現在我們應該看看這在一個HTML文檔裏會是什麼樣。使用domReady函數就像使用addEvent函數(見第6章)一樣,綁定你的特定函數到文檔準備好導航和操作的時候被觸發。在下麵的例子裏我把domReady函數放入了一個名為domready.js的外部JavaScript文件裏。程序5-3展示了怎樣使用新的domReady函數來監視DOM何時已載入。

  程序5-13. 使用domReady函數在判定DOM何時準備好導航和修改

<html>
<head>
    <title>Testing DOM Loading</title>
    <script type="text/javascript" src="domready.js"></script>
    <script type="text/javascript">
        function tag(name, elem) {
            //如果上下文元素未提供,則搜索整個文檔
            return (elem || document).getElementsByTagName(name);
        }
        domReady(function() {
            alert( "The DOM is loaded!" );
            tag("h1")[0].style.border = "4px solid black";
        });
    </script>
</head>
<body>
    <h1>Testing DOM Loading</h1>
    <!--這裏是大量的HTML -->
    </body>
</html>

  既然你了解了用來導航一般的XML DOM文檔的和克服HTML DOM文檔加載難題的幾種方法,這個問題應該被擺在眼前了:有沒有更好的在HTML文檔中查找元素的方法呢?可喜的是,答案是響亮的"有"。

[ 本帖最後由 mozart0 於 2007-4-13 23:53 編輯 ]

頂部
one by one
mozart0

匪徒田老大



帖子 2326
體力 6628
威望 177
注冊 2003-6-18

發表於 2007-4-13 21:03  資料  短消息  加為好友  QQ
在HTML文檔中查找元素

  在一個HTML文檔中查找元素的方式常常與在一個XML文檔中有很大的不同。考慮到現代HTML實際上是XML的一個子集,這看起來可能有些矛盾;但是HTML文檔包含一些你可以利用的基礎的不同點。
  對JavaScript/HTML開發者來說,最重要的兩個優勢是CSS類的使用的CSS選擇符的知識。記住這些,你就可以創建一些強大的函數用來使得DOM導航更加簡單和可理解。

  通過類名查找元素

  用類名定位元素是一種廣泛流傳的技術,由Simon Willison(https://simon.incutio.com)於2003年推廣,最初由Andrew Hayward(https://www.mooncalf.me.uk)編寫。這一技術是非常易行的:遍曆所有元素(或所有元素的一個子集),選出其中具有特定類名的。程序5-14展示了一種可能的實現。

  程序5-14. 從所有元素中找出具有特定類名的元素的一個函數

function hasClass(name,type) {
    var r = [];
    //限定類名(允許多個類名)
    var re = new RegExp("(^|//s)" + name + "(//s|$)");
    //用類型限製搜索範圍,或搜索所有的元素
    var e = document.getElementsByTagName(type || "*");
    for ( var j = 0; j < e.length; j++ )
        //如果元素類名匹配,則加入到返回值列表中
        if ( re.test(e[j]) ) r.push( e[j] );
    
    //返回匹配的元素
    return r;
}

  現在你可以通過一個指定的類名使用些函數來快速地查找任何元素,或特定類別的任何元素(比如,<li>或<p>)。指定要查找的標簽名總會比查找全部(*)要快,因為查找元素的範圍被縮小了。比如,在我們的HTML文檔裏,如果想要查找所有類名包含"test"的元素,你可以這麼做:

hasClass("test")

  如果你隻想查找類名包含"test"的所有<li>元素,則這樣:

hasClass("test","li")

  最後,如果你想找到第一個類名包含"test"的<li>元素,則這麼做:

hasClass("test","li")[0]

  這個函數單獨使用已經很強大了,而當與getElementById和getElementByTagname聯合使用時,你就擁有了非常強大的可完成最複雜的DOM工作的一套工具。
   
  通過CSS選擇符查找元素

  作為一個web開發者,你已經知道一種選擇HTML元素的方式:CSS選擇符。CSS選擇符是用來將CSS樣式應用於一組元素的表達式。隨著CSS標準的每一次修訂(1,2和3),更多的功能被加入了選擇符規範中,允許開發者更容易地精確確定他們想要的元素。不幸的是,瀏覽器一直是極其緩慢地提供CSS2和CSS3選擇符的完全實現,這意味著你可能還不知道它們所提供的一些很酷的新功能。如果你對CSS的所有的新而酷的功能感興趣,我建議探究一下W3C的關於該項目的網頁:
  CSS1 selectors: https://www.w3.org/TR/REC-CSS1
  CSS2 selectors: https://www.w3.org/TR/REC-CSS2/selector.html
  CSS3 selectors: https://www.w3.org/TR/2005/WD-css3-selectors-20051215/
  每種CSS選擇符規範中可用的的功能大體上是相似的,因為後繼的版本總是包含前麵版本的所有功能。然而,每一個版本都加入了一些新的功能。舉例來說,CSS2包含屬性和子代選擇符,而CSS3提供了額外的語言支持,通過屬性類型和否定來選擇。比如,下麵都是有效的CSS選擇符:
  #main div p: 此表達式查找一個ID為"main"的元素的所有的後代<div>元素的所有的後代<p>元素。這是一個正確的CSS1選擇符。
  div.items > p: 此表達式查找所有的類名包含"items"的<div>元素,然後找出所有的子代<p>元素。這是一個有效的CSS2選擇符。
  div:not(.items): 此表達式查找所有類名不包含"items"的<div>元素。這是一個有效的CSS3選擇符。
  現在,你可能會奇怪為什麼我會討論CSS選擇符,如果不能實際地使用它們來定位元素(隻應用於CSS樣式)的話。一些富有進取精神的開發者在著手於此,創建了能夠處理從CSS1到全部的CSS3的CSS選擇符實現。使用這些庫,你將能夠快速而容易地選擇任何元素並對它們進行操作。

  cssQuery

  第一個公開可用的支持全部CSS1-CSS3的庫被稱為cssQuery,由Dean Edwards(https://dean.edwards.name)創建。它背後的前提是簡單的:給出一個選擇符,cssQuery將找到所有匹配的元素。另外,cssQuery被分割成許多子庫,每一個對應於CSS選擇符的一個時期,這意味著如果不需要CSS3支持的話,你可以把它排除在外。這一特別的庫徹底而廣泛,可工作於所有的現代瀏覽器中(Dean是一個堅定的跨瀏覽器支持者)。為了使用這個庫,你需要提供選擇器,以及可選的在其中搜索的上下文元素。下麵是幾個示例:

//查找<div>元素的所有子代<p>元素
cssQuery("div > p");
//查找所有的<div>,<p>,<form>
cssQuery("div,p,form");
//查找所有的<p>和<div>,然後查找他們內部的所有<a>元素
var p = cssQuery("p,div");
cssQuery("a",p);

  執行cssQuery函數會返回一個匹配元素的數組。你可以對它實施操作,就好像你剛剛執行了一次getElementsByTagName。比如說,為了給所有鏈接到Google的鏈接加上邊框,你可以執行以下操作:

//為所有指向Google的鏈接加上邊框
var g = cssQuery("a[href^='google.com']");
for ( var i = 0; i < g.length; i++ ) {
    g[i].style.border = "1px dashed red";
}

  關於cssQuery的更多信息及完整的源代碼下載可以在Dean Edwards的網站上找到:https://dean.edwards.name/my/cssQuery/
  提示:Dean Edwards是一位JavaScript奇才;他的代碼絕對是令人吃驚的。我極力推薦你到他的cssQuery庫裏去逛逛,至少看看優秀的可擴展的JavaScript代碼是怎麼寫的。

  jQuery

  這是JavaScript庫的世界裏新近的加入者,但是提供了一些值得注意的編寫JavaScript代碼的方式。我最初隻是想把他寫成“簡單的”CSS選擇符庫(跟cssQuery相似),直到Dean Edwards發布他的傑出的cssQuery庫迫使這些代碼向另一個不同的方向發展。這個庫提供完全的CSS1-CSS3選擇符的支持以及一些基本的XPath功能。在此之上,它還提供了進行更深入的DOM導航和操作的能力。跟cssQuery一樣,jQuery也完全支持現代瀏覽器。這裏有幾個使用jQuery自定義的CSS的XPath的混合物選擇元素的例子:

//查找所有的類名包括"links"且其內部有<p>元素的<div>元素
$("div.links[p]")
//查找所有<p>元素和<div>元素的後代
$("p,div").find("*")
//查找不指向Google的所有<a>超鏈接
$("a[@href^='google.com']:even")

  為了使用jQuery得到的結果,你有兩種選擇。首先,你可以執行$("expression").get()來得到匹配元素的一個數組——與cssQuery完全相同的結果。你可以做的第二件事是使用jQuery的獨有的內建函數操作CSS和DOM。於是,回到用cssQuery為所指向Google的鏈接加邊框的例子,你可以這麼做:

//為所有指向Google的鏈接加上邊框
$("a[@href^=google.com]").css("border","1px dashed red");

  在jQuery的項上網站上可以找到大量的示例、演示和文檔,以及可定製的下載:https://jquery.com
  注意:應該指出的是,無論是cssQuery還是jQuery,實際上都不要一定求使用HTML文檔來導航;它們適用於任何XML文檔。下節的XPath將為你講述純XML形式的導航。

  XPath

  XPath表達式是一種導航XML文檔的極其強大的方法。XPath已經存在了好幾年;幾乎可以說隻要有DOM的實現,就會有XPath緊隨其後。XPath表達式要比用CSS選擇符可以寫出的任何東西都強大得多,即便他們更加冗長。表5-1列舉了一些不同的CSS選擇符與XPath表達式的並行比較。
  表5-1. CSS3選擇符與XPath表達式的比較
  ————————————————————————————————————
  目標                                          CSS3                     XPath
  ————————————————————————————————————
  所有元素                                          *                     //*
  所有<p>元素                                   p                     //p
  所有子元素                                   p>*                     //p/*
  特定ID的元素                                   #foo                     //*[@id='foo']
  特定class的元素                                   .foo                     //*[contains(@class,'foo')]
  帶屬性的元素                                   *[title]                     //*[@title]
  <p>的第一個子元素                            p>&.first-child       //p/*[0]
  擁有一個子元素的所有<p>元素              不能實現              //p[a]
  下一個元素                                          p+*              //p/following-sibling::*[0]
  ————————————————————————————————————
  如果前麵的表達式激起了你的興趣,我推薦你去瀏覽兩個XPath規範(不過,XPath 1.0 通常是唯一被現代瀏覽器所完全支持的),感覺一下那些表達式是如何工作的。
  XPath 1.0: https://www.w3.org/TR/xpath/
  XPath 1.0: https://www.w3.org/TR/xpath20/
  如果你想對該主題進行深入的研究,我推薦你閱讀Elliotte Harold和Scott Means所著的XML in a Nutshell(O'Reilly,2004),或者Jeni Tennison所著的XSLT 2.0:From Novice To Professional(Apress,2005)。另外,有一些很好的教程可以幫助你開始使用XPath:
  W3Schools的XPath教程: https://w3schools.com/xpath/
  ZVON XPath教程: https://zvon.org/xxl/XPathTutorial/General/examples.html
  目前,瀏覽器對XPath的支持是零星的;IE和Mozilla都支持全部(盡管各不相同)的XPath實現,而Safari和Opera都隻有正在開發中的版本。為解決這一問題,有幾種完全用JavaScript編寫的XPath實現。它們一般都很慢(與基於瀏覽器的XPath實現相比),但是可以在所有瀏覽器裏穩定地工作。
  XML for Script: https://xmljs.sf.net/
  Google AJAXSLT: https://goog-ajaxslt.sf.net
  另外,一個名為Sarissa(https://sarissa.sf.net)的項目立誌於針對每種瀏覽器實現創建一個通用的包裝。這能給你隻須一次編寫XML訪問代碼的能力,而仍能獲得瀏覽器所支持的XML解析的所有速度優勢。這一技術最大的問題是在Opera和Safari瀏覽器裏它仍缺乏對XPath的支持。
  與廣泛支持的純JavaScript方案相比,使用瀏覽器內建的XPath通常被認為是實驗性的技術。但是,XPath的使用和流行隻會增長,它肯定應該被看作CSS選擇器王位的強勁的競爭者。
  既然你已經擁有了定位任何一個甚至是一組DOM元素必須的知識和工具,我們現在應該討論你可以使用該能力做些什麼。從屬性的操作到DOM元素的添加或刪除,一切都是可能的。

[ 本帖最後由 mozart0 於 2007-4-13 23:52 編輯 ]

頂部
one by one
mozart0

匪徒田老大



帖子 2326
體力 6628
威望 177
注冊 2003-6-18

發表於 2007-4-13 21:03  資料  短消息  加為好友  QQ
獲取元素的內容

  所有的DOM元素可以包含一種或三種東西:文本,元素,或文本與元素的混合。大致說來,最常見的是第一種和第三種情況。在這一節裏你將學到檢索元素內容的幾種常見的方式。

  獲取元素內的文本

  對於新接觸DOM的人來說,獲取元素內部的文本可能是最令人困惑的任務。然而,它也是一種在HTML DOM和XML DOM裏都能需要的,因而了解怎樣實現將很適合你。在圖5-3中的示例DOM結構裏,一個根元素<p>包含了一個<strong>元素和一個文本塊。<strong>元素本身又包含了一個文本塊。
[attach]33279[/attach]
  圖5-3. 同時包含元素和文本的示

最後更新:2017-04-02 00:06:23

  上一篇:go 把指定的DataSet中的內容導出到Excel中
  下一篇:go Pro JavaScript Techniques第一章: 現代javscript編程