The Will Will Web

記載著 Will 在網路世界的學習心得與技術分享

網站建置不是件簡單的事 @ 跨瀏覽器網頁設計密技 (2)

設計跨瀏覽器網頁經常要在不同瀏覽器版本之間進行微調,調整的地方不一定只有 CSS 樣式,我們有時候會為了避開一些瀏覽器在特定版本下的 JavaScript 臭蟲,也會需要針對不同的瀏覽器版本撰寫不同的程式碼來因應,在這篇文章裡我會介紹各瀏覽器 CSS Hack 技巧以及 IE 條件式註解,幫助各位更輕易的設計出跨瀏覽器網頁。

(1) 各瀏覽器 CSS Hacks 技巧

撰寫 CSS 樣式時必須瞭解到樣式套用順序的觀念,也就是越晚定義的樣式,優先權越高!

舉個例子來說,如下 .page 這個樣式重複定義了 width 屬性,因此真正被套用的 width 屬性應該是 95% 這個值才對。

.page {
    width: 90%;
    width: 95%;
    margin-left: auto;
    margin-right: auto;
}

不只單一則樣式裡的屬性可以重複定義,連整條樣式也能夠重複定義,一樣也是比較晚定義的樣式會蓋掉先前定義的樣式,如下範例 .page 這個樣式最後生效的 width 屬性會是 100% 才對。

.page {
    width: 90%;
    margin-left: auto;
    margin-right: auto;
}
.page {
    width: 100%;
}

無論是 IE、Firefox、Safari、Opera 或 Chrome 瀏覽器,在解析一份 CSS 樣式表時多少都會有些不一樣的行為,但是都有一個共通的特性,那就是當瀏覽器無法解析其中一段 CSS 樣式或屬性時,就會自動忽略這一段的樣式,進而解析下一段樣式。所謂的 CSS Hack 技巧,講的就是找出不同瀏覽器版本之間不同的 CSS 解析規則,好讓網頁設計師所定義的 CSS 樣式可以讓該樣式在特定瀏覽器版本下無法辨識,但另一個版本的瀏覽器可以辨識的技巧,也是因為這類「小技巧」十分神奇,所以才被稱為 “Hack”。

備註:筆者倒覺得 CSS Hack 的神奇之處不是他的語法特別,而是為什麼有人可以找的到這麼多怪異 CSS 語法,國外還有些人會寫許多測試案例設法找出各種可能的 CSS Hacks,真讓人佩服。

CSS Hack 主要分成兩種類型,分別描述如下:

  • 樣式屬性的密技 ( Attribute Hacks )
  • 選取器的密技 ( Selector Hacks )

樣式屬性的密技 ( Attribute Hacks )

