次の方法で共有


HTML5 によるアプリケーションのビルド

取り残されるブラウザーをなくす: HTML5 の導入戦略

Brandon Satrom

コード サンプルのダウンロード

HTML5 は魅力にあふれています。新しいマークアップ、CSS 機能、および JavaScript API を利用すれば、Web での可能性が飛躍的に高まります。ブラウザー ベンダーの中で着実に先手を打つことができるだけでなく、魅力的な機能を毎日のように増やしていくことができます。夜を徹して行ってきたビルドから、開発チャネル リリースを経て、通常のプラットフォーム プレビューに至るまで、ブラウザーの開発は迅速に進んできており、世界中の Web 開発者がこのわくわくする活動に参加しています。

しかし、開発コミュニティやブラウザー コミュニティが HTML5 の機運を熱狂的に盛り上げているとしても、Web 利用者のほとんどは、コミュニティのメンバーが使用しているような新しいブラウザーや新しいバージョンを使用しているわけではありません。大規模開発会社や多数のユーザーを抱える企業の Web 開発者は、おそらくこのことを既にご存じでしょう。Web 経由でサービスを提供している中小企業や新興企業の方も、自社のサイトができる限り多くのブラウザーやブラウザー バージョンに対応していることを確認するために、おそらく長い時間を費やしていると思います。

このような現実を踏まえると、「自身が HTML5 を使用する準備が整っている」かどうかではなく、「ユーザーが HTML5 を利用する準備を整えている」どうかに注目した方が、HTML5 について考えやすくなります。たとえば、新しいセマンティック タグ (<header>、<article> など) を使用したページを作成し、このページに新しい CSS 機能 (border-radius、box-shadow など) と、HTML5 ロゴをページに描画する <canvas> 要素を追加するとします。

Internet Explorer 9、Firefox 4 以降、Google Chrome などの最新のブラウザーでは、このページは図 1 のようにレンダリングされます。しかし、このページを Internet Explorer 8 以前のブラウザーに読み込もうとすると、図 2 のように、完全に崩れたページになります。

Semantic Page with Styles and an HTML5 <canvas> Element, Rendered in Internet Explorer 9
図 1 Internet Explorer 9 で表示したセマンティック ページ (スタイルと HTML5 の <canvas> 要素が有効)

The Same Semantic Page, Rendered in Internet Explorer 8 with No Styles and No <canvas>
図 2 Internet Explorer 8 で表示した同じセマンティック ページ (スタイルと HTML5 の <canvas> 要素が無効)

皆さんが HTML5 のすばらしい機能を検討しながらも、このようなことがあるならもうしばらく導入を待つのが最善だと考えたとしても、責められません。HTML5 を利用する準備が整っているユーザーはそれほどいないと結論付けるのは簡単です。

2022 年まで HTML5 の検討を保留しようと決断する前に、ぜひ今回の記事を最後までお読みください。今月の目標は、図 2 のような不適切な機能低下を引き起こさずに HTML5 テクノロジを今すぐ導入するための、実用的な戦略を紹介することです。ここでは、次のテーマを取り上げます。

  1. 機能検出とユーザー エージェント (UA) スニッフィング
  2. JavaScript によるポリフィル
  3. グレースフル デグラデーション

この 3 つのテーマは、幅広いブラウザーに対応した Web サイトを構築するうえで必要な知識のほとんどをカバーしています。この記事を読み終えるころには、遅れずに堂々と HTML5 テクノロジを導入する堅固な戦略を理解できることでしょう。また、新しいブラウザー向けの "プログレッシブ エンハンスメント" と同時に、他のブラウザー向けの "グレースフル デグラデーション" を行うためのツールも利用できるようになります。

機能検出の重要性

どのブラウザーを使っても安定した一貫性のあるエクスペリエンスを確保するために、多くの場合、開発者はユーザーのブラウザーに関する情報を取得する必要があります。従来は、次のような JavaScript でこの情報を確認するのが一般的でした。

var userAgent = navigator.userAgent;
 
if (userAgent.indexOf('MSIE') >= 0) {
  console.log("Hello, IE user");
} else if (userAgent.indexOf('Firefox') >= 0) {
  console.log("Hello, Firefox user");
} else if (userAgent.indexOf('Chrome') >= 0) {
  console.log("Hello, Chrome user");
}

