Writing forward-compatible websites

  • 版本網址代稱: Web_development/Writing_forward-compatible_websites
  • 版本標題: Writing forward-compatible websites
  • 版本 ID: 296613
  • 建立日期:
  • 建立者: irvinfly
  • Is current revision?
  • 回應

版本內容

這個頁面將解釋如何撰寫在新的瀏覽器版本發布時不會遭受毀損的網頁。

這對內部網路和其他非公共網站尤其重要,如果我們不能看到你的原始碼,我們將無法看到它是否已遭受毀損。底下所提到的原則可能無法全數做到,但盡可能遵守這些原則,對於你的網站在未來發展維護上有所幫助。

JavaScript

以「window.」前綴修飾所有存取於 onfoo 屬性的全域變數

當一個事件處理器內容屬性(例如:onclick, onmouseover 等等)被使用在 HTML 的元素上時,所有對於屬性內名稱的查找首先發生在元素本身,若元素為表單控制項,再來尋找元素的表單,接著是 document,最後是 window(你定義全域變數的地方)。例如,如果你有這樣的原始碼:

<div onclick="alert(ownerDocument)">點我一下</div>

在點選文字時,div 中的 ownerDocument 會被提示,即使是在全域範圍內宣告 var ownerDocument 這種情況依然會發生。

這意味著,無論你何時在事件處理器內容屬性存取了一個全域變數,包括呼叫任何全域函數,當規格中新增了和您變數或函式同名的 DOM 屬性到元素或文件之中,在瀏覽器實作之後,就會產生名稱衝突。這時你的函式將突然被停止呼叫。這種情況在 HTML5 的發展之下,多次在不同網站中發生。

為了避免這種情況,以 window 來限定全域變數的存取,例如:

<script>
  function localName() {
    alert('函式 localName 被呼叫了');
  }
</script>
<div onclick="window.localName()">按一下會跑出一個提示訊息</div>

不要直接附加非您能控制的腳本

"use strict;" 指令在 ECMAScript 裡,使用於檔案層級時適用於檔案的所有程式碼。因此,若將取決非嚴格模式行為的腳本,直接附加到要求嚴格模式的腳本會導致不正常的行為。

要求 JavaScript 函式庫的作者遵守這些規則

向您喜歡的函式庫開發者們建議他們遵循這些規範,否則您無法確保未來這些函式庫能否依舊正常運作。可惜的是函式庫往往違反這些準則。

偵測

偵測特定功能支援

如果您打算使用某些功能,盡可能使用物件偵測來偵測功能是否支援。簡單來說,不要假設只要 "filter" in body.style 測試結果為 true 的瀏覽器必定是 Microsoft Internet Explorer,進而我們一定會有 window.event 物件提供事件處理器。不要假設一個擁有特定 DOM 功能的瀏覽器一定也有另外一個 DOM 功能(特別是非標準功能);或著反過來假設一定不會支持某些功能(例如,假設在腳本元素中支援 onload 的瀏覽器一定不會支援 onreadystatechange)。隨著瀏覽器們整合他們的行為,它們會同時新增和刪除許多功能並修正錯誤。過去即是如此,未來也將會如此。

所以,在偵測特定功能時,不要接著假定「只要某個功能支援與否,另外一樣功能就一定支援與否」。

別做 UA 偵測

這就是假設一項功能存在(User Agent 中包含特定的字元)時,必定有哪些東西可用或不可用的常見實例。

如果您不得不做 UA 偵測,僅針對過去的瀏覽器版本

如果您還是得訴諸 UA(User Agent)偵測,請只針對特定瀏覽器的過去版本進行偵測。首先,對於未知的、所測試瀏覽器的目前與未來版本執行預設的程式內容。接著如果無法透過偵測,找出過去瀏覽器版本中預設程式內容中無法使用的功能,就可以透過偵測特定瀏覽器的過去版本來追加對應的修正。