顧名思義,這種類型的 Hacks 可以用在樣式屬性上,以下是相關 Hacks 語法的整理:

  • 只有 IE6 才能解析的屬性,在屬性名稱之前加上一個底線 ( _ )
  • #style1 { _color: blue }
  • 只有 IE6, IE7 才能解析的屬性,在屬性名稱前面加上星號 ( * ) 或井號 ( # )
  • #style1 { *color: blue; }
    #style1 { #color: blue; }
  • 只有 IE7, IE8, IE9 才能解析的屬性,在屬性名稱後面加上一個空白與一個註解 ( /**/ )
  • #style1 { color /**/: blue }
  • 只有 IE6, IE7, IE8, IE9 才能解析的屬性,在屬性值最後加上一個 \9 字串
  • #style1 { color: blue\9; }
  • 只有 IE8, IE9 才能解析的屬性,在屬性值最後加上一個 \0/ 字串
  • #style1 {color: blue\0/;} 

有了這些特殊的 CSS Hacks,網頁設計師便可以利用這些特性來設計針對特定瀏覽器的 CSS 問題來解決不同版本之前的相容性問題。舉個例子來說,如果你想針對不同的 IE 版本給與不同的顏色顯示文字,你就可以將 CSS 寫成以下這樣:

.page {
  color: black;       /* 所有瀏覽器    */
  color /**/: green;  /* IE7, IE8, IE9 */
  *color: blue;       /* IE6, IE7      */
  _color: red;        /* IE6           */
 }

請注意:上述樣式的順序性非常重要,因為比較晚定義的樣式會取代較早定義的樣式,所以你必須把最多瀏覽器版本看的懂的樣式寫在比較上面,這樣才能達到 CSS Hack 的效果。

由於我們有 4 個不同的 color 屬性定義,不同瀏覽器版本套用這些屬性時就會有不同的結果出現:

  • 所有非 IE 瀏覽器 (包括 IE10 瀏覽器)
    會先套用正常的 color: black; 屬性,套用值為 black
    剩下三行的定義非 IE 瀏覽器看不懂,所以會自動跳過
  • IE8 , IE9
    會先套用正常的 color: black; 屬性,套用值為 black
    第 2 行的 color /**/: green; 它也看的懂,所以套用值會變成 green
    剩下兩行 IE8, IE9 都看不懂,所以會自動跳過
  • IE7
    會先套用正常的 color: black; 屬性,套用值為 black
    第 2 行的 color /**/: green; 它也看的懂,所以套用值會變成 green
    第 3 行的 *color: blue; 它也看的懂,所以套用值會變成 blue
    最後一行 IE7 看不懂,所以會自動跳過
  • IE6
  • 會先套用正常的 color: black; 屬性,套用值為 black
    第 2 行的 color /**/: green; 它看不懂,所以跳過
    第 3 行的 *color: blue; 它也看的懂,所以套用值會變成 blue
    第 4 行的 _color: blue; 它也看的懂,所以套用值會變成 red

就是這樣的一個計算邏輯,可以讓樣式表在不同 IE 版本之間進行差異化設計。當然,除了這個範例之外,你還能依據上述各種 CSS Attribute Hacks 的語法與支援的 IE 版本自行變化組合。

※ 注意事項 ※
在網路上你會找到許多 Attribute Hacks 的文章,不過在我逐一驗證下發現,大部分描述 IE8 Hacks 語法都是錯誤的,我所查過所有號稱支援 IE8 and below 的 Attribute Hacks 其實都能被 IE9 正確讀取,也代表著你沒辦法透過 Attribute Hacks 區分 IE8 與 IE9,如果你的目的是要能設計出 IE8 與 IE9 有不同樣式的話,那麼你必須使用接下來討論的 Selector Hacks 或是本文最後會提到的 IE 條件式註解

 

選取器類型的密技 ( Selector Hacks )

選取器類型的 Hacks 就是找出不同瀏覽器之間解析 選取器 (Selector) 的瑕疵來判別不同的瀏覽器或版本,這種類型的 Hacks 不止 IE 專屬,你還可以將這種類型的 Hacks 運用在其他的瀏覽器裡,而這裡的語法也比 CSS Attribute Hacks 的語法更奇怪。

  • 只有 IE6 才能解析的選取器,在選取器名稱之前加上 * html
  • * html #style1 { color: red }
  • 只有 IE7 才能解析的選取器,在選取器名稱之前加上 *+html*:first-child+html
  • *+html #style1 { color: red }
    
    *:first-child+html #style1 { color: red }
  • 只有 IE8 才能解析的選取器,在整個 CSS 樣式規則前後用以下 @media \0screen 定義
  • @media \0screen {
      .style1 {color: red;}
    }
  • 只有 IE9 才能解析的選取器搭配屬性 Hack,在選取器名稱之前加上 :root 與屬性值後面加上 \9
  • :root #style1 {color: #FF0000\9;}
  • 只有 IE7, Firefox, Safari, Opera 才能解析的選取器,在選取器名稱之前加 html>body
  • html>body #style1 { color: red }
  • 只有 IE8, IE9, Firefox, Safari, Opera 才能解析的選取器,在選取器名稱之前加 html>/**/body
  • html>/**/body #style1 { color: red }
  • 只有 Opera 9.27 以下與 Safari 2 才能解析的選取器,在選取器名稱之前加 html:first-child
  • html:first-child #style1 { color: red }
  • 只有 Safari 2 ~ 3 才能解析的選取器,在選取器名稱之前加 html[xmlns*=""] body:last-child
  • html[xmlns*=""] body:last-child #style1 { color: red }
  • 只有 Safari 3+, Chrome 1+, Opera9+, Firefox 3.5+才能解析的選取器,在選取器名稱之前加 body:nth-of-type(1)body:first-of-type
  • body:nth-of-type(1) #siete { color: red }
    
    body:first-of-type #ocho { color: red }
  • 只有 Safari 3+, Chrome 1+ 才能解析的選取器,在整個 CSS 樣式規則前後用以下 @media 定義
  • @media screen and (-webkit-min-device-pixel-ratio:0) {
     #style1 { color: red  }
    }
  • 只有 iPhone 與 Mobile Webkit 的瀏覽器才能解析的選取器
  • @media screen and (max-device-width: 480px) {
     #style1 { color: red  }
    }
  • 只有 Safari 2 ~ 3.1 才能解析的選取器,在選取器名稱之前加 html[xmlns*=""]:root
  • html[xmlns*=""]:root #style1  { color: red  }
  • 只有 IE6, IE7, IE8 才無法解析的選取器,在選取器名稱之前加 :root *>
  • :root *> #style1 { color: red  }
  • 只有 Firefox 1.0+ 才看的懂得選取器,在選取器名稱後面加上 , x:-moz-any-link
  • #veinticuatro,  x:-moz-any-link { color: red }
  • 只有 Firefox 3.0+ 才看的懂得選取器,在選取器名稱後面加上 ,  x:-moz-any-link, x:default
  • #veinticuatro,  x:-moz-any-link, x:default { color: red }
  • 只有 Firefox 3.5+ 才看的懂得選取器,在選取器名稱前面加 body:not(:-moz-handler-blocked)
  • body:not(:-moz-handler-blocked) #veinticuatro { color: red }

說實在的,這些 Hacks 語法真有點複雜,但當網頁設計師被客戶要求克服各種瀏覽器版本的樣式差異時,也不得不學會這些密技才能滿足客戶要求。但話說回來,一般客戶頂多就是要求網頁要能夠相容於 IE6 ~ IE9 這些瀏覽器而已,依照筆者多年來的網頁建置經驗,其他像是 Firefox, Safari, Opera, Chrome 根本不會有相容性的問題,如果有客戶要求你要針對 Firefox, Safari, Opera, Chrome 進行相容性網頁設計,你大可要求客戶升級到最新版,不然還真做不下去了。 ^_^

 

相關連結:

 

(2) IE 條件式註解

由於 CSS Hack 技巧是運用許多「非標準」的寫法來達到開發相容性網頁的手段,如果你採用 W3C 提供的 CSS 驗證服務來驗證這些含有 CSS Hacks 的樣式表,將會得到許多格式的錯誤,不過即便如此,這些 CSS Hacks 目前還是能夠正常的運作,只能說它有點不安全而已。

所謂「不安全」是因為這類語法是透過各瀏覽器解析 CSS 規則的瑕疵來達成目的的,例如只有 IE6 才能解析的屬性是在屬性名稱之前加上一個底線 ( _ ) 即可達到,這樣的語法「目前」在 IE7, IE8, IE9 以及其他瀏覽器都看不懂,但如果哪天 IE11 或 Chrome 20 又不小心可以解析這類特殊的 CSS Hacks 怎麼辦?這就是一個對未來不確定的風險,所以也有人提到應該盡量減少使用 CSS Hacks 的語法,但如果不用 CSS Hacks 語法,那我們要如何做到跨瀏覽器版本的相容性呢?

沒錯,這就是這一節的重點:使用 IE 條件式註解

IE 條件式註解 (Conditional Comments) 是 Internet Explorer 獨有的相容性技術,在其他非 IE 的瀏覽器中並沒有類似的設計,條件式註解並非 W3C 標準,但這樣的技術的確非常實用,針對不同的 IE 版本可以非常安全的套用不同的網頁內容,又不會影響其他非 IE 瀏覽器的網頁呈現。

有別於 CSS Hack 技巧,IE 條件式註解是寫在 HTML 裡面的,接著我們來看一下條件式註解的語法,以下這段條件式註解寫著一個判斷條件式 [if IE 8] 所代表的意思是:當 IE 版本為 8 時,顯示在註解內的那段綠色文字,若使用者使用的 IE 版本為 9 或其他非 IE8 的版本,則會將這一整段視為註解文字。

<!--[if IE 8]>
<p>Welcome to Internet Explorer 8.</p>
<![endif]-->

請注意:上述紅字部分,你不能在這幾個字元之間加上空白字元,要完全一樣才可以生效。

我們再寫一個比較完整的例子,我們可以用條件式註解來選擇不同的 HTML 內容來輸出:

真正輸出的時候只會輸出判斷條件為「真」的內容,如下圖示:

同樣的網頁,若用非 IE 瀏覽器開啟(在此我用 Google Chrome 開啟)其畫面如下,所有內容都被瀏覽器視為註解了!

這時你可能會想說,如果條件式註解內的內容也想讓其他非 IE 瀏覽器顯示有辦法嗎?答案是肯定的!

條件式註解還有另一種特殊的表示法,就是把條件式註解變成條件式標籤,如下範例:

<![if IE 8]>
<p>此內容只會在 IE8 與其他非 IE 瀏覽器顯示</p>
<![endif]>

請注意:這段「條件式標籤」跟之前的版本差別僅在於把 -- 字元給刪除,這段條件式標籤在其他非 IE 瀏覽器下,由於看不懂這種表示法,這些瀏覽器會把他當成一個看不懂的標籤,因為上下兩段都是 < 開頭與 > 結尾,而瀏覽器只要看到不認識的 HTML 標籤就會自動忽略,也因此綠色的部分就會正常顯示在瀏覽器上,因此,條件式標籤也是可以用在跨瀏覽器的應用上。

在條件式註解的判斷式中還可以寫許多複雜的判斷條件,以下是判斷條件的語法:

  • !     NOT 邏輯運算  [ 注意: 判斷 !IE 時應該要用條件式標籤才對 ]
  • <![if !IE]>
    <p>任何非 Internet Explorer 瀏覽器</p>
    <![endif]>
  • lt    小於
  • <!--[if lt IE 9]>
    <p>任何小於 IE9 的瀏覽器,例如: IE8, IE7, IE6 </p>
    <![endif]-->
  • lte   小於等於
  • <!--[if lte IE 8]>
    <p>任何小於等於 IE8 的瀏覽器,例如: IE8, IE7, IE6 </p>
    <![endif]-->
  • gt    大於
  • <!--[if gt IE 7]>
    <p>任何大於 IE7 的瀏覽器,例如: IE8, IE9 </p>
    <![endif]-->
  • gte   大於等於
  • <!--[if gte IE 7]>
    <p>任何大於等於 IE7 的瀏覽器,例如: IE7, IE8, IE9 </p>
    <![endif]-->
  • ( )   子條件式
  • <!--[if !(IE 8)]>
    <p>任何非 IE8 的 IE 瀏覽器,例如: IE6, IE7, IE9 </p>
    <![endif]-->
  • &     AND 邏輯運算
  • <!--[if (gt IE 6) & (lt IE 9)]>
    <p>僅大於 IE6 以及小於 IE9 的瀏覽器,例如: IE7, IE8</p>
    <![endif]—>
  • |     OR 邏輯運算
  • <!--[if (IE 7) | (IE 8)]>
    <p>僅 IE7 與 IE8 瀏覽器</p>
    <![endif]-->
  • true   永遠為的邏輯運算,等同於 [if IE] 判斷式
  • <!--[if true]>
    <p>在 IE 瀏覽器中永遠顯示</p>
    <![endif]—>
  • false  永遠為的邏輯運算,等同於 [if !IE] 判斷式
  • <!--[if false]>
    <p>在 IE 瀏覽器中永不遠顯示</p>
    <![endif]-->

活用條件式註解條件式標籤的技巧有很多,你不只可能那來顯示不同的 HTML 內容,還可以用來載入不同的 CSS 檔,這種撰寫技巧又被稱為條件式樣式表 (Conditional stylesheets),如下範例所示,你可以透過條件式註解來完成條件式樣式表的載入,如此一來就可以避免在 CSS 樣式表內使用「不安全」的 CSS Hacks 語法:

<!--[if lte IE 8]><link rel="stylesheet" href="lte-ie-8.css"><![endif]-->
<!--[if lte IE 7]><link rel="stylesheet" href="lte-ie-7.css"><![endif]-->
<!--[if lte IE 6]><link rel="stylesheet" href="lte-ie-6.css"><![endif]-->

還有一種撰寫技巧被稱為條件式類別名稱 (Conditional classnames),這技巧經常用在 HTML5 的網頁裡,我們為了做到不支援 HTML5 的舊版瀏覽器也能讀取正確的樣式,有時會採用這種方式搭配 CSS 撰寫,達到不同 IE 版本也能套用不同樣式的目的。例如在 InitializrHTML5 Boilerplate 這些 HTML5 範本產生器裡都能看到使用條件式類別名稱的技巧。

<!doctype html>
<!--[if lt IE 7 ]> <html lang="en" class="no-js ie6"> <![endif]-->
<!--[if IE 7 ]>    <html lang="en" class="no-js ie7"> <![endif]-->
<!--[if IE 8 ]>    <html lang="en" class="no-js ie8"> <![endif]-->
<!--[if IE 9 ]>    <html lang="en" class="no-js ie9"> <![endif]-->
<!--[if (gt IE 9)|!(IE)]><!--> <html lang="en" class="no-js"> <!--<![endif]-->
<head>

透過條件式類別名稱的技巧,你的 CSS 內容就可以寫成以下這樣,也可以有效避免使用 CSS Hacks:

.style1 { color: black; }
.ie8 .style1 { color: green; } /* IE8 */
.ie7 .style1 { color: blue; }  /* IE7 */
.ie6 .style1 { color: red; }   /* IE6 */

 

※ 注意事項 ※
微軟已經決定 Internet Explorer 10 之後的版本將移除條件式註解功能,因此下一代 IE 瀏覽器在解析條件式註解時將會與現有其他瀏覽器一樣,針對條件式註解條件式標籤裡的條件式都會自動忽略,因此未來該功能只能用於 IE9 以下的瀏覽器裡。相關資訊請參考:
Obsolete features in Internet Explorer 10

 

相關連結:

 

總結

為了達到同一份 HTML 可以精準的呈現在不同的瀏覽器與版本之間的確不是件簡單的事,這次講的 CSS Hacks 與 IE 條件式註解都是在網頁設計的領域中非常實用的技巧,若能夠對這兩種開發技巧多加熟悉,相信能對日常網頁設計工作帶來許多幫助。