この手法は UA スニッフィングと呼ばれ、ページを要求しているブラウザーの特定に幅広く使用されています。これは、ユーザーが使用しているブラウザー (たとえば Internet Explorer 7) がわかれば、有効または無効にするサイトの機能を実行時に決定できるという考え方です。UA スニッフィングは、ブラウザーに「あなたはだれですか」と質問するのに等しい手法です (UA スニッフィングとその他の検出手法の詳細については、bit.ly/mlgHHY (英語) を参照してください)。

この手法の問題は、ブラウザーから返される情報が正しくない場合があることです。UA 文字列はユーザーが構成でき、対象のブラウザーについて必ずしも 100% 正確な情報を示しているわけではありません。さらに、この手法が普及すると共に、使用しているブラウザーについて間違った推測をするようスクリプトを欺くことでブラウザー検出を回避する手段として、多くのブラウザー ベンダーが自社の製品の UA 文字列にコンテンツを追加しました。ブラウザーによっては、ユーザーが数回クリックするだけで UA 文字列を変更できる機能さえ備えているものがあります。

ただし、UA スニッフィングは、ユーザーのブラウザーやバージョンを知ることが本来の目的ではありませんでした。もちろん、サイトが推奨しないブラウザーを使用しているユーザーに、別のブラウザーをダウンロードするように依頼する手段を提供することでもありません (実際にこの手法を使用するブラウザーがあるとしても、UA スニッフィング本来の目的ではありません)。使用するブラウザーを選択するのはユーザーです。開発者の役割は、最も信頼性が高く、一貫性のあるエクスペリエンスを提供することです。決して、ブラウザーの好みについての意見をユーザーに押し付けることではありません。どのようなときも UA スニッフィングの目的は、ユーザーが使用しているブラウザーで利用できる機能を正確に把握することです。ブラウザー自体に関する知識は、この情報を得るための手段にすぎません。

現在は、UA スニッフィングに代わる手法がいくつかあります。中でも (jQuery と Modernizr の効果もあって) 最近人気が出てきているのが、オブジェクト検出や機能検出と呼ばれる手法です。どちらもほぼ同じ意味ですが、ここでは "機能検出" で統一します。

機能検出の目的は、ユーザーが現在使用しているブラウザーが特定の機能をサポートしているかどうかを判断することです。UA スニッフィングがブラウザーに「あなたはだれですか」と質問するようなものだとすれば、機能検出は、ブラウザーに「何ができますか」と質問するようなものだと言えます。こちらの質問の方が直接的で、条件付きの機能をより確実にユーザーに提供できます。機能検出スクリプトを正しく実装すれば、機能のサポートについてユーザーやブラウザーが偽装したり不正確に通知したりしにくくなります。

手動の機能検出

では、UA スニッフィングの例と比べて、機能検出とはどのようなものでしょう。この疑問に答えるために、先ほどの HTML5 ページ (図 1 参照) を Internet Explorer 9 ではなく Internet Explorer 8 で表示する際に発生する問題の解決方法を説明しましょう。このページのマークアップを図 3 に示します。

図 3 HTML5 の新しいセマンティック マークアップを使用したページ

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="utf-8" />
  <title>My Awesome Site</title>
  <style>
    body { font-size: 16px; font-family: arial,helvetica,clean,sans-serif;  }
    header h1 { font-size: 36px; margin-bottom: 25px; }
    article 
    {
      background: lightblue;
      margin-bottom: 10px;
      padding: 5px;
      border-radius: 10px;
      box-shadow: 4px 4px 10px rgba(0, 0, 0, 0.5);
    }
    article h1 { font-size: 12px; }
  </style>
</head>
<body>
  <header><h1>My Awesome Site</h1></header>
  <article>
    <header><h1>An Article</h1></header>
    <p>Isn't this awesome?</p>
  </article>
  <canvas width="250" height="500"></canvas>
</body>
<script src="../js/html5CanvasLogo.js" type="text/javascript"></script>  
</html>

Internet Explorer 9 と Internet Explorer 8 (図 1図 2) では、表示に大きな違いがあります。まず、このページには CSS が存在しないかのように、ページにスタイルがまったく適用されていません。さらに、ページの下部にあるすてきな盾形の HTML5 ロゴが表示されていません。これらの問題はどちらも簡単に解決できます。機能検出は、こうした問題を特定するために最初にとる手順です。

どちらの問題も原因は単純です。Internet Explorer 8 では <header>、<article>、および <canvas> が有効な HTML 要素ではないので機能しません。<canvas> の問題を解決するために、ここでは使用中のブラウザーやバージョンを UA スニッフィングで判断するのではなく、<canvas> 要素とそのJavaScript API がサポートされているかどうかを JavaScript を使ってブラウザーに問い合わせます。キャンバス機能のチェックは、次のとおりです。

