在HTML 的外部文件中一樣,標記的組織與其樣式、格式和行為是分離的。雖然您絕對可以用JavaScript 更改元素或文字的樣式,但實際更改您的標記所佈置的組織卻更有趣。
只要牢記您的標記只為您的頁面提供組織、框架,您就能立於不敗之地。再前進一小步,您就會明白瀏覽器是如何接受所有的文字組織並將其轉變為超級有趣的一些東西的,即一組對象,其中每個對像都可被更改、添加或刪除。
文字標記的優點
在討論Web 瀏覽器之前,值得考慮為什麼純文字絕對是儲存HTML 的最佳選擇(有關詳細信息,請參閱有關標記的一些其他想法)。不考慮優缺點,只是回憶一下在每次查看頁面時HTML 是透過網路發送到Web 瀏覽器的(為了簡潔,不考慮高速緩存等)。真是再沒有比傳遞文字再有效的方法了。二進位物件、頁面圖形表示、重新組織的標記區塊等等,所有這一切都比純文字檔案透過網路傳遞要更困難。
此外,瀏覽器也為此增添光彩。現今的瀏覽器允許使用者更改文字大小、按比例伸縮圖像、下載頁面的CSS 或JavaScript(大多數情況),甚至更多,這完全排除了將任何類型的頁面圖形表示傳送到瀏覽器上。但是,瀏覽器需要原始HTML,這樣它才能在瀏覽器中對頁面套用任何處理,而不是信任瀏覽器去處理該任務。同樣地,將CSS 從JavaScript 分離和將CSS 從HTML 標記分離要求一種容易分離的格式。文字檔案又一次成為該任務的最佳方法。
最後但同樣重要的一點是,記住,新標準(例如HTML 4.01 與XHTML 1.0 和1.1)承諾將內容(頁面中的資料)與表示和樣式(通常由CSS 應用)分開。如果程式設計師要將HTML 與CSS 分離,然後強制瀏覽器檢索黏結頁面各部分的一些頁面表示,這會失去這些標準的多數優點。保持這些部分到達瀏覽器時都一直分離使得瀏覽器在從伺服器獲取HTML 時有了前所未有的靈活性。
關於標記的其他想法
純文字編輯:是對是錯?
純文字是儲存標記的理想選擇,但是不適合編輯標記。大行其道的是使用IDE,例如Macromedia DreamWeaver 或更強勢點的Microsoft® FrontPage®,來操作Web 頁面標記。這些環境通常提供捷徑和協助來建立Web 頁面,尤其是在使用CSS 和JavaScript 時,二者都來自實際頁面標記以外的檔案。許多人仍偏愛好用古老的記事本或vi(我承認我也是其中一員),這並不要緊。不管怎樣,最終結果都是充滿標記的文字檔案。
網路上的文字:好東西
已經說過,文字是文件的最佳媒體,例如HTML 或CSS,在網路上被千百次地傳輸。當我說瀏覽器表示文字很難時,是特別指將文字轉換為使用者檢視的可視圖形頁面。這與瀏覽器實際上如何從Web 瀏覽器檢索頁面沒有關係;在這種情況下,文字仍然是最佳選擇。
文字標記的缺點
正如文字標記對於設計人員和頁面創建者俱有驚人的優點之外,它對於瀏覽器也具有相當出奇的缺點。具體來說,瀏覽器很難直接將文字標記視覺地表示給使用者(詳細資訊請參閱有關標記的一些其他想法)。考慮下列常見的瀏覽器任務:
·基於元素類型、類別、ID 及其在HTML 文件中的位置,將CSS 樣式(通常來自外部文件中的多個樣式表)套用至標記。
·基於JavaScript 程式碼(通常位於外部文件)將樣式和格式套用至HTML 文件的不同部分。
·基於JavaScript 程式碼更改表單欄位的值。
·基於JavaScript 程式碼,支援視覺效果,例如影像翻轉和影像交換。
複雜性並不在於編碼這些任務;其中每件事都是相當容易的。複雜度來自實際實作請求動作的瀏覽器。如果標記儲存為文本,例如,想要在center-text 類別的p 元素中輸入文本(text-align: center),如何實現呢?
·將內聯樣式加入文字嗎?
·將樣式套用到瀏覽器中的HTML 文本,並只保持內容居中或不居中?
·套用無樣式的HTML,然後事後套用格式?
這些非常困難的問題是如今很少人寫瀏覽器的原因。 (編寫瀏覽器的人應該接受最由衷的感謝)
無疑,純文字不是儲存瀏覽器HTML 的好辦法,儘管文字是獲取頁面標記最好的解決方案。如果加上JavaScript 改變頁面結構的能力,事情就變得有些微妙了。瀏覽器應該將修改過的結構重新寫入磁碟嗎?如何才能保持文件的最新版本呢?
無疑,文本不是答案。它難以修改,為其應用樣式和行為很困難,與今天Web 頁面的動態本質在根本上相去甚遠。
求助於樹視圖
這個問題的答案(至少是由當今Web 瀏覽器選擇的答案)是使用樹狀結構來表示HTML。請參閱清單1,這是一個表示為本文標記的相當簡單又無聊的HTML 頁面。
清單1. 文字標籤中的簡單HTML 頁面
<html>
<head>
<title>Trees, trees, everywhere</title>
</head>
<body>
<h1>Trees, trees, everywhere</h1>
<p>Welcome to a <em>really</em> boring page.</p>
<div>
Come again soon.
<img src="come-again.gif" />
</div>
</body>
</html>
瀏覽器接受該頁面並將其轉換為樹狀結構,如圖1 所示。
為了保持本文的進度,我做了少許簡化。 DOM 或XML 的專家會意識到空白對於文件文字在Web 瀏覽器樹結構中表示和分解方式的影響。膚淺的了解只會使事情變得模稜兩可,所以如果想弄清楚空白的影響,那最好不過了;如果不想的話,那可以繼續讀下去,不要考慮它。當它成為問題時,那時您就會明白您需要的一切。
除了實際的樹背景之外,可能會首先註意到樹中的一切是以最外層的HTML 包含元素,即html 元素開始的。使用樹的比喻,這叫做根元素。所以即使這是樹的底層,當您查看並分析樹的時候,我通常會以此開始。如果它確實奏效,您可以將整個樹顛倒一下,但這確實有點拓展了樹的比喻。
從根流出的線表示不同標記部分之間的關係。 head 和body 元素是html 根元素的孩子;title 是head 的孩子,而文字「Trees, trees, everywhere」 是title 的孩子。整個樹就這樣組織下去,直到瀏覽器獲得與圖1 類似的結構。
一些附加術語
為了沿用樹的比喻,head 和body 被稱為html 的分支(branches)。叫分支是因為它們有自己的孩子。當到達樹的末端時,您將進入主要的文本,例如“Trees, trees, everywhere” 和“really”;這些通常稱為葉子,因為它們沒有自己的孩子。您不需要記住所有這些術語,當您試圖弄清楚特定術語的意思時,只要想像一下樹狀結構就容易多了。
物件的值
既然了解了一些基本的術語,現在應該關註一下其中包含元素名稱和文字的小矩形了(圖1)。每個矩形是一個物件;瀏覽器在其中解決一些文字問題。透過使用物件來表示HTML 文件的每一部分,可以輕鬆更改組織、應用程式樣式、允許JavaScript 存取文檔,等等。
物件類型和屬性
標記的每個可能類型都有自己的物件類型。例如,HTML 中的元素以Element 物件類型表示。文件中的文字以Text 類型表示,屬性以Attribute 類型表示,以此類推。
所以Web 瀏覽器不僅可以使用物件模型來表示文件(從而避免了處理靜態文字),還可以用物件類型立即辨別出某事物是什麼。 HTML 文件被解析並轉換為物件集合,如圖1 所示,然後尖括號和轉義序列(例如,使用< 表示<,使用> 表示>)等事物不再是問題了。這就使得瀏覽器的工作(至少在解析輸入HTML 之後)變得更容易。弄清楚某事物究竟是元素還是屬性並確定如何處理該類型的對象,這些操作都十分簡單了。
透過使用對象,Web 瀏覽器可以變更這些對象的屬性。例如,每個元素物件具有一個父元素和一系列子元素。所以新增新的子元素或文字只需要在元素的子元素清單中新增一個新的子元素。這些物件也具有style 屬性,所以快速更改元素或文字段的樣式非常簡單。例如,要使用JavaScript 更改div 的高度,如下所示:
someDiv.style.height = "300px";
換句話說,Web 瀏覽器使用物件屬性可以非常容易地更改樹的外觀和結構。將之比作瀏覽器在內部將頁面表示為文字時必須進行的複雜事情,每次更改屬性或結構都需要瀏覽器重新編寫靜態文件、重新解析並在螢幕上重新顯示。有了對象,所有這一切都解決了。
現在,花點時間展開一些HTML 文件並用樹將其勾勒出來。儘管這看起來是個不尋常的請求(尤其是在包含極少程式碼的這樣一篇文章中),如果您希望能夠操縱這些樹,那麼需要熟悉它們的結構。
在這個過程中,可能會發現一些古怪的事情。例如,考慮下列情況:
·屬性發生了什麼事?
·分解為元素(如em 和b)的文字呢?
·結構不正確(例如缺少結束p 標記時)的HTML 呢?
一旦熟悉這些問題之後,就能更理解下面幾節了。
嚴格有時是好事
如果嘗試剛提到的練習I,您可能會發現標記的樹視圖中存在一些潛在問題(如果不練習的話,那就聽我說吧!)。事實上,在清單1 和圖1 就會發現一些問題,首先看p 元素是如何分解的。如果您問通常的Web 開發人員“p 元素的文字內容是什麼”,最常見的答案將是“Welcome to a really boring Web page.”。如果將之與圖1 做比較,將會發現這個答案(雖然合乎邏輯)是根本不正確的。
實際上,p 元素具有三個不同的子對象,其中沒有一個包含完整的“Welcome to a really boring Web page.” 文本。您會發現文字的一部分,例如“Welcome to a ” 和“ boring Web page”,但不是全部。為了理解這一點,記住標記中的任何內容都必須轉換為某種類型的物件。
此外,順序無關緊要!如果瀏覽器顯示正確的對象,但顯示順序與您在HTML 中提供的順序不同,那麼您能想像出使用者將如何回應Web 瀏覽器嗎?段落夾在頁面標題和文章標題中間,而這不是您自己組織文件時的樣式呢?很顯然,瀏覽器必須保持元素和文字的順序。
在本例中,p 元素有三個不同部分:
·em 元素之前的文本·em 元素本身·em 元素之後的文本
如果將該順序打亂,可能會把重點放在文本的錯誤部分。為了保持一切正常,p 元素有三個子對象,其順序是在清單1 的HTML 中顯示的順序。而且,重點文字「really」 不是p 的子元素;而是p 的子元素em 的子元素。
理解這一概念非常重要。儘管「really」 文字將可能與其他p 元素文字一起顯示,但它仍是em 元素的直接子元素。它可以具有與其他p 文字不同的格式,並且可以獨立於其他文字到處移動。
要將之牢記在心,試著用圖表示清單2 和3 中的HTML,確保文字具有正確的父元素(而不管文字最終會如何顯示在螢幕上)。
清單2. 帶有巧妙元素嵌套的標記
<html>
<head>
<title>This is a little tricky</title>
</head>
<body>
<h1>Pay <u>close</u> attention, OK?</h1>
<div>
<p>This p really isn't <em>necessary</em>, but it makes the
<span id="bold-text">structure <i>and</i> the organization</span>
of the page easier to keep up with.</p>
</div>
</body>
</html>
清單3. 更巧妙的元素巢狀
<html>
<head>
<title>Trickier nesting, still</title>
</head>
<body>
<div id="main-body">
<div id="contents">
<table>
<tr><th>Steps</th><th>Process</th></tr>
<tr><td>1</td><td>Figure out the <em>root element</em>.</td></tr>
<tr><td>2</td><td>Deal with the <span id="code">head</span> first,
as it's usually easy.</td></tr>
<tr><td>3</td><td>Work through the <span id="code">body</span>.
Just <em>take your 時間</em>.</td></tr>
</table>
</div>
<div id="closing">
This link is <em>not</em> active, but if it were, the answers
to this <a href="answers.html"><img src="exercise.gif" /></a> would
be there. But <em>do the exercise anyway!</em>
</div>
</div>
</body>
</html>
在本文末的GIF 檔案圖2 中的tricky-solution.gif 和圖3 中的trickier-solution.gif 中將會找到這些練習的答案。不要偷看,先花點時間自動解答。這樣能幫助您理解組織樹時所應用的規則有多嚴格,並真正幫助您掌握HTML 及其樹狀結構。
屬性呢?
當您試圖弄清楚如何處理屬性時,是否遇到一些問題?前已提及,屬性確實具有自己的物件類型,但屬性確實不是顯示它的元素的子元素,嵌套元素和文字不在同一屬性“層級”,您將注意到,清單2 和3 中練習的答案沒有顯示屬性。
屬性事實上儲存在瀏覽器使用的物件模型中,但它們有一些特殊情況。每個元素都有可用屬性的列表,且與子物件列表是分開的。所以div 元素可能有一個包含屬性「id」 和另一個屬性「class」 的清單。
記住,元素的屬性必須有惟一的名稱,也就是說,一個元素不能有兩個「id」 或兩個「class」 屬性。這使得列表易於維護和存取。在下一篇文章將會看到,您可以簡單地呼叫諸如getAttribute("id") 的方法來按名稱取得屬性的值。也可以用相似的方法呼叫來新增屬性或設定(重置)現有屬性的值。
值得指出的是,屬性名的惟一性使得該清單不同於子物件清單。 p 元素可以有多個em 元素,所以子物件清單可以包含多個重複項。儘管子項清單和屬性清單的操作方式相似,但一個可以包含重複項(物件的子項),而一個不能(元素物件的屬性)。最後,只有元素具有屬性,所以文字物件沒有用於儲存屬性的附加清單。
凌亂的HTML
在繼續之前,談到瀏覽器如何將標記轉換為樹表示,還有一個主題值得探討,即瀏覽器如何處理不是格式良好的標記。格式良好是XML 廣泛使用的術語,有兩個基本意義:
·每個開始標記都有一個與之相符的結束標記。所以每個<p> 在文件中與</p> 匹配,每個<div> 與</div> 匹配,等等。
·最裡面的開始標記與最裡面的結束標記相匹配,然後次裡面的開始標記與次裡面的結束標記相匹配,依此類推。所以<b><i>bold and italics</b></i> 是不合法的,因為最裡面的開始標記<i> 與最裡面的結束標記<b> 匹配不當。要使之格式良好,要么切換開始標記順序,要么切換結束標記順序。 (如果兩者都切換,則仍會出現問題)。
深入研究這兩條規則。這兩個規則不僅簡化了文件的組織,也消除了不定性。是否應先施用粗體後施以斜體?或恰恰相反?如果覺得這種順序和不定性不是大問題,那麼請記住,CSS 允許規則覆蓋其他規則,所以,例如,如果b 元素中文字的字體不同於i 元素中的字體,則格式的應用順序將變得非常重要。因此,HTML 的格式良好性有著舉足輕重的影響。
如果瀏覽器收到了不是格式良好的文檔,它只會盡力而為。得到的樹結構在最好情況下將是作者希望的原始頁面的近似,最壞情況下將面目全非。如果您曾經將頁面載入到瀏覽器後看到完全出乎意料的結果,您可能在看到瀏覽器結果時會猜想您的結構應該如何,並沮喪地繼續工作。當然,搞定這個問題相當簡單:確保文件是格式良好的!如果不清楚如何撰寫標準化的HTML,請諮詢參考資料以獲得協助。
DOM 簡介
到目前為止,您已經知道瀏覽器將Web 頁面轉換為物件表示,可能您甚至會猜想,物件表示是DOM 樹。 DOM 表示Document Object Model,是一個規範,可從World Wide Web Consortium (W3C) 取得(您可以參閱參考資料中的一些DOM 相關連結)。
但更重要的是,DOM 定義了物件的類型和屬性,從而允許瀏覽器表示標記。 (本系列下一篇文章將專門講述在JavaScript 和Ajax 程式碼中使用DOM 的規格。)
文件物件
首先,需要存取物件模型本身。這非常容易;要在運行於Web 頁面上的任何JavaScript 程式碼中使用內建document 變量,可以編寫如下程式碼:
var domTree = document;
當然,該程式碼本身沒什麼用,但它演示了每個Web 瀏覽器使得document物件可用於JavaScript 程式碼,並示範了物件表示標記的完整樹(圖1)。
每項都是一個節點
顯然,document 物件很重要,但這只是開始。在進一步深入之前,需要學習另一個術語:節點。您已經知道標記的每個部分都由一個對象表示,但它不只是一個任意的對象,它是特定類型的對象,一個DOM 節點。更特定的類型,例如文字、元素和屬性,都繼承自這個基本的節點類型。所以可以有文字節點、元素節點和屬性節點。
如果已經有很多JavaScript 程式設計經驗,那您可能已經在使用DOM 程式碼了。如果到目前為止您一直在追蹤本Ajax 系列,那麼現在您一定使用DOM 程式碼已經有一段時間了。例如,程式碼行var number = document.getElementById("phone").value; 使用DOM 尋找特定元素,然後擷取該元素的值(在本例中為表單欄位)。所以即使您沒有意識到這一點,但您每次將document 鍵入JavaScript 程式碼時都會使用DOM。
詳細解釋已經學過的術語,DOM 樹是物件的樹,但更具體地說,它是節點物件的樹。在Ajax 應用程式中或任何其他JavaScript 中,可以使用這些節點產生下列效果,例如移除元素及其內容,突出顯示特定文本,或新增圖像元素。因為都發生在客戶端(運行在Web 瀏覽器中的程式碼),所以這些效果立即發生,而不與伺服器通訊。最終結果通常是應用程式感覺起來響應更快,因為當請求轉向伺服器時以及解釋回應時,Web 頁面上的內容變更不會出現長時間的停頓。
在多數程式語言中,需要學習每種節點類型的實際物件名稱,學習可用的屬性,並弄清楚類型和強制轉換;但在JavaScript 中這都不是必需的。您可以只建立一個變量,並為它分配您想要的物件(如您已經看到的):
var domTree = document;
var phoneNumberElement = document.getElementById("phone");
var phoneNumber = phoneNumberElement.value;
沒有類型,JavaScript 根據需要建立變數並為其指派正確的類型。結果,從JavaScript 中使用DOM 變得微不足道(將來有一篇文章會專門講述與XML 相關的DOM,那時將更加巧妙)。
結束語
在這裡,我要給您留一點懸念。顯然,這並非是對DOM 完全詳盡的說明;事實上,本文不過是DOM 的簡介。 DOM 的內容要遠遠多於我今天介紹的這些!
本系列的下一篇文章將擴展這些觀點,並深入探討如何在JavaScript 中使用DOM 來更新Web 頁面、快速更改HTML 並為您的用戶創建更互動的體驗。在後面專門講述在Ajax 請求中使用XML 的文章中,我將再次返回來討論DOM。所以要熟悉DOM,它是Ajax 應用程式的一個主要部分。
此時,深入了解DOM 將十分簡單,例如詳細設計如何在DOM 樹中移動、獲得元素和文字的值、遍歷節點列表,等等,但這可能會讓您有這種印象,即DOM 是關於程式碼的,而事實上並非如此。
在閱讀下一篇文章之前,試著思考一下樹狀結構並用一些您自己的HTML 實踐一下,以查看Web 瀏覽器是如何將HTML 轉換為標記的樹視圖的。此外,思考一下DOM 樹的組織,並用本文介紹的特殊情況實踐一下:屬性、有元素混合在其中的文字、沒有文字內容的元素(例如img 元素)。
如果紮實掌握了這些概念,然後學習了JavaScript 和DOM 的語法(下一篇文章),則會讓回應更容易。
而且不要忘了,這裡有清單2 和3 的答案,其中還包含了範例程式碼!