ふろしき.js

Web + Mobile + UX + Performance Tech

クロスブラウザ対応を助けるJSライブラリ"Modernizr"

f:id:furoshiki0223:20130629030936p:plain

Modernizrは、HTML5やCSS3などの新しいWebの標準がWebブラウザに実装されているか、汎用的なインタフェースを通じて確認することができるJavaScriptライブラリです。

▼公式サイト
http://modernizr.com
▼ダウンロード
http://modernizr.com/download/
▼Developper版(動作を試してみたい方向け。)
http://modernizr.com/downloads/modernizr-latest.js
※注:そのままでは機能が不足するため、1章〜4章の解説通りには動いてくれません!


以下は、簡単なコードのサンプルです。WebブラウザにCanvasの機能が備わっているか、確認することができます。

    <script src="./js/modernizr.canvas.js"></script>
    <script>
if( Modernizr.canvas ) {
    alert("You can use Canvas!");
} else {
    alert("You can NOT use Canvas!");
}
    </script>

▼Canvasをサポートしていない"IE8(Win7版)"の場合
f:id:furoshiki0223:20130629030407p:plain
▼Canvasをサポートしている"IE10(Win7版)"の場合
f:id:furoshiki0223:20130629030648p:plain

WindowsXPの終焉、Google Chromeのシェア獲得、iPhoneから始まったスマートデバイスの普及で、Webブラウザ環境は多様化しました。ほんの少し前まで、"インターネット=IE6"が常識で、Webは長い間、統一された一つのプラットフォームしか持たない状況が続きました。

しかし近年、Windows7の浸透により、IEは6以外のバージョンも広く普及し始めました。IEは他のモダンブラウザとは異なり、OSへの依存性が強く、古いOSでは最新のバージョンが利用できないという制約を持ちます。実際に現在のインターネット上でのWebブラウザシェアは、IE8、IE9、IE10が、それぞれ無視出来ない程度の割合で含まれています。IEだけは最新でない場合も考慮して、Webコンテンツを作る必要があるのです。

また、GoogleのChromeブラウザのシェアが急激に上昇し、Firefoxも一般ユーザへ広く浸透するようになりました。スマートデバイスの登場で、Safariなどのスマートデバイス専用のWebブラウザも、無視できない存在となりました。今Webは、統一された環境を失い、ブラウザごとにコンテンツを作りこむことが、困難な状況に陥っています。

こうした状況に置かれ、Webコンテンツの作成には、Webブラウザの機能実装を確認し振る舞いを変えるという考え方が広く普及しました。Webブラウザ・バージョンを判断軸にするのでなく、機能実装を判断軸にする。進化の早いWeb技術の中で、シンプルに後方互換性を獲得するための確かな手段です。

Modernizrはこのような、機能駆動によるWebコンテンツの実装を、理想的な形で実現するために開発されたJavaScriptライブラリです。Modernizrを使えば、IEの条件付き書式を使ったり、JavaScriptからUserAgentを取得するような処理を記述しなくても、汎用的なインタフェースを通じてWebブラウザに実装された機能を確認することができます。

1. Polyfillによる相互運用性を意識した実装

エンジニアリングの世界のうち、画面部分に特化した専門家のことを、フロントエンド技術者といいます。近年の互換性に関する問題は、正式には"相互運用性"と呼ばれ、その対策手法はフロントエンド技術者の間でノウハウが整理され、体系化が進みつつあります。

Polyfillは、HTML5やCSS3など新しいWeb技術の持つ機能が、レガシーブラウザ(主に古いIE)によって利用される必要が生じた際に、XHRやActiveXやFlashなどの古くから活用されているテクノロジーを使用し、類似した動作になるようシミュレートさせ目的を達成させるという、相互運用性に関連した技法の一つです。

以下のコードは、Modernizrを利用して、HTML Canvasが実装されていないWebブラウザで、Canvasの動作をシミュレート(Polyfill)させるため、JavaScriptライブラリ"excanvas.js"をロードしている例です。

    <script src="./js/modernizr.canvas.js"></script>
    <script>
if( ! Modernizr.canvas ) {
    Modernizr.load("./js/excanvas.js");
}
    </script>

2. プログレッシブ・エンハンスメントという設計方針

excanvas.jsはcanvasと同じインタフェースを持たせ、動作をシミュレートしてくれるJavaScriptライブラリです。先ほどの例では、Canvasが実装されていない環境下でも動作が上手くいくよう、単純に"excanvas.js"をロードさせて対処していました。