!!document.createElement('canvas').getContext

このステートメントでは、いくつかの処理を行っています。まず、二重否定 (!!) を使って、未定義の値を明示的に false に設定します。次に、新しいキャンバス要素を手動で作成し、DOM にアタッチします。最後に、getContext 関数を呼び出します。これは、JavaScript で Canvas API を操作するために <canvas> で使用できる新しい関数です。Internet Explorer 9 を使用している場合、このステートメントは true を返します。Internet Explorer 8 を使用している場合、getContext 関数から未定義の値が返されるため、二重否定によって強制的に false に設定されます。

これが、最も基本的な機能検出です。このようなステートメントを使用すると、ブラウザーでの機能のサポートについて、より確実にクエリできます。手動の機能検出については、diveintohtml5.info/everything.html (英語) を参照してください。

機能検出に Modernizr を使用する

手動の機能検出は UA スニッフィングより優れていますが、機能の有無を検出し、その機能が存在しない場合の対応を判断するという面倒な作業は、やはり開発者が行う必要があります。また、キャンバスの例は 1 行のコードで済む単純な例でしたが、検出する必要があるすべての機能をこれほど単純に検出できるわけでも、すべてのブラウザーで検出コードが同じわけでもありません。たとえば、前回使用した CSS3 モジュール (border-radius と box-shadow) に対するサポートの検出は、いささか厄介です。

さいわい、Modernizr (modernizr.com、英語) により、適切な手法が提供されます。この Web ページいわく、Modernizr は、「HTML5 および CSS3 に基づく次世代 Web テクノロジのネイティブ実装を利用できるかどうかを検出」する JavaScript ライブラリです。Modernizr への参照をページに追加すると、次の 4 つの主要機能を利用できます。

  1. サポート対象の機能の包括的リスト。マークアップに追加され、条件付き CSS を定義できます。
  2. スクリプトベースの機能検出に役立つ JavaScript オブジェクト。
  3. Internet Explorer 8 およびそれ以前の Internet Explorer ブラウザーで役立つよう、実行時に DOM に追加されるすべての新しい HTML5 タグ (これについては後で説明します)。
  4. 条件付きでポリフィルをページに読み込むスクリプト ローダー。

1 つ目の機能についてはここで詳しく説明しませんが、modernizr.com (英語) にアクセスして、この機能やその他の機能に関するドキュメントをご覧になることをお勧めします。

2 つ目の機能を使用すると、次のコード行の代わりに、

!!document.createElement('canvas').getContext

こちらのコード行を使用できます。

Modernizr.canvas

このコードは、キャンバス要素がページでサポートされているかどうかを示すブール値を返します。自作の機能検出ではなく Modernizr を使用する長所は、Modernizr が十分にテストされて広く使用されている堅牢なライブラリであり、面倒な作業が自動的に処理される点です。Twitter、Google、マイクロソフトなど、数え切れないほどのサイトが Modernizr を使用しており、皆さんも同様に使用できます。ASP.NET MVC 3 Tools Update (2011 年 4 月リリース) では、新しい ASP.NET MVC アプリケーションに Modernizr が付属しています。さて、ここまでは <canvas> 要素がサポートされているかどうかを検出しただけです。その後の手順についてはまだ何も説明していません。機能検出により、その機能がブラウザーで利用できるかどうかわかったので、次の手順では、以下のような条件付きロジックを作成して、機能が存在しない場合に特定のコードが実行されないようにしたり、代替パスを実行したりします。

 

if (Modernizr.canvas) {
  // Execute canvas code here.
}

ブラウザーの追加機能があるかどうかに応じてサイトに機能を追加することを、高度な機能を備えたブラウザーのエクスペリエンスを拡張 (エンハンス) することから、"プログレッシブ エンハンスメント" と呼びます。これに対して "グレースフル デグラデーション" では、特定の機能が存在していない場合にブラウザーでエラーを発生させずに、制限付きの機能や代替機能でユーザーに表示します。古いブラウザーの場合、グレースフル デグラデーションは必ずしも既定の手法ではなく、多くの場合、最適な手法でもありません。Modernizr を活用すると、利用できる多数のブラウザー ポリフィルの 1 つを使用して、サポートしていないブラウザーに HTML5 に似た機能を追加できる可能性があります。

ポリフィルとは

