前方互換性のあるウェブサイトを書く

このページでは、新しいバージョンのブラウザーーが公開されても壊れることのないウェブサイトを記述する方法を説明します。

これはイントラネットや公衆向けでないウェブサイトで特に重要です。私たちがコードを見ることができないので、それが壊れていることを確認できないのです。これらのすべてに従うことができない場合もありますが、できる限り多くの事項に従うことで、ウェブサイトを将来にわたって有用な状態にすることの助けになります。

JavaScript

onfoo 属性でグローバル変数へアクセスする際に "window." 接頭辞を付加する

HTML の要素でイベントハンドラーのコンテンツ属性(onclickonmouseover など)が使用されているとき、それら属性でのすべての名前探索は始めに要素自身、次にフォームコントロール要素の場合はそのフォーム、そして document、さらに(あなたがグローバル変数を定義した) window の順に行われます。例えば、以下のマークアップがあるとします。

<div onclick="alert(ownerDocument)">Click me</div>

文字列をクリックすると divownerDocument をアラートで表示します。これは、var ownerDocument がグローバルスコープで宣言されている場合でも動作します。

つまり、イベントハンドラーのコンテンツ属性でグローバル変数にアクセスする場合、グローバルに宣言された関数を呼び出す場合も含めて、その関数や変数と同じ名前の新しい DOM プロパティが要素や文書に追加され、ブラウザーーがそれを実装すると、名前の衝突が発生する可能性があるということです。そうすると、突然、関数が呼び出されなくなるのです。このようなことは、 HTML5 の進化の過程で、すでにさまざまなサイトで何度も起こっています。

これを防ぐには、以下のように "window." を用いてグローバル変数にアクセスしてください。

<script>
  function localName() {
    alert('Function localName has been called');
  }
</script>
<div onclick="window.localName()">Clicking me should show an alert<div>

自身で管理しないスクリプトを連結しない

ファイルレベルで使用される ECMAScript の "use strict;" ディレクティブは、ファイル全体に適用されます。このため、厳格モードでない動作に依存するスクリプトを厳格モードのスクリプトに付加すると、スクリプトが動作しなくなります。

使用する JavaScript ライブラリーの作者に、上記のガイドラインに従っているかを確認する

お気に入りのライブラリーの開発者にも、このガイドラインに従うように提案してみてください。そうしないと、将来そのライブラリーが壊れないかどうか当てにならないからです。残念ながら、ライブラリーはこのガイドラインに違反することが多いのです。

検出

特定の機能の検出

ある機能を用いようとするとき、可能であればその機能を見つけるためにオブジェクト検出を行ってください。簡単な例として、body.style"filter" があるかのテストが真になるブラウザーは必ず Microsoft Internet Explorer であり、それゆえ例えばイベントハンドラーで window.event オブジェクトが利用できるとは考えないでください。また、ある一定の DOM 機能に対応しているブラウザーが、他の機能、特に非標準の DOM 機能にも対応しているとは考えないでください。あるいは逆に、他の機能に対応しないとも考えないでください。例えば、script 要素での onload に対応するブラウザーは onreadystatechange に対応しないと考えてはいけません。各ブラウザーの動作が収束することにより、機能は追加や削除されます。また、不具合の修正も行います。これら 3 点は過去に発生しており、また今後も発生するでしょう。

従ってある機能の存在有無と別の機能の存在有無に関連はありませんので、ある機能やオブジェクトの検出結果から別のことを予測しないでください。

UA 検出を行わない

これはある機能(ユーザエージェント (UA) 文字列内に特定の文字列が含まれていること)が別の機能の有無を暗に示すと考えられている、特に一般的な実例です。

UA 検出が必要な場合は、過去のブラウザーのバージョンにのみ行う

UA 検出に頼る必要がある場合は、特定のブラウザーの過去のバージョンに対して用いてください。始めに、未知のブラウザーおよびあなたがテストで用いているブラウザーの現行バージョンと将来のバージョン向けの既定のコードパスがあります。そして、既定のコードパスが特定のブラウザーの過去のバージョンで動作せず、またそのコードパスで機能の欠落を検出することで問題点を発見できないときは、該当する過去のバージョンのブラウザーを検出することでそのブラウザー向けのハックを追加してかまいません。

この提案において、「現行」とはテストを行った最新のバージョンのブラウザーを指します。例えば既定のコードパスが Firefox Aurora で適切に動作するが、Firefox Beta や最新の release 版では不具合があるためにコードが動作しない場合は、テストを行った Aurora の Firefox バージョン番号を「現行」とみなし、一般向けに公開されていないものであっても Beta のバージョンを「過去」のバージョンと考えてください。

異なるブラウザー向けに分離したコードパスはむやみに作成しない