在這個提案中,「目前的版本」意指您所能測試到的最新版本。如果您的預設程式內容在 Firefox Aurora 中可以正常運作,而在 Beta 和最新釋出版中存在問題而無法運作,此時您可以將您所測試中的 Firefox Aurora 版本標為「目前的版本」,將 Beta 以前的版本都視為「過去的版本」,即使它們還沒有被正式釋出給大眾。

不要為了不同的瀏覽器設計多餘的對應程式

當您所用的一部分程式內容可能在所有瀏覽器都能運作時,別隨便透過物件或 UA 偵測來決定執行不同的程式碼。瀏覽器很有可能改變它們的行為並互相整合,若您任意切出不同的替代程式,您的網站將有可能會損壞。

測試

測試所有主流引擎

至少在 Firefox、Chrome 或 Safari(因為兩者都基於相同的 WebKit 引擎)、Opera 及 Internet Explorer 測試您的程式碼。若遵循以上原則,你有一個單一的程式碼內容在目前所有的和未知的瀏覽器都測試過,在所有主要引擎都能運作下,極有可能表示您的程式碼將不會在未來被破壞。

有時不同瀏覽器對特定功能的實作不盡相同。如果你有一個單一的程式碼內容,在所有常用的引擎中都沒問題,這可能表示,你使用了各瀏覽器間已經整合的行為,或著使用了尚未整合,而程式碼無關引擎的行為標準所堅持的部份。

特定瀏覽器支援的功能和前綴

別針對目前或未來的瀏覽器版本做臨時方案

這又是一個常見的實例:假設目前瀏覽器臭蟲之間的關聯性可以代表未來也會繼續擁有如此的關聯。針對舊瀏覽器,如果您用做臨時方案的臭蟲在目前的瀏覽器已經不存在,那針對舊瀏覽器套用方案沒有問題;只針對舊瀏覽器下,一旦瀏覽器修正了 X 臭蟲,您可以肯定所有具有臭蟲 X 的瀏覽器都有臭蟲 Y,因此使用臭蟲 X 的存在來針對臭蟲 Y 的問題做解套。

跟之前 UA 偵測中的建議一樣,「目前的版本」意指您所能測試到的最新版本。

避免依賴新潮的非標準功能

就算加了前綴,使用新潮的功能依舊還是很危險:隨著規格的發展,瀏覽器前綴的實作也會遵循最新的規範而改變。一旦功能被標準化,含前綴的版本有可能會被刪除。

瀏覽器開發者提供前綴與非標準功能給您是為了實驗和意見回饋,而非讓您將這些酷玩意散佈出去。如果您選擇使用它們,請準備經常更新您的網站以趕上變化。

當使用未普遍實作(即使是標準)的新潮功能時,記得測試備援方案

要檢查在未實作所用功能的瀏覽器下瀏覽網頁會發生什麼事,尤其是您在工作時不會經常使用的瀏覽器。

除非針對過去有問題的版本,不要使用廠商前綴(Vender-prefix)功能

廠商前綴的功能可以在將來的版本中改變。然而,一旦瀏覽器已提供不帶前綴的功能,您可以在確保不帶前綴版在可用時總會被套用下,使用前綴版本針對舊版本。一個很好的例子,假設-vnd 廠商已經將不帶前綴的 make-it-pretty 屬性實作加入新出品的瀏覽器,包含一個前綴版與不含前綴版作用不同的值「sometimes」:

<style>
  .pretty-element {
    -vnd-make-it-pretty: sometimes;
    make-it-pretty: sometimes;
  }
</style>

上述規則中,聲明的順序非常重要:無前綴者一定要排在最後。

在沒有瀏覽器支援前,不要使用不含前綴的 CSS 屬性或 API

除非不帶前綴的版本得到了廣泛支持,其行為可能仍會發生意想不到的改變。特別是,當沒有瀏覽器支援不帶前綴的版本時,就不要使用不帶前綴的版本。最終的語法不盡然會和任何帶前綴的語法相同。

程式碼維護

別忘了 >