Modernizr の Web サイトによると、ポリフィルとは、「古いブラウザー向けに標準 API を複製する JavaScript シム」です。"標準 API" は、キャンバスなど、特定の HTML5 テクノロジを表します。"JavaScript シム" とは、標準 API をサポートしないブラウザーに、そのような API を模倣する JavaScript コードやライブラリを動的に読み込めることを表します。たとえば、地理位置情報のポリフィルでは、World Wide Web コンソーシアム (W3C) の Geolocation API で定義されているとおり、グローバルな geolocation オブジェクトを navigator オブジェクトに渡し、getCurrentPosition 関数と "コード" のコールバック オブジェクトを追加します。ポリフィルは標準 API を模倣するため、すべてのブラウザーで、将来も使用できるような方法で、その API に対する開発が可能になり、最終的には、ある程度サポートが普及した時点でポリフィルを削除できます。追加作業は必要ありません。

Modernizr への参照をページに追加すると、図 3 の例に関してポリフィルのメリットがすぐに得られます。Internet Explorer 8 では <article> や <header> などのタグが認識されないため、スタイルが適用されずにページがレンダリングされます。また、タグが認識されなかったので、スタイルの適用先要素を CSS で選択する手段である DOM にもタグが追加されませんでした。

<script> タグと Modernizr への参照をページに追加すると、スタイルが適用されたページが表示されます (図 4 参照)。このメリットを得られる理由は、Modernizr では JavaScript (document.CreateElement('nav')) を使用して、すべての新しい HTML5 タグを手動で DOM に追加するので、タグを選択して CSS でスタイルを適用できるためです。

An HTML5 Page in Internet Explorer 8, with the Help of Modernizr
図 4 Internet Explorer 8 で表示した、Modernizrを利用する HTML5 ページ

Internet Explorer に新しい HTML5 要素のサポートを追加する使用法以外に、Modernizr ライブラリで既定で提供されているポリフィルはありません。このようなポリフィルは、開発者自身が自作のスクリプトで提供するか、Modernizr の Web サイトで随時追加されるオプションの一覧を利用します。バージョン 2.0 の時点では、Modernizr には yepnope.js (yepnopejs.com、英語) を基盤とする条件付きスクリプト ローダーが提供され、ポリフィル ライブラリを必要な場合にのみ非同期でダウンロードできます。必要な機能を提供する 1 つ以上のポリフィル ライブラリと Modernizr の併用は、強力な組み合わせです。

ポリフィルを使用して HTML5 機能のシミュレーションを行う

キャンバスの場合、excanvas という JavaScript ライブラリと Modernizr を使用すると、ポリフィルを利用して Internet Explorer 8 以前のブラウザーをサポートできます。excanvas は、Internet Explorer 6、Internet Explorer 7、および Internet Explorer 8 に API レベルでキャンバスに対するサポートを追加します。excanvas は bit.ly/bSgyNR (英語) からダウンロードでき、これをスクリプト フォルダーに追加すると、ページ上のスクリプト ブロックにコードを追加できます (図 5 参照)。

図 5 Modernizr を使用したポリフィルによるキャンバスのサポート

 

Modernizr.load({
  test: Modernizr.canvas,
  nope: '../js/excanvas.js',
  complete: function () {
    Modernizr.load('../js/html5CanvasLogo.js');
  }
}]);

図のコードでは、Modernizr のスクリプト ローダーを使用して、次の項目を指定しています。

  1. テストするブール式
  2. 式が false に評価された場合に読み込むスクリプトへのパス
  3. チェックやスクリプトの読み込みが完了したら実行するコールバック

キャンバスのコンテキストでは、アプリケーションにインテリジェンスやポリフィルを追加するために必要な要素はこれだけです。Modernizr では、ブラウザーがキャンバスをサポートしていない場合にのみ excanvas.js を非同期に読み込んでから、ページ上に HTML5 ロゴを描画するスクリプト ライブラリを読み込みます。

Modernizr の価値をよく表している、別の例を紹介しましょう。きちょうめんな方はお気付きでしょうが、図 4 のスタイルが適用されたサイトは、Internet Explorer 9 で表示した元のページ (図 1 参照) とはかなり異なっています。Internet Explorer 8 で表示したページには box-shadow と角丸コーナーが表示されません。どちらも表示されなければこの例のサイトを公開できないことも考えられるので、もう一度 Modernizr の力を借りましょう。

