DOCTYPE 與瀏覽器模式分析
DOCTYPE 的誕生
DOCTYPE,或者稱為 Document Type Declaration(文檔類型聲明,縮寫 DTD)。通常情況下,DOCTYPE 位於一個 HTML 文檔的最前面的位置,位於根元素 HTML 的起始標簽之前。因為瀏覽器必須在解析 HTML 文檔正文之前就確定當前文檔的類型,以決定其需要采用的渲染模式,不同的渲染模式會影響到瀏覽器對於 CSS 代碼甚至 JavaScript 腳本的解析。尤其是在 IE 系列瀏覽器中,由 DOCTYPE 所決定的 HTML 頁面的渲染模式至關重要。
首先看看當一個 HTML 文檔在沒有 DOCTYPE 時,頁面在各瀏覽器中會如何渲染及解析。我們嘗試生成一個在最頂端沒有 DOCTYPE 的 HTML 文檔:
<html> <head></head> <body> <script> document.write(document.compatMode); </script> </body> </html>
這個頁面在所有的瀏覽器中均返回一致的結果,頁面上打印出了“BackCompat”。 document.compatMode 屬性最初由微軟在 IE 中創造出來,這是一個只讀的屬性,返回一個字符串,只可能存在兩種返回值:
- BackCompat:標準兼容模式(Standards-compliant mode)未開啟;
- CSS1Compat:標準兼容模式已開啟。
其實這裏所謂的標準兼容模式未開啟即“混雜模式”(又叫怪異模式,Quirks mode),標準兼容模式已開啟即“標準模式”(又叫嚴格模式,Standards mode 或者 Strict mode)。 所以前面那個測試樣例中,沒有書寫 DOCTYPE 的 HTML 文檔在所有瀏覽器中均會以混雜模式進行渲染和解析。
註:document.compatMode 在 MSDN 中的介紹:compatMode Property
究竟為何瀏覽器要制作這麽一個“開關”。微軟開發的 IE 系列瀏覽器中壽命最長的 IE6 伴隨 Windows XP 誕生。相比上一個版本 IE5.5,IE6 確實有著許多重大的改進,其中對於頁面渲染而言最大的變化就在於 IE6 支持了部分 CSS1 中的特性。例如,為一個塊級元素設定寬度及高度時,不再作用於 border 外圍,而是如 W3C 規範中所描述的僅僅是元素內容之上。這一點和 IE5.5 差別巨大。為了保證那些 90 年代後期的基於 IE6 之前版本開發的頁面能夠正常顯示,即保證瀏覽器有向後兼容性,此“開關”誕生,微軟試圖通過對 DOCTYPE 的判斷來決定瀏覽器采取何種模式工作,即是 IE6 還是 IE5.5 的問題。所以從 document.compatMode 返回的字符串值中也可以看出來,BackCompat 代表了向後兼容(即 IE5.5),CSS1Compat 代表了對 CSS1 規範的兼容(即 IE6)。由此,瀏覽器的工作模式被分為了混雜模式及標準模式。
值得註意的是,IE 的版本號一路從 6.0 升至了目前的 9.0,但升級僅限於標準模式。對於混雜模式,IE 的版本號永久的凍結在 5.5,這也算是為了向後兼容的巨大犧牲。也就是說即使我們使用著最新最高級的 IE9,但若我們不書寫 DOCTYPE 或者使用了能夠觸發混雜模式的 DOCTYPE,那我們所面對的瀏覽器仍相當於是那個十幾年前的老古董 IE5.5。而其他那些瀏覽器的混雜模式和標準模式之間卻沒有想 IE 中這麽大的差別。
註:IE6 增強的 CSS:CSS Enhancements in Internet Explorer 6
近似標準模式
近似標準模式(Almost Standards Mode)從字面意思上看與標準模式非常類似,但確實有小的差別。主要體現在對於表格單元格內垂直方向布局渲染差異。IE8 開始、Firefox、Chrome、Safari、Opera 7.5 開始,這些瀏覽器的標準模式更加嚴格的遵循了 CSS2.1 規範,故對於在目前看來不太“標準”的以前的標準模式,被賦予了“近似標準模式”的名字。但是在較早的 IE6 IE7 以及 Opera 7.5 之前版本中,瀏覽器無法嚴格遵循 CSS2.1 規範,故對於它們來說沒有這個近似標準模式,也可以理解為它們的近似標準模式就是標準模式。
到目前為止,可以看到各瀏覽器主要包含了三種模式。在 HTML5 草案中,更加明確的規定了模式的定義:
傳統名稱 | HTML5 草案名稱 | document.compatMode 返回值 |
---|---|---|
standards mode 或者 strict mode | no-quirks mode | CSS1Compat |
almost standards mode | limited-quirks mode | CSS1Compat |
quirks mode | quirks mode | BackCompat |
註:HTML5 草案關於 compatMode 的介紹:3.1.3 Resource metadata management
DOCTYPE 的選擇
工作模式簡介
瀏覽器的工作模式常被稱為“渲染模式”。實際上瀏覽器不同的工作模式不僅對渲染有影響,對代碼的解析以及腳本的行為也同樣有影響。
從更廣泛的角度來看,瀏覽器的工作模式的差異不僅體現在處理 HTML 頁面的時候,處理 XML 及一些非 WEB 內容時也有模式上的差異,但本文僅討論瀏覽器在處理 HTML 頁面時工作模式。1
註:
- 關於瀏覽器的工作模式的更多信息,請參考 Activating Browser Modes with Doctype。
工作模式的來源及變遷
不使用 DOCTYPE 一定會使 HTML 文檔處於混雜模式,然而使用了 DOCTYPE,也不一定就能夠使文檔在所有瀏覽器中均處於標準模式。DOCTYPE 本身不就是一個“開關”嗎?為何在有 DOCTYPE 的 HTML 文檔之上仍然還會出現混雜模式?這個和以下條件有關:
- 使用了本身就會使瀏覽器進入混雜模式的古老的甚至是錯誤的 DOCTYPE;
- 在 DOCTYPE 之前出現了其他內容,如註釋,甚至是 HTML 標簽。
我們先說第一個條件。HTML 歷史悠久,僅正確的 HTML 類型的 DOCTYPE 就有很多種。先看一個標準的 DOCTYPE:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
上面的 DOCTYPE 包含 6 部分:
- 字符串“<!DOCTYPE”
- 根元素通用標識符“HTML”
- 字符串“PUBLIC”
- 被引號括起來的公共標識符(publicId)“-//W3C//DTD HTML 4.01//EN”
- 被引號括起來的系統標識符(systemId)“http://www.w3.org/TR/html4/strict.dtd”
- 字符串“>”
其中根元素通用標識符、公共標識符、系統標識符均可以通過腳本調用 DOM 接口獲得,分別對應 document.doctype.name、document.doctype.publicId、document.doctype.systemId(IE6 IE7 不支持)。
不同的 DOCTYPE 之間,上面三部分可能不盡相同,有些 DOCTYPE 確實其中某部分,如何在這些紛繁的 DOCTYPE 中選擇?
其實瀏覽器在嗅探 DOCTYPE 時只考慮了上述 6 部分中的第 1、2、4、6 部分,且不區分大小寫。在各瀏覽器內核實現中,幾乎都存在一個名單用於記錄這些常見的 DOCTYPE 所對應的模式,例如 WebKit 內核中 DocTypeStrings.gperf 文件。各瀏覽器名單列表中觸發模式的不同導致了某些 DOCTYPE 出現在不同瀏覽器中觸發了不同模式的現象,如 。而對於名單之外的 DOCTYPE,瀏覽器之間處理的差異也會導致觸發不同的模式,比如可能有的瀏覽器會將名單之外的 DOCTYPE 當作混雜模式,而有的卻會一律當作標準模式。
所以我們在選用 DOCTYPE 的時候首先確定的是我們想讓 HTML 文檔使用標準模式。
如果力求最簡,則 HTML5 的 DOCTYPE 是最佳選擇:<!DOCTYPE html>,所有的主流瀏覽器均將這種只包含第 1、2、6 部分的最短的 DOCTYPE 視為標準模式。
如果力求穩妥,則較早的 HTML4.01 Strict 的 DOCTYPE 也是一種好的選擇:<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">,它在各主流瀏覽器中觸發的模式與上面的 HTML5 的完全一致。
有時候我們處於特殊情況也希望瀏覽器能夠都處於近似標準模式,則可選擇:<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">。
註:關於 Firefox 中 DTD 與瀏覽器工作模式:Mozilla‘s DOCTYPE sniffing
DOCTYPE 之前不能出現的內容
前面提到,DOCTYPE 作為一個決定瀏覽器對於 HTML 文檔采取何種模式“開關”,應出現在 HTML 文檔的最前面。但有時候處於某些原因,有的作者會在 DOCTYPE 之前防止一些內容,可能是服務端輸出的某些信息。這樣會讓瀏覽器感到極為“困惑”,它第一眼看到的不是 DOCTYPE,故可能會認為頁面沒有 DOCTYPE,則可能觸發了混雜模式。然而事實上在這一點各瀏覽器的處理並不相同。我們將 DOCTYPE 之前可能出現的這些內容分類,它們包括:
- 普通文本
- HTML 標簽
- HTML 註釋
- XML 聲明
- IE 條件註釋
對於普通文本和 HTML 標簽,各瀏覽器均進入了混雜模式,這個很好理解,都看到疑似的 HTML 文檔正文了,瀏覽器不太會往下追查 DOCTYPE 在哪裏。
對於 HTML 註釋和 XML 聲明,它們和上面的普通文本和 HTML 標簽有些差別,它們不會在頁面中展示出來,即不可視。這時,有的瀏覽器則顯得十分“智能”,非 IE 瀏覽器均會忽略它們的存在,DOCTYPE 被正確解析。但是在 IE6 中,DOCTYPE 之前的 XML 聲明會導致頁面進入混雜模式,而所有的 IE 均會使 DOCTYPE 之前出現了 HTML 註釋的頁面進入混雜模式。在 IE9 中當出現這種情況時,瀏覽器在控制臺中給出了提示:“HTML1113: 文檔模式從 IE9 標準 重新啟動到 Quirks ”,看來微軟在這一點上不打算“隨大流”,這樣做也可以敦促作者盡量避免在 DOCTYPE 之前加入其他內容。
有的作者很聰明,他既在 DOCTYPE 之前加入了他需要的內容,卻又沒有使 IE 由於這些內容而進入混雜模式。他可能會這麽寫:
- <![if !IE]><!-- some comments --><![endif]>
- <![if false]><!-- some comments --><![endif]>
又或者是
- <!--[if !IE]>some text<![endif]-->
上面這些 IE 條件註釋在非 IE 瀏覽器中,可能完全被忽略,可能被解釋為普通 HTML 註釋。但是在 IE 中它們全部消失了,因為這就是 IE 條件註釋的作用。所以這也是目前比較合適的在 DOCTYPE 之前寫點什麽又保證所有瀏覽器均為標準模式的做法,但我們仍然不推薦在 DOCTYPE 之前加入任何非空白內容。
註:關於 IE 條件註釋:About Conditional Comments
建議
通過上面的歷史回顧以及分析,我們看到了 DOCTYPE 對於目前主流瀏覽器的關鍵作用,同時也發掘了能夠觸發各瀏覽器標準模式的最佳 DOCTYPE。標準模式會使不同瀏覽器之間發生兼容性問題的風險降至最低,選擇正確的 DOCTYPE 以及保證 DOCTYPE 在 HTML 文檔中絕對開頭的位置則是使 DOCTYPE 發揮其正確作用的關鍵。
總結:在確保書寫正確的情況下,<!DOCTYPE html>會在不支持html5的瀏覽器中解析成標準模式。
操作系統版本: | Windows 7 Ultimate build 7600 |
---|---|
瀏覽器版本: | IE6 IE7 IE8 IE9 Firefox 4.0.1 Chrome 12.0.742.100 Safari 5.0.5 Opera 11.11 |
DOCTYPE 與瀏覽器模式分析