透過驗證器可以確保這個問題不會發生,但即使你的網站沒有完全驗證,你仍應確保你所有的 > 字元都有出現。少了它可能會讓接下來的 Tag 名稱被當成上一個 Tag 所屬的屬性,而造成意想不到的結果。可能一小段沒問題,但接下來因為某段文字代表一個屬性而完全被破壞。舉例來說,以下是一段在不支援 HTML5 瀏覽器下可正常運作,但卻讓支援 HTML5 的瀏覽器無法正常運作的網頁程式碼:

<form action="http://www.example.com">
  <input type="submit" value="傳送此表單"
</form>

因為在 input Tag 的最後忘了加上 >

別把失敗的實驗品留在您的網頁程式碼裡

如果您想嘗試一個 CSS 屬性或其他的酷東西,但沒有效果,請記得拿掉。不然您無法預知這東西未來會做什麼壞事。

版本來源

<p>這個頁面將解釋如何撰寫在新的瀏覽器版本發布時不會遭受毀損的網頁。</p>
<p>這對內部網路和其他非公共網站尤其重要,如果我們不能看到你的原始碼,我們將無法看到它是否已遭受毀損。底下所提到的原則可能無法全數做到,但盡可能遵守這些原則,對於你的網站在未來發展維護上有所幫助。</p>
<h2 id="JavaScript">JavaScript</h2>
<h3 id=".E4.BB.A5.E3.80.8Cwindow..E3.80.8D.E5.89.8D.E7.B6.B4.E4.BF.AE.E9.A3.BE.E6.89.80.E6.9C.89.E5.AD.98.E5.8F.96.E6.96.BC_onfoo_.E5.B1.AC.E6.80.A7.E7.9A.84.E5.85.A8.E5.9F.9F.E8.AE.8A.E6.95.B8">以「<code>window.</code>」前綴修飾所有存取於 <code>onfoo</code> 屬性的全域變數</h3>
<p>當一個事件處理器內容屬性(例如:<code>onclick</code>, <code>onmouseover</code> 等等)被使用在 HTML 的元素上時,所有對於屬性內名稱的查找首先發生在元素本身,若元素為表單控制項,再來尋找元素的表單,接著是 document,最後是 window(你定義全域變數的地方)。例如,如果你有這樣的原始碼:</p>
<pre>&lt;div onclick="alert(ownerDocument)"&gt;點我一下&lt;/div&gt;</pre>
<p>在點選文字時,div 中的 ownerDocument 會被提示,即使是在全域範圍內宣告 var ownerDocument 這種情況依然會發生。</p>
<p>這意味著,無論你何時在事件處理器內容屬性存取了一個全域變數,包括呼叫任何全域函數,當規格中新增了和您變數或函式同名的 DOM 屬性到元素或文件之中,在瀏覽器實作之後,就會產生名稱衝突。這時你的函式將突然被停止呼叫。這種情況在 HTML5 的發展之下,多次在不同網站中發生。</p>
<p>為了避免這種情況,以 window 來限定全域變數的存取,例如:</p>
<pre>&lt;script&gt;
  function localName() {
    alert('函式 localName 被呼叫了');
  }