キャンバスと同様に、Modernizr では CSS3 モジュールがサポートされていないことを検出できますが、モジュールのポリフィルを提供するライブラリは開発者が用意する必要があります。さいわい、1 つのライブラリで両方のモジュールを提供する PIE というライブラリを利用できます (css3pie.com、英語)。

border-radius と box-shadow に対するサポートを追加するには、PIE をダウンロードした後で図 6 のコードをスクリプトに追加します。ここでは、border-radius モジュールまたは box-shadow モジュールの "いずれか" がサポートされているかどうか (両方とも必ずサポートされているかどうかではなく) をテストで確認し、いずれもサポートされていない場合は、動的に PIE.js を読み込みます。PIE の読み込みが完了したら、jQuery を実行して、すべての <article> タグを選択し、PIE.attach 関数を呼び出します。この結果、CSS で定義済みの border-radius と box-shadow のスタイルがサポートされるようになります。最終結果を、図 7 に示します。

図 6 Modernizr と PIE を使用した CSS3 サポートの追加

Modernizr.load({
  test: Modernizr.borderradius || Modernizr.boxshadow,
  nope: '../js/PIE.js',
  callback: function () {
    $('article').each(function () {
      PIE.attach(this);
    });
  }
});

CSS3 Support with Modernizr and PIE
図 7 Modernizr と PIE を使用した CSS3 サポート

ポリフィルをグレースフル デグラデーションに役立てる

今回説明したポリフィルの手法を使用する以外に、(別のライブラリのポリフィルではなく) アプリケーションのグレースフル デグラデーションが必要な場合にも、Modernizr を活用できます。

たとえば、Web ページに Bing Maps コントロールが含まれていて、地理位置情報を使用してユーザーの現在位置を特定し、この位置をプッシュピンとしてマップ コントロールに配置するとします (地理位置情報については、今後の記事で詳しく説明します)。

地理位置情報は、どのブラウザーでも最新のバージョンでサポートされていますが、古いブラウザーではサポートされません。また、地理位置情報のポリフィルは存在していますが、JavaScript だけでは Geolocation API を完全にサポートすることが少し難しいので、アプリケーションの機能を制限することにします。ユーザーのブラウザーが地理位置情報をサポートしていない場合に備えて、位置を手動で入力できるフォームを用意します。この情報を使用して、地図上の位置を特定し、プッシュピンを追加します。

Modernizr を使用すれば、作成しておいた 2 つのスクリプトのいずれかを呼び出して読み込むだけで済みます (図 8 参照)。このコードでは、Modernizr.geolocation プロパティをテストします。true の場合 ("yep") は、fullGeolocation.js スクリプトを読み込みます。このスクリプトでは、Geolocation API を使用して、(私のアクセス許可で) 位置を特定し、マップ上にプッシュピンを配置します (図 9 参照)。一方、テスト結果が false の場合 ("nope") は、フォールバック スクリプトを読み込みます。このスクリプトでは、ページに住所フォームを表示します。ユーザーがフォームを送信したら、そのアドレスを使用して、地図の中心を変更し、ピンを表示します (図 10 参照)。このように、最近のブラウザーには高度なエクスペリエンスを提供しながら、古いブラウザーには適切な代替手段を使用するよう機能を制限します。

図 8 Modernizr を使用したグレースフル デグラデーション

Modernizr.load({
  test: Modernizr.geolocation,
  yep: '../js/fullGeolocation.js',
  nope: '../js/geolocationFallback.js'
});

Mapping with Geolocation
図 9 地理位置情報による地図表示

Providing Geolocation Fallback Support
図 10 地理位置情報のフォールバック サポート

HTML5 の高度な機能を検討しても、多数のユーザーがまだ古いブラウザーを使用していることから、サイトがユーザーに十分対応していないと判断するのは簡単です。しかし、グレースフル デグラデーションに役立つだけでなく、ユーザーが今すぐ HTML5 を利用できるように古いブラウザーを最新機能に対応させる機能も備えた、すばらしいソリューションも存在します。今月は、機能検出、Modernizr、およびポリフィルを使用すると、ますます増加している最新ブラウザーのユーザーに遅れることなく HTML5 を導入でき、しかも他のユーザーが取り残されないよう配慮できることについて説明しました。

Brandon Satrom は、テキサス州オースティンでマイクロソフトの開発者エバンジェリストとして活躍しています。彼のブログは userinexperience.com (英語) です。Twitter のアカウントは @BrandonSatrom (英語) です。

この記事のレビューに協力してくれた技術スタッフの Damian EdwardsScott Hunter、および Clark Sell に心より感謝いたします。