しかし、実態としてそうもいかないケースがあります。Polyfill用ライブラリは、内部では各Webブラウザに実装された独自機能を使うなど強引な手を使っているものが多く、HTML5ほどの高いポテンシャルが得られていないものが多かったりします。

そこで、古いWebブラウザの実装に合わせて機能を絞り、新しいWebブラウザについては付加価値として高いUXを実現させようという考えが生まれました。それがプログレッシブ・エンハンスメントです。

例えば、Table要素を使ったデータの一覧表を表示する機能を作ったとします。新しいWebブラウザだと、これをビジュアライズさせたグラフを表示させて、より高い付加価値を与えてたいという事例を想定してみましょう。

この場合、以下のような実装になります。

    <table>・・・</table>
    <script src="./js/modernizr.canvas.js"></script>
    <script>
if( Modernizr.canvas ) {
    Modernizr.load([
        "./js/jquery-visualizer.js",
        "./js/visualizeTable.js"
    ]);
}
    </script>

Canvasが実装されているか否かをModernizerが判定し、canvasがある環境下でのみ、jQuery-visualizerと呼ばれるtable要素の内容をグラフ化させるライブラリをロード。加えて、visualizeTable.jsという名前を与えたユーザスクリプトをロードし、グラフが表示されるようにスクリプトを実行させています。

3. グレイスフル・デグラデーションという設計方針

古いWebブラウザが混在している環境下でも、なんとかグラフを表示できないものかと考えるでしょう。jquery-visualizerは結構シンプルなグラフを吐き出すツールなので、excanvasでpolyfillしてもそこそ動いてくれたりします。ただ、お世辞にもパフォーマンスが良いとは言えません。ガッツリと処理をさせるには力不足です。

古いWebブラウザは、excanvasでそこそこに動いて貰おう。そして新しいWebブラウザでは、Canvasの機能をフル活用して、高いUXを実現しよう。高い価値を発揮させよう。Webブラウザの入替えが近くに行われるような現場などでは、そういう設計思想が歓迎されるかもしれません。新しいWebブラウザを基準にして、機能を決定するというケースです。

新しいWebブラウザでは最適で、古いWebブラウザではやや質や機能が落ちても良い、場合によっては動かないことを許容するという考えで機能設計を行う場合、これをグレイスフル・デグラデーションと言います。

実装は以下のイメージです。

    <table>・・・</table>
    <script src="./js/modernizr.canvas.js"></script>
    <script>
Modernizr.load([
    test : Modernizr.canvas,
    yep : "./js/visualizeTableExtends.js",
    nope : "./js/excanvas.js",
    both : [ "./js/jquery-visualizer.js", "./js/visualizeTable.js" ]
]);
    </script>

書き方が一気に変わったように見えます。実はModernizr.loadメソッドには、単純にJSライブラリをロードする以外にも機能があったりします。簡単な条件式を解釈し、ロードするライブラリを切り替える機能です。

ModernizrはPolyfillでの活用が前提のライブラリなので、こういった気の利いた機能がついていたりします。ちなみに構文は、以前このブログにも書いたyepnope.jsと同じです。

"test"プロパティで指定した式が真である場合、"yep"プロパティにて指定されたJSファイルがロードされます。偽である場合は、"nope"プロパティで指定されたJSファイルがロードされます。"both"プロパティはどちらであってもロードされます。

この例では、testプロパティでCanvasの実装の有無を確認し、実装がある場合は"./js/visualizeTableExtends.js"でリッチな動作を実現するユーザスクリプトをロードし、実装がない場合は単に"./js/excanvas.js"というPolyfill用ライブラリを呼んでいます。Canvasの実装の有無に関係なく、グラフ出力ライブラリである"./js/jquery-visualizer.js"がロードされ、最低限のグラフ出力処理が記述されたユーザスクリプトである"./js/visualizeTable.js"をロードしています。

レガシーWebブラウザが混在する環境下では、こうしたアプローチを行うことがあるでしょう。Modernizrは、単純にWebブラウザの実装を確認できるようにするだけでなく、相互運用性に関する多くのケーススタディをカバーする機能が実装されています。その機能は、多くの技術者により検討され考えぬかれたベストプラクティスです。対処方法に悩みが生じたなら、Modernizrに付いている機能を確認すると、その解が得られるかもしれません。

4. Polyfillの限界

Modernizrを通じてPolyfillの有用性を説明してきましたが、常にPolyfillが最善の策になるとは限りません。Polyfillはあくまで、相互運用性に関する対策技法の一つに過ぎないのです。

例えば、HTML5ではheader、footer、section、articleといった、コンテンツの意味付けを強化する(セマンティクス)、要素が追加されています。これは古いWebブラウザで正しく認識してくれません。Polyfillライブラリとしては、"html5shiv.js"が有名ですが、パフォーマンスに少々問題があります。