&lt;/script&gt;
&lt;div onclick="<strong>window.</strong>localName()"&gt;按一下會跑出一個提示訊息&lt;/div&gt;
</pre>
<h3 id=".E4.B8.8D.E8.A6.81.E7.9B.B4.E6.8E.A5.E9.99.84.E5.8A.A0.E9.9D.9E.E6.82.A8.E8.83.BD.E6.8E.A7.E5.88.B6.E7.9A.84.E8.85.B3.E6.9C.AC">不要直接附加非您能控制的腳本</h3>
<p><code>"use strict;"</code> 指令在 ECMAScript 裡,使用於檔案層級時適用於檔案的所有程式碼。因此,若將取決非嚴格模式行為的腳本,直接附加到要求嚴格模式的腳本會導致不正常的行為。</p><h3 id=".E8.A6.81.E6.B1.82_JavaScript_.E5.87.BD.E5.BC.8F.E5.BA.AB.E7.9A.84.E4.BD.9C.E8.80.85.E9.81.B5.E5.AE.88.E9.80.99.E4.BA.9B.E8.A6.8F.E5.89.87">要求 JavaScript 函式庫的作者遵守這些規則</h3>
<p>向您喜歡的函式庫開發者們建議他們遵循這些規範,否則您無法確保未來這些函式庫能否依舊正常運作。可惜的是函式庫往往違反這些準則。</p>
<h2 id=".E5.81.B5.E6.B8.AC">偵測</h2>
<h3 id=".E5.81.B5.E6.B8.AC.E7.89.B9.E5.AE.9A.E5.8A.9F.E8.83.BD.E6.94.AF.E6.8F.B4">偵測特定功能支援</h3>
<p>如果您打算使用某些功能,盡可能使用物件偵測來偵測功能是否支援。簡單來說,不要假設只要 <code>"filter" in body.style</code> 測試結果為 true 的瀏覽器必定是 Microsoft Internet Explorer,進而我們一定會有 <code>window.event</code> 物件提供事件處理器。不要假設一個擁有特定 DOM 功能的瀏覽器一定也有另外一個 DOM 功能(特別是非標準功能);或著反過來假設一定不會支持某些功能(例如,假設在腳本元素中支援 <code>onload</code> 的瀏覽器一定不會支援 <code>onreadystatechange</code>)。隨著瀏覽器們整合他們的行為,它們會同時新增和刪除許多功能並修正錯誤。過去即是如此,未來也將會如此。</p>
<p>所以,在偵測特定功能時,不要接著假定「只要某個功能支援與否,另外一樣功能就一定支援與否」。</p>
<h3 id=".E5.88.A5.E5.81.9A_UA_.E5.81.B5.E6.B8.AC">別做 UA 偵測</h3>
<p>這就是假設一項功能存在(User Agent 中包含特定的字元)時,必定有哪些東西可用或不可用的常見實例。</p>
<h4 id=".E5.A6.82.E6.9E.9C.E6.82.A8.E4.B8.8D.E5.BE.97.E4.B8.8D.E5.81.9A_UA_.E5.81.B5.E6.B8.AC.EF.BC.8C.E5.83.85.E9.87.9D.E5.B0.8D.E9.81.8E.E5.8E.BB.E7.9A.84.E7.80.8F.E8.A6.BD.E5.99.A8.E7.89.88.E6.9C.AC">如果您不得不做 UA 偵測,僅針對過去的瀏覽器版本</h4>
<p>如果您還是得訴諸 UA(User Agent)偵測,請只針對特定瀏覽器的過去版本進行偵測。首先,對於未知的、所測試瀏覽器的目前與未來版本執行預設的程式內容。接著如果無法透過偵測,找出過去瀏覽器版本中預設程式內容中無法使用的功能,就可以透過偵測特定瀏覽器的過去版本來追加對應的修正。</p>
<p>在這個提案中,「目前的版本」意指您所能測試到的最新版本。如果您的預設程式內容在 Firefox Aurora 中可以正常運作,而在 Beta 和最新釋出版中存在問題而無法運作,此時您可以將您所測試中的 Firefox Aurora 版本標為「目前的版本」,將 Beta 以前的版本都視為「過去的版本」,即使它們還沒有被正式釋出給大眾。</p>
<h3 id=".E4.B8.8D.E8.A6.81.E7.82.BA.E4.BA.86.E4.B8.8D.E5.90.8C.E7.9A.84.E7.80.8F.E8.A6.BD.E5.99.A8.E8.A8.AD.E8.A8.88.E5.A4.9A.E9.A4.98.E7.9A.84.E5.B0.8D.E6.87.89.E7.A8.8B.E5.BC.8F">不要為了不同的瀏覽器設計多餘的對應程式</h3>
<p>當您所用的一部分程式內容可能在所有瀏覽器都能運作時,別隨便透過物件或 UA 偵測來決定執行不同的程式碼。瀏覽器很有可能改變它們的行為並互相整合,若您任意切出不同的替代程式,您的網站將有可能會損壞。</p>
<h2 id=".E6.B8.AC.E8.A9.A6">測試</h2>
<h3 id=".E6.B8.AC.E8.A9.A6.E6.89.80.E6.9C.89.E4.B8.BB.E6.B5.81.E5.BC.95.E6.93.8E">測試所有主流引擎</h3>
<p>至少在 Firefox、Chrome 或 Safari(因為兩者都基於相同的 WebKit 引擎)、Opera 及 Internet Explorer 測試您的程式碼。若遵循以上原則,你有一個單一的程式碼內容在目前所有的和未知的瀏覽器都測試過,在所有主要引擎都能運作下,極有可能表示您的程式碼將不會在未來被破壞。</p>
<p>有時不同瀏覽器對特定功能的實作不盡相同。如果你有一個單一的程式碼內容,在所有常用的引擎中都沒問題,這可能表示,你使用了各瀏覽器間已經整合的行為,或著使用了尚未整合,而程式碼無關引擎的行為標準所堅持的部份。</p>
<h2 id=".E7.89.B9.E5.AE.9A.E7.80.8F.E8.A6.BD.E5.99.A8.E6.94.AF.E6.8F.B4.E7.9A.84.E5.8A.9F.E8.83.BD.E5.92.8C.E5.89.8D.E7.B6.B4">特定瀏覽器支援的功能和前綴</h2>
<h3 id=".E5.88.A5.E9.87.9D.E5.B0.8D.E7.9B.AE.E5.89.8D.E6.88.96.E6.9C.AA.E4.BE.86.E7.9A.84.E7.80.8F.E8.A6.BD.E5.99.A8.E7.89.88.E6.9C.AC.E5.81.9A.E8.87.A8.E6.99.82.E6.96.B9.E6.A1.88">別針對目前或未來的瀏覽器版本做臨時方案</h3>
<p>這又是一個常見的實例:假設目前瀏覽器臭蟲之間的關聯性可以代表未來也會繼續擁有如此的關聯。針對舊瀏覽器,如果您用做臨時方案的臭蟲在目前的瀏覽器已經不存在,那針對舊瀏覽器套用方案沒有問題;只針對舊瀏覽器下,一旦瀏覽器修正了 X 臭蟲,您可以肯定所有具有臭蟲 X 的瀏覽器都有臭蟲 Y,因此使用臭蟲 X 的存在來針對臭蟲 Y 的問題做解套。</p>
<p>跟之前 UA 偵測中的建議一樣,「目前的版本」意指您所能測試到的最新版本。</p>
<h3 id=".E9.81.BF.E5.85.8D.E4.BE.9D.E8.B3.B4.E6.96.B0.E6.BD.AE.E7.9A.84.E9.9D.9E.E6.A8.99.E6.BA.96.E5.8A.9F.E8.83.BD">避免依賴新潮的非標準功能</h3>
<p>就算加了前綴,使用新潮的功能依舊還是很危險:隨著規格的發展,瀏覽器前綴的實作也會遵循最新的規範而改變。一旦功能被標準化,含前綴的版本有可能會被刪除。</p>
<p>瀏覽器開發者提供前綴與非標準功能給您是為了實驗和意見回饋,而非讓您將這些酷玩意散佈出去。如果您選擇使用它們,請準備經常更新您的網站以趕上變化。</p>
<h3 id=".E7.95.B6.E4.BD.BF.E7.94.A8.E6.9C.AA.E6.99.AE.E9.81.8D.E5.AF.A6.E4.BD.9C.EF.BC.88.E5.8D.B3.E4.BD.BF.E6.98.AF.E6.A8.99.E6.BA.96.EF.BC.89.E7.9A.84.E6.96.B0.E6.BD.AE.E5.8A.9F.E8.83.BD.E6.99.82.EF.BC.8C.E8.A8.98.E5.BE.97.E6.B8.AC.E8.A9.A6.E5.82.99.E6.8F.B4.E6.96.B9.E6.A1.88">當使用未普遍實作(即使是標準)的新潮功能時,記得測試備援方案</h3>
<p>要檢查在未實作所用功能的瀏覽器下瀏覽網頁會發生什麼事,尤其是您在工作時不會經常使用的瀏覽器。</p>
<h3 id=".E9.99.A4.E9.9D.9E.E9.87.9D.E5.B0.8D.E9.81.8E.E5.8E.BB.E6.9C.89.E5.95.8F.E9.A1.8C.E7.9A.84.E7.89.88.E6.9C.AC.EF.BC.8C.E4.B8.8D.E8.A6.81.E4.BD.BF.E7.94.A8.E5.BB.A0.E5.95.86.E5.89.8D.E7.B6.B4.EF.BC.88Vender-prefix.EF.BC.89.E5.8A.9F.E8.83.BD">除非針對過去有問題的版本,不要使用廠商前綴(Vender-prefix)功能</h3>
<p>廠商前綴的功能可以在將來的版本中改變。然而,一旦瀏覽器已提供不帶前綴的功能,您可以在確保不帶前綴版在可用時總會被套用下,使用前綴版本針對舊版本。一個很好的例子,假設<code>-vnd</code> 廠商已經將不帶前綴的 <code>make-it-pretty</code> 屬性實作加入新出品的瀏覽器,包含一個前綴版與不含前綴版作用不同的值「<code>sometimes</code>」:</p>
<pre>&lt;style&gt;
  .pretty-element {
    -vnd-make-it-pretty: sometimes;
    make-it-pretty: sometimes;
  }
