Brett McLaughlin, 作者和編輯, O‘Reilly Media Inc. 2006 年 4 月 10 日 程序員(使用后端應(yīng)用程序)和 Web 程序員(編寫 HTML、CSS 和 JavaScript)之間的分水嶺是長久存在的。但是,Document Object Model (DOM) 彌補(bǔ)了這個(gè)裂縫,使得在后端使用 XML 同時(shí)在前端使用 HTML 切實(shí)可行,并成為極其有效的工具。在本文中,Brett McLaughlin 介紹了 Document Object Model,解釋它在 Web 頁面中的應(yīng)用,并開始挖掘其在 JavaScript 中的用途。 與許多 Web 程序員一樣,您可能使用過 HTML。HTML 是程序員開始與 Web 頁面打交道的方式;HTML 通常是他們完成應(yīng)用程序或站點(diǎn)前的最后一步——調(diào)整一些布局、顏色或樣式。不過,雖然經(jīng)常使用 HTML,但對于 HTML 轉(zhuǎn)到瀏覽器呈現(xiàn)在屏幕上時(shí)到底發(fā)生了什么,人們普遍存在誤解。在我分析您認(rèn)為可能發(fā)生的事情及其可能錯(cuò)誤的原因之前,我希望您對設(shè)計(jì)和服務(wù) Web 頁面時(shí)涉及的過程一清二楚:
這看起來非常基礎(chǔ),但事情很快會(huì)變得有趣起來。事實(shí)上,步驟 4 和步驟 5 之間發(fā)生的巨大數(shù)量的 “填充物(stuff)” 就是本文的焦點(diǎn)。術(shù)語 “填充物” 也十分適用,因?yàn)槎鄶?shù)程序員從來沒有真正考慮過當(dāng)用戶瀏覽器請求顯示標(biāo)記時(shí)到底在標(biāo)記身上發(fā)生了什么。
實(shí)踐證明,所有這些問題的答案都是 Document Object Model。因此,廢話少說,直接研究 DOM。 對于多數(shù)程序員,當(dāng) Web 瀏覽器開始時(shí)他們的工作就結(jié)束了。也就是說,將一個(gè) HTML 文件放入 Web 瀏覽器的目錄上后,您通常就認(rèn)為它已經(jīng)“完成”,而且(滿懷希望地)認(rèn)為再也不會(huì)考慮它!說到編寫干凈、組織良好的頁面時(shí),這也是一個(gè)偉大的目標(biāo);希望您的標(biāo)記跨瀏覽器、用各種版本的 CSS 和 JavaScript 顯示它應(yīng)該顯示的內(nèi)容,一點(diǎn)錯(cuò)都沒有。 問題是這種方法限制了程序員對瀏覽器中真正發(fā)生的事情的理解。更重要的是,它限制了您用客戶端 JavaScript 動(dòng)態(tài)更新、更改和重構(gòu) Web 頁面的能力。擺脫這種限制,讓您的 Web 站點(diǎn)擁有更大的交互性和創(chuàng)造性。 作為典型的 Web 程序員,您可能啟動(dòng)文本編輯和 IDE 后就開始輸入 HTML、CSS 甚至 JavaScript。很容易認(rèn)為這些標(biāo)記、選擇器和屬性只是使站點(diǎn)正確顯示而做的小小的任務(wù)。但是,在這一點(diǎn)上您需要拓展您的思路,要意識到您是在組織您的內(nèi)容。不要擔(dān)心;我保證這不會(huì)變成關(guān)于標(biāo)記美觀、您必須如何認(rèn)識到 Web 頁面的真正潛力或其他任何元物質(zhì)的講座。您需要了解的是您在 Web 開發(fā)中到底是什么角色。 說到頁面的外觀,頂多您只能提提建議。您提供 CSS 樣式表時(shí),用戶可以覆蓋您的樣式選擇。您提供字體大小時(shí),用戶瀏覽器可以為視障者更改這些大小,或者在大顯示器(具有同等大的分辨率)上按比例縮小。甚至您選擇的顏色和字體也受制于用戶顯示器和用戶在其系統(tǒng)上安裝的字體。雖然盡您所能來設(shè)計(jì)頁面樣式很不錯(cuò),但這絕不是 您對 Web 頁面的最大影響。 您絕對控制的是 Web 頁面的結(jié)構(gòu)。您的標(biāo)記不可更改,用戶就不能亂弄;他們的瀏覽器只能從您的 Web 服務(wù)器檢索標(biāo)記并顯示它(雖然樣式更符合用戶的品味而不是您自己的品味)。但頁面組織,不管是在該段落內(nèi)還是在其他分區(qū),都只由您單獨(dú)決定。要是想實(shí)際更改您的頁面(這是大多數(shù) Ajax 應(yīng)用程序所關(guān)注的),您操作的是頁面的結(jié)構(gòu)。盡管很容易更改一段文本的顏色,但在現(xiàn)有頁面上添加文本或整個(gè)區(qū)段要難得多。不管用戶如何設(shè)計(jì)該區(qū)段的樣式,都是由您控制頁面本身的組織。 一旦意識到您的標(biāo)記是真正與組織相關(guān)的,您就會(huì)對它另眼相看了。不會(huì)認(rèn)為 還應(yīng)該清楚,樣式和行為(事件處理程序和 JavaScript)是在事后 應(yīng)用于該組織的。標(biāo)記就緒以后才能對其進(jìn)行操作或設(shè)計(jì)樣式。所以,正如您可以將 CSS 保存在 HTML 的外部文件中一樣,標(biāo)記的組織與其樣式、格式和行為是分離的。雖然您肯定可以用 JavaScript 更改元素或文本的樣式,但實(shí)際更改您的標(biāo)記所布置的組織卻更加有趣。 只要牢記您的標(biāo)記只為您的頁面提供組織、框架,您就能立于不敗之地。再前進(jìn)一小步,您就會(huì)明白瀏覽器是如何接受所有的文本組織并將其轉(zhuǎn)變?yōu)槌売腥さ囊恍〇|西的,即一組對象,其中每個(gè)對象都可被更改、添加或刪除。
在討論 Web 瀏覽器之前,值得考慮一下為什么純文本絕對 是存儲 HTML 的最佳選擇(有關(guān)詳細(xì)信息,請參閱 有關(guān)標(biāo)記的一些其他想法)。不考慮優(yōu)缺點(diǎn),只是回憶一下在每次查看頁面時(shí) HTML 是通過網(wǎng)絡(luò)發(fā)送到 Web 瀏覽器的(為了簡潔,不考慮高速緩存等)。真是再?zèng)]有比傳遞文本再有效的方法了。二進(jìn)制對象、頁面圖形表示、重新組織的標(biāo)記塊等等,所有這一切都比純文本文件通過網(wǎng)絡(luò)傳遞要更困難。 此外,瀏覽器也為此增光添彩。今天的瀏覽器允許用戶更改文本大小、按比例伸縮圖像、下載頁面的 CSS 或 JavaScript(大多數(shù)情況),甚至更多,這完全排除了將任何類型的頁面圖形表示發(fā)送到瀏覽器上。但是,瀏覽器需要原 HTML,這樣它才能在瀏覽器中對頁面應(yīng)用任何處理,而不是信任瀏覽器去處理該任務(wù)。同樣地,將 CSS 從 JavaScript 分離和將 CSS 從 HTML 標(biāo)記分離要求一種容易分離的格式。文本文件又一次成為該任務(wù)的最好方法。 最后但同樣重要的一點(diǎn)是,記住,新標(biāo)準(zhǔn)(比如 HTML 4.01 與 XHTML 1.0 和 1.1)承諾將內(nèi)容(頁面中的數(shù)據(jù))與表示和樣式(通常由 CSS 應(yīng)用)分離。如果程序員要將 HTML 與 CSS 分離,然后強(qiáng)制瀏覽器檢索粘結(jié)頁面各部分的一些頁面表示,這會(huì)失去這些標(biāo)準(zhǔn)的多數(shù)優(yōu)點(diǎn)。保持這些部分到達(dá)瀏覽器時(shí)都一直分離使得瀏覽器在從服務(wù)器獲取 HTML 時(shí)有了前所未有的靈活性。 對于一些 Web 程序員來說,在前文閱讀到的所有內(nèi)容可能是對您在 Web 開發(fā)過程中角色的滑稽講述。但說到瀏覽器的行為,許多最能干的 Web 設(shè)計(jì)人員和開發(fā)人員通常都沒有意識到 “內(nèi)幕” 的實(shí)際狀況。我將在本節(jié)重點(diǎn)進(jìn)行講述。不要擔(dān)心,代碼很快就到,但是要克制您編碼的急躁心情,因?yàn)檎嬲私?Web 瀏覽器的確切行為對于您的代碼正確工作是非常關(guān)鍵的。 正如文本標(biāo)記對于設(shè)計(jì)人員和頁面創(chuàng)建者具有驚人的優(yōu)點(diǎn)之外,它對于瀏覽器也具有相當(dāng)出奇的缺點(diǎn)。具體來說,瀏覽器很難直接將文本標(biāo)記可視地表示給用戶(詳細(xì)信息請參閱 有關(guān)標(biāo)記的一些其他想法)。考慮下列常見的瀏覽器任務(wù):
復(fù)雜性并不在于編碼這些任務(wù);其中每件事都是相當(dāng)容易的。復(fù)雜性來自實(shí)際實(shí)現(xiàn)請求動(dòng)作的瀏覽器。如果標(biāo)記存儲為文本,比如,想要在
這些非常困難的問題是如今很少有人編寫瀏覽器的原因。(編寫瀏覽器的人應(yīng)該接受最由衷的感謝) 無疑,純文本不是存儲瀏覽器 HTML 的好辦法,盡管文本是獲取頁面標(biāo)記最好的解決方案。如果加上 JavaScript 更改 頁面結(jié)構(gòu)的能力,事情就變得有些微妙了。瀏覽器應(yīng)該將修改過的結(jié)構(gòu)重新寫入磁盤嗎?如何才能保持文檔的最新版本呢? 無疑,文本不是答案。它難以修改,為其應(yīng)用樣式和行為很困難,與今天 Web 頁面的動(dòng)態(tài)本質(zhì)在根本上相去甚遠(yuǎn)。 這個(gè)問題的答案(至少是由當(dāng)今 Web 瀏覽器選擇的答案)是使用樹結(jié)構(gòu)來表示 HTML。參見 清單 1,這是一個(gè)表示為本文標(biāo)記的相當(dāng)簡單又無聊的 HTML 頁面。 清單 1. 文本標(biāo)記中的簡單 HTML 頁面
瀏覽器接受該頁面并將之轉(zhuǎn)換為樹形結(jié)構(gòu),如圖 1 所示。 圖 1. 清單 1 的樹視圖 ![]() 為了保持本文的進(jìn)度,我做了少許簡化。DOM 或 XML 方面的專家會(huì)意識到空白對于文檔文本在 Web 瀏覽器樹結(jié)構(gòu)中表示和分解方式的影響。膚淺的了解只會(huì)使事情變得模棱兩可,所以如果想弄清空白的影響,那最好不過了;如果不想的話,那可以繼續(xù)讀下去,不要考慮它。當(dāng)它成為問題時(shí),那時(shí)您就會(huì)明白您需要的一切。 除了實(shí)際的樹背景之外,可能會(huì)首先注意到樹中的一切是以最外層的 HTML 包含元素,即 從根流出的線表示不同標(biāo)記部分之間的關(guān)系。 為了沿用樹的比喻, 既然了解了一些基本的術(shù)語,現(xiàn)在應(yīng)該關(guān)注一下其中包含元素名稱和文本的小矩形了(圖 1)。每個(gè)矩形是一個(gè)對象;瀏覽器在其中解決一些文本問題。通過使用對象來表示 HTML 文檔的每一部分,可以很容易地更改組織、應(yīng)用樣式、允許 JavaScript 訪問文檔,等等。 標(biāo)記的每個(gè)可能類型都有自己的對象類型。例如,HTML 中的元素用 所以 Web 瀏覽器不僅可以使用對象模型來表示文檔(從而避免了處理靜態(tài)文本),還可以用對象類型立即辨別出某事物是什么。HTML 文檔被解析并轉(zhuǎn)換為對象集合,如 圖 1 所示,然后尖括號和轉(zhuǎn)義序列(例如,使用 通過使用對象,Web 瀏覽器可以更改這些對象的屬性。例如,每個(gè)元素對象具有一個(gè)父元素和一系列子元素。所以添加新的子元素或文本只需要向元素的子元素列表中添加一個(gè)新的子元素。這些對象還具有
換句話說,Web 瀏覽器使用對象屬性可以非常容易地更改樹的外觀和結(jié)構(gòu)。將之比作瀏覽器在內(nèi)部將頁面表示為文本時(shí)必須進(jìn)行的復(fù)雜事情,每次更改屬性或結(jié)構(gòu)都需要瀏覽器重新編寫靜態(tài)文件、重新解析并在屏幕上重新顯示。有了對象,所有這一切都解決了。 現(xiàn)在,花點(diǎn)時(shí)間展開一些 HTML 文檔并用樹將其勾畫出來。盡管這看起來是個(gè)不尋常的請求(尤其是在包含極少代碼的這樣一篇文章中),如果您希望能夠操縱這些樹,那么需要熟悉它們的結(jié)構(gòu)。 在這個(gè)過程中,可能會(huì)發(fā)現(xiàn)一些古怪的事情。比如,考慮下列情況:
一旦熟悉這些問題之后,就能更好地理解下面幾節(jié)了。 如果嘗試剛提到的練習(xí) I,您可能會(huì)發(fā)現(xiàn)標(biāo)記的樹視圖中存在一些潛在問題(如果不練習(xí)的話,那就聽我說吧!)。事實(shí)上,在 清單 1 和 圖 1 中就會(huì)發(fā)現(xiàn)一些問題,首先看 實(shí)際上, 此外,順序無關(guān)緊要!如果瀏覽器顯示正確的對象,但顯示順序與您在 HTML 中提供的順序不同,那么您能想像出用戶將如何響應(yīng) Web 瀏覽器嗎?段落夾在頁面標(biāo)題和文章標(biāo)題中間,而這不是您自己組織文檔時(shí)的樣式呢?很顯然,瀏覽器必須保持元素和文本的順序。 在本例中,
如果將該順序打亂,可能會(huì)把重點(diǎn)放在文本的錯(cuò)誤部分。為了保持一切正常, 理解這一概念非常重要。盡管 “really” 文本將可能與其他 要將之牢記在心,試著用圖表示清單 2 和 3 中的 HTML,確保文本具有正確的父元素(而不管文本最終會(huì)如何顯示在屏幕上)。 清單 2. 帶有巧妙元素嵌套的標(biāo)記
清單 3. 更巧妙的元素嵌套
在本文末的 GIF 文件 圖 2 中的 tricky-solution.gif 和 圖 3 中的 trickier-solution.gif 中將會(huì)找到這些練習(xí)的答案。不要偷看,先花些時(shí)間自動(dòng)解答一下。這樣能幫助您理解組織樹時(shí)應(yīng)用的規(guī)則有多么嚴(yán)格,并真正幫助您掌握 HTML 及其樹結(jié)構(gòu)。 當(dāng)您試圖弄清楚如何處理屬性時(shí),是否遇到一些問題呢?前已提及,屬性確實(shí)具有自己的對象類型,但屬性確實(shí)不是顯示它的元素的子元素,嵌套元素和文本不在同一屬性 “級別”,您將注意到,清單 2 和 3 中練習(xí)的答案沒有顯示屬性。 屬性事實(shí)上存儲在瀏覽器使用的對象模型中,但它們有一些特殊情況。每個(gè)元素都有可用屬性的列表,且與子對象列表是分離的。所以 記住,元素的屬性必須具有惟一的名稱,也就是說,一個(gè)元素不能有兩個(gè) “id” 或兩個(gè) “class” 屬性。這使得列表易于維護(hù)和訪問。在下一篇文章將會(huì)看到,您可以簡單調(diào)用諸如 值得指出的是,屬性名的惟一性使得該列表不同于子對象列表。 在繼續(xù)之前,談到瀏覽器如何將標(biāo)記轉(zhuǎn)換為樹表示,還有一個(gè)主題值得探討,即瀏覽器如何處理不是格式良好的標(biāo)記。格式良好 是 XML 廣泛使用的一個(gè)術(shù)語,有兩個(gè)基本意思:
深入研究這兩條規(guī)則。這兩條規(guī)則不僅簡化了文檔的組織,還消除了不定性。是否應(yīng)先應(yīng)用粗體后應(yīng)用斜體?或恰恰相反?如果覺得這種順序和不定性不是大問題,那么請記住,CSS 允許規(guī)則覆蓋其他規(guī)則,所以,例如,如果 如果瀏覽器收到了不是格式良好的文檔,它只會(huì)盡力而為。得到的樹結(jié)構(gòu)在最好情況下將是作者希望的原始頁面的近似,最壞情況下將面目全非。如果您曾將頁面加載到瀏覽器中后看到完全出乎意料的結(jié)果,您可能在看到瀏覽器結(jié)果時(shí)會(huì)猜想您的結(jié)構(gòu)應(yīng)該如何,并沮喪地繼續(xù)工作。當(dāng)然,搞定這個(gè)問題相當(dāng)簡單:確保文檔是格式良好的!如果不清楚如何編寫標(biāo)準(zhǔn)化的 HTML,請咨詢 參考資料 獲得幫助。 到目前為止,您已經(jīng)知道瀏覽器將 Web 頁面轉(zhuǎn)換為對象表示,可能您甚至?xí)孪耄瑢ο蟊硎臼?DOM 樹。DOM 表示 Document Object Model,是一個(gè)規(guī)范,可從 World Wide Web Consortium (W3C) 獲得(您可以參閱 參考資料 中的一些 DOM 相關(guān)鏈接)。 但更重要的是,DOM 定義了對象的類型和屬性,從而允許瀏覽器表示標(biāo)記。(本系列下一篇文章將專門講述在 JavaScript 和 Ajax 代碼中使用 DOM 的規(guī)范。) 首先,需要訪問對象模型本身。這非常容易;要在運(yùn)行于 Web 頁面上的任何 JavaScript 代碼中使用內(nèi)置
當(dāng)然,該代碼本身沒什么用,但它演示了每個(gè) Web 瀏覽器使得 每項(xiàng)都是一個(gè)節(jié)點(diǎn) 顯然, 如果已經(jīng)有很多 JavaScript 編程經(jīng)驗(yàn),那您可能已經(jīng)在使用 DOM 代碼了。如果到目前為止您一直在跟蹤本 Ajax 系列,那么現(xiàn)在您一定 使用 DOM 代碼有一段時(shí)間了。例如,代碼行 詳細(xì)解釋已經(jīng)學(xué)過的術(shù)語,DOM 樹是對象的樹,但更具體地說,它是節(jié)點(diǎn) 對象的樹。在 Ajax 應(yīng)用程序中或任何其他 JavaScript 中,可以使用這些節(jié)點(diǎn)產(chǎn)生下列效果,比如移除元素及其內(nèi)容,突出顯示特定文本,或添加新圖像元素。因?yàn)槎及l(fā)生在客戶端(運(yùn)行在 Web 瀏覽器中的代碼),所以這些效果立即發(fā)生,而不與服務(wù)器通信。最終結(jié)果通常是應(yīng)用程序感覺起來響應(yīng)更快,因?yàn)楫?dāng)請求轉(zhuǎn)向服務(wù)器時(shí)以及解釋響應(yīng)時(shí),Web 頁面上的內(nèi)容更改不會(huì)出現(xiàn)長時(shí)間的停頓。 在多數(shù)編程語言中,需要學(xué)習(xí)每種節(jié)點(diǎn)類型的實(shí)際對象名稱,學(xué)習(xí)可用的屬性,并弄清楚類型和強(qiáng)制轉(zhuǎn)換;但在 JavaScript 中這都不是必需的。您可以只創(chuàng)建一個(gè)變量,并為它分配您希望的對象(正如您已經(jīng)看到的):
沒有類型,JavaScript 根據(jù)需要?jiǎng)?chuàng)建變量并為其分配正確的類型。結(jié)果,從 JavaScript 中使用 DOM 變得微不足道(將來有一篇文章會(huì)專門講述與 XML 相關(guān)的 DOM,那時(shí)將更加巧妙)。 在這里,我要給您留一點(diǎn)懸念。顯然,這并非是對 DOM 完全詳盡的說明;事實(shí)上,本文不過是 DOM 的簡介。DOM 的內(nèi)容要遠(yuǎn)遠(yuǎn)多于我今天介紹的這些! 本系列的下一篇文章將擴(kuò)展這些觀點(diǎn),并深入探討如何在 JavaScript 中使用 DOM 來更新 Web 頁面、快速更改 HTML 并為您的用戶創(chuàng)建更交互的體驗(yàn)。在后面專門講述在 Ajax 請求中使用 XML 的文章中,我將再次返回來討論 DOM。所以要熟悉 DOM,它是 Ajax 應(yīng)用程序的一個(gè)主要部分。 此時(shí),深入了解 DOM 將十分簡單,比如詳細(xì)設(shè)計(jì)如何在 DOM 樹中移動(dòng)、獲得元素和文本的值、遍歷節(jié)點(diǎn)列表,等等,但這可能會(huì)讓您有這種印象,即 DOM 是關(guān)于代碼的,而事實(shí)上并非如此。 在閱讀下一篇文章之前,試著思考一下樹結(jié)構(gòu)并用一些您自己的 HTML 實(shí)踐一下,以查看 Web 瀏覽器是如何將 HTML 轉(zhuǎn)換為標(biāo)記的樹視圖的。此外,思考一下 DOM 樹的組織,并用本文介紹的特殊情況實(shí)踐一下:屬性、有元素混合在其中的文本、沒有 文本內(nèi)容的元素(比如 如果扎實(shí)掌握了這些概念,然后學(xué)習(xí)了 JavaScript 和 DOM 的語法(下一篇文章),則會(huì)使得響應(yīng)更為容易。 而且不要忘了,這里有清單 2 和 3 的答案,其中還包含了示例代碼! 圖 2. 清單 2 的答案 ![]() 圖 3. 清單 3 的答案 ![]()
學(xué)習(xí)
獲得產(chǎn)品和技術(shù)
討論
|