関係のあるコードパスの一つがすべてのブラウザーで正しく動作するのに、わざわざオブジェクト検出や UA 検出に基づいて異なるコードを実行することは行わないでください。各ブラウザーの動作がお互い収束するように変更され、そのために代替のコードパスを設定していたあなたのサイトが正常に動作しなくなってしまう可能性があります。

テストの実施

すべての主要なエンジンについてテストを行う

コードは少なくとも Firefox、Chrome、Safari、Opera、Internet Explorer でテストを行ってください。すべての現行ブラウザーや未知のブラウザー向けに単一のコードパスを持つためこの提案に従えば、そのコードパスがすべての主要なエンジンで動作することをテストすることで、コードが将来にわたって動作する可能性がとても高くなります。

各ブラウザーがある機能を若干異なる形で実装していることがあります。主要なエンジンすべてで動作する単一のコードパスが得られた場合は、使用している機能の動作は各ブラウザー間で既に収束していることを意味します。一方、各ブラウザーの動作が完全には収束していない場合は、どのエンジンの標準動作を支持するかが判明することに関係なくコードが動作します。

ブラウザー独自の機能や接頭辞

現行あるいは将来のバージョンのブラウザーを目標にするハックを行わない

これは、現在ある不具合間の相関関係が将来の不具合間の相関関係を暗に示すと考えられている一般的な事例です。現行バージョンでは解決している不具合に関するハックを、過去のバージョンのブラウザーに対して適用することは問題ありません。ブラウザーが不具合 X を修正すると、不具合 X が存在するリリースすべてには不具合 Y もあることが確実にわかり、不具合 X が存在することを不具合 Y の回避策の適用基準として用いることができます。

この提案において、前出の UA 検出でのアドバイスのように "現行" とはあなたがテストを行った最新のバージョンのブラウザーを指します。

最新の非標準機能に依存することを避ける

接頭辞ありの機能であっても、それを使用することは危険です。仕様書の進展に伴い、ブラウザーの接頭辞ありの実装が仕様書に追随して変更されることがあります。またその機能が標準化されると、接頭辞ありのバージョンは削除されるでしょう。

接頭辞が付加された非標準の機能は、実験やフィードバックを行うためにブラウザーの開発者が提供しているものであり、一般に展開することを意味していません。これらの機能を使うことを選択した場合は、機能の変更に伴って頻繁にサイトの更新が必要であることを覚悟してください。

(標準化されていても) 広く実装されていない最新機能を用いる際は縮退処理のテストを行う

用いている機能を実装していないブラウザー でどう動作するか(そのようなブラウザーをウェブサイトの業務で日常使用していない場合は特に)、必ずテストを行ってください。

ベンダー接頭辞が付加されている機能は、過去のバグが多いバージョンを目的とする場合を除き使用しない

ベンダー接頭辞が付加されている機能は、その動作が将来変更されるかもしれません。ブラウザーがある機能を接頭辞なしで公開しても、利用可能であれば接頭辞なし版を常に使用するようにすることで、過去のリリース向けに接頭辞あり版を使用することができます。良い例として、make-it-pretty プロパティの "sometimes" 値について接頭辞あり版とは異なる動作を接頭辞なし版に実装して公開した、 CSS の -vnd 接頭辞を用いるブラウザーベンダー向けの記述を示します。

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

上記の例で、ルール中の宣言の順番は重要です。接頭辞のないものは、最後に置くことが必要です。

少なくとも一つのブラウザーがサポートするまで、接頭辞のない CSS プロパティや API は使用しない

何らかの機能について接頭辞なし版がある程度広くサポートされるまでは、その動作が突然変更されることがあります。特に、実際に接頭辞なし版に対応しているブラウザーがない機能は使用しないでください。接頭辞あり版の文法と完成版の文法が同じであると考えてはいけません。

良好なコード

> の欠落を防ぐ

検証ツールを用いることがこれを確実にする方法の一つですが、あなたのウェブサイト全体を検証しないとしてもすべての > 文字が置かれていることを確認してください。 > が欠落していると、後ろのタグの名前が前のタグの属性であると判断されて予期せぬ状況に陥る場合があります。これはしばらくの間は動作するかもしれませんが、仕様書でその属性に意味が与えられると動作しなくなります。以下の例は HTML5 に対応しないブラウザーでは動作しますが、HTML5 に対応するブラウザーでは動作しません。

<form action="http://www.example.com">
  <input type="submit" value="Submit the form"
</form>

これは input タグで > が欠落しているためです。

コード中で動作しない実験的な部分は残さない

何か行いたいことがあって CSS プロパティを使用してみたが効果がなかった場合、そのプロパティは削除してください。将来、予期しない動作が発生する可能性があります。