HTML5の学習を始めた時、一番最初に習うと言っても過言ではないセマンティクスの要素ですが、その目的はコンピュータによるコンテンツの解釈の高度化です。可読性を上げるという効果もありますが、最大の効果を発揮するのは、検索エンジンのクローラーなどにコンテンツが読み取られる場合でしょう。業務システムのようなクローズドな環境下で利用する場合、その効果を発揮できないことが多いはずです。

無理にPolyfillを使うのではなく、古くから利用されるdivやspanタグで代替するのも手です。対応Webブラウザが高いシェアを占めるその日まで、利用を封印してしまうのです。技術的理想からは外れてしまいますが、古いWebブラウザを生かさざる得ない環境下では、避ける事ができない問題は必ずといっていいほど存在します。当たり前のことですが、古いWebブラウザに新しいことをさせるのは、限界があるのです。

ただ、古いWebブラウザにより受ける制限、その理由は、後方互換性を無くすようなものであってはいけません。古いWebブラウザは、モダンブラウザに比べ非常に癖の強い動作をしますが、そこに固執しすぎることも良いことではありません。クロスブラウザの相互運用性では、全ての環境が同じように動作することは無いという前提で、設計を行うべきです。多少の動作の違いは、寛容に受け止めましょう。

5. さらにステップアップ

ここでは、Modernizrのダウンロードの手順と、他の機能について説明します。

ダウンロードページはこちらです。
http://modernizr.com/download/

f:id:furoshiki0223:20130629060443p:plain

大量のチェックボックスに尻込みしそうになるでしょう。Modernizrにはこれだけ多くのチェック機能や、Polyfillを支援する機能が備わっているのです。JavaScriptは、不要な機能を出来る限り取り外した状態にしておく必要があります。コードボリュームが大きいほど、ページ表示の応答性能が低下してしまうからです。通信量もそうですが、JavaScriptの場合はコンパイルにも時間を要します。スクリプトの1バイトは血の一滴に相当します。開発者はそれくらい、シビアにコーディングを行なっているのです。

したがって、まず初めにこの画面でやることは、全てのチェックボックスからチェックを外すことでしょう。全ての機能が取り外された、最小の状態にします。そこから最低限の必要機能だけに焦点を絞ってチェックを入れていきます。

まずこの画面の上部には、"CSS3"、"HTML5"、"Misc."、"Extra"の4つの分類があります。うち、CSS3、HTML5、Misc.の3つは、実装機能をチェックするためのものです。Extraは特殊な機能で、レガシーIEでhtml5のセマンティクス要素を追加する"html5shiv"や、先ほどから頻繁に利用しているJS/CSSのロード機能である"load"メソッド、Media QueryのPolyfillライブラリが含まれます。Polyfillを目的としているなら、"Modernizr.load"へチェックを入れましょう。

すぐ下には、"Extensibilty"、"None-core detects"の2つの分類があります。

f:id:furoshiki0223:20130629062322p:plain

"None-core detects"は、HTML5やCSS3のようなメインから外れたものを検知する機能です。HTML4の時点で既に標準化されてはいるものの、実装されていない可能性がある機能などが含まれています。

"Extensibility"は、どちらかと言えばWebブラウザやプラットフォーム開発者や、そのテスターが利用する機能でしょう。 新しいチェック用APIを追加するための機能も備えています。また、ベンダプレフィクスを付与する機能もあります。WebブラウザのユーザとしてModernizrを活用し、かつ安定した機能を利用したいと考えている場合、ここの機能は利用されることが無いでしょう。

6. Modernizr Prefixed

ExtensibilityはWebブラウザ開発者やそのテスターが利用する機能と説明しました。しかし実態として、Webブラウザが実装した機能を早期に利用したいというケースがあるでしょう。例えばCSS3は、テスト中のであるにも関わらず広く利用されている状況です。

例えば、以下のような記述を見たことは無いでしょうか?

div.comment {
   -moz-border-radius: 20px;
   -webkit-border-radius: 20px;
   border-radius: 20px;
}

これはCSSにより、枠を丸くするためのプロパティ指定です。CSSの各プロパティには、"-moz-"や"-webkit-"といったベンダプレフィクスが指定されています。IEの場合は、バージョン9から"-ms-"というベンダプレフィクスを追加し、テスト版のCSSの機能が利用できるようになりました。Operaも古いものは独自のエンジンを利用しているため、"-o-"というプレフィクスが必要でした。