&lt;/style&gt;
</pre>
<p>上述規則中,聲明的順序非常重要:無前綴者一定要排在最後。</p>
<h3 id=".E5.9C.A8.E6.B2.92.E6.9C.89.E7.80.8F.E8.A6.BD.E5.99.A8.E6.94.AF.E6.8F.B4.E5.89.8D.EF.BC.8C.E4.B8.8D.E8.A6.81.E4.BD.BF.E7.94.A8.E4.B8.8D.E5.90.AB.E5.89.8D.E7.B6.B4.E7.9A.84_CSS_.E5.B1.AC.E6.80.A7.E6.88.96_API">在沒有瀏覽器支援前,不要使用不含前綴的 CSS 屬性或 API</h3>
<p>除非不帶前綴的版本得到了廣泛支持,其行為可能仍會發生意想不到的改變。特別是,當沒有瀏覽器支援不帶前綴的版本時,就不要使用不帶前綴的版本。最終的語法不盡然會和任何帶前綴的語法相同。</p>
<h2 id=".E7.A8.8B.E5.BC.8F.E7.A2.BC.E7.B6.AD.E8.AD.B7">程式碼維護</h2>
<h3 id=".E5.88.A5.E5.BF.98.E4.BA.86_>">別忘了 <code>&gt;</code></h3>
<p>透過驗證器可以確保這個問題不會發生,但即使你的網站沒有完全驗證,你仍應確保你所有的 &gt; 字元都有出現。少了它可能會讓接下來的 Tag 名稱被當成上一個 Tag 所屬的屬性,而造成意想不到的結果。可能一小段沒問題,但接下來因為某段文字代表一個屬性而完全被破壞。舉例來說,以下是一段在不支援 HTML5 瀏覽器下可正常運作,但卻讓支援 HTML5 的瀏覽器無法正常運作的網頁程式碼:</p>
<pre class="brush: html">&lt;form action="http://www.example.com"&gt;
  &lt;input type="submit" value="傳送此表單"
&lt;/form&gt;
</pre>
<p>因為在 <code>input</code> Tag 的最後忘了加上 <code>&gt;</code>。</p>
<h3 id=".E5.88.A5.E6.8A.8A.E5.A4.B1.E6.95.97.E7.9A.84.E5.AF.A6.E9.A9.97.E5.93.81.E7.95.99.E5.9C.A8.E6.82.A8.E7.9A.84.E7.B6.B2.E9.A0.81.E7.A8.8B.E5.BC.8F.E7.A2.BC.E8.A3.A1">別把失敗的實驗品留在您的網頁程式碼裡</h3>
<p>如果您想嘗試一個 CSS 屬性或其他的酷東西,但沒有效果,請記得拿掉。不然您無法預知這東西未來會做什麼壞事。</p>
Revert to this revision