CSSの場合、ベンダプレフィクス以外大抵は同じプロパティ名で動作できます。しかもパラメータの意味も同じであり、指定される値はベンダプレフィクスを除いて全く同じものを、各Webブラウザで動作するよう複数指定することになります。

"Modernizr Prefixed"は、これを一元化させることができます。例えば今回のような、border-radiusのケースでは、以下のような記述を行うことで、ベンダプレフィクスをわざわざ追加する手間を無くすことができます。

Modernizr.prefixed('borderRadius');

CSSとJavaScriptはCSSプロパティの命名規則が異なります。"border-radius"は、JavaScriptから呼び出す時は"borderRadius"とキャメルケース記法で行います。CSSプロパティ名は2つ以上の言葉を合わせることが多いため、注意が必要でしょう。

prefixedには第二引数があり、これを活用すればJavaScript内でベンダプレフィクスの付いたメソッドを呼び出すケースでも活用できます。例えば以下の例です。

window.requestAnimFrame = (function(){
    return window.requestAnimationFrame       || 
           window.webkitRequestAnimationFrame || 
           window.mozRequestAnimationFrame    || 
           window.oRequestAnimationFrame      || 
           window.msRequestAnimationFrame     || 
           function( callback ){
               window.setTimeout(callback, 1000 / 60);
           };
    })();

requestAnimationFrameは、Webブラウザのレンダリング周期に合わせ、再描画の準備が整ったタイミングでコールバックに制御を渡してくれるメソッドです。ネイティブのゲームだと必ずといって良いほど活用される機能ですが、Webブラウザでの実装は最近になってからのことです。まだベンダプレフィクスが必要な試験的機能という位置づけになります。このメソッドが動作しないWebブラウザでは、一般的なディスプレイの更新周期に合わせて、1000/60ミリ秒程度の時間差を指定して返すのが定石です。厳密にするならもう少し工夫が必要ですが、requestAnimationFrameの置き換えには十分に機能してくれます。

この機能を利用する場合、上記の例に記述した通り、大量のORを指定しなくてはいけません。これはとても見苦しいものになります。Modernizr Prefixを利用すると、以下のような記述に短縮化できます。

window.requestAnimFrame
    = Modernizr.prefixed('requestAnimationFrame', window)
        || function( callback ){ window.setTimeout(callback, 1000 / 60); };

これらの手法は、あくまでWebブラウザの試験的機能が正常に動作することが前提です。一部のWebブラウザだけ異なる動作をしてしまう場合は、破綻してしまいます。気をつけて使用して下さい。

7. Add CSS Classes

Extra内に、"Add CSS Classes"にチェックを付けると、JavaScriptだけでなく、CSSからも機能の有効性を確認するためのインタフェースを提供します。

"Add SCC Classes"を利用する場合、HTMLファイルへは、以下のような記述を行なって下さい。

<html class="no-js">

例えば、ダウンロードページで"Canvas"と"Add CSS Classes"へチェックを入れたMondertizerを読み込ませると、以下の記述に変化します。

<html class="js canvas">

これは、JavaScriptが有効かつ、Canvasが有効という意味になります。Canvasが有効でない環境、例えばIE8の場合、開発者ツールでは以下のように表示されます。

f:id:furoshiki0223:20130629214333p:plain

"no-canvas"、つまりCanvasは無効と表示されるわけです。Canvasだとあんまりその良さがわかりませんが、CSS3の機能だとその有用さが理解できるかと思います。

例えば"multiple backgrounds"だとどうなるでしょうか。

#nice {
    background: url(background-one.png) top left repeat-x;
}
.multiplebgs #nice {
    background: url(background-one.png) top left repeat-x,
    url(background-two.png) bottom left repeat-x;
}

このCSSは、Web Designer Notebookの内容を参考にしました。表示するとこうなります。

f:id:furoshiki0223:20130629215021j:plainf:id:furoshiki0223:20130629215027j:plain

CSS側で、機能の有無を確認して、表示を切り替えることができるのです。

8. 最後に

Modernizrの解説と言いながら、クロスプラットフォームにおける相互運用性対策の思想とその実践について学ぶことになったかと思います。Modernizrは相互運用性対策に特化したツールですので、こういう説明になるのは無理もないでしょう。

HTML5を通じて、各Webブラウザベンダは相互運用性に対してシビアになり、HTML4の頃とは比べ物にならないほど改善が図られました。相互運用性は永久に解決されない課題で、小さな仕様の違いは残り続けることになるでしょう。ただ、これらをクリアしていくための技法は体系化され、進化しています。悩みを抱えた時、こうした技法について調べてみてはいかがでしょうか。