クロスデバイス ウェブアプリの構築に対する非レスポンシブ アプローチ

メディアクエリは優れていますが...

メディアクエリは、スタイルシートを少し調整して、さまざまなサイズのデバイスでユーザー エクスペリエンスを向上させたいウェブサイト デベロッパーにとって最適な機能です。メディアクエリでは基本的に画面サイズに応じて サイトの CSS をカスタマイズできますこの記事をご覧になる前に、レスポンシブ デザインの詳細について学び、メディアクエリの使用例を mediaqueri.es でご確認ください。

Brad Frost が以前の記事で指摘しているように、外観の変更は、モバイルウェブ向けに構築する際に考慮すべき多くのことの一つにすぎません。モバイルサイトを構築する際に、メディアクエリによるレイアウトのカスタマイズのみを行う場合、次のような状況になります。

  • すべてのデバイスで同じ JavaScript、CSS、アセット(画像、動画)を取得するため、必要以上に読み込み時間が長くなります。
  • すべてのデバイスは同じ初期 DOM を取得するため、デベロッパーは過度に複雑な CSS を記述しなければならない可能性があります。
  • 各デバイスに合わせたカスタム操作を指定する柔軟性がほとんどない。

ウェブアプリに必要なのはメディアクエリだけ

誤解しないでください。メディアクエリを使ったレスポンシブデザインは 嫌いではなく 世界中に配信できると思いますさらに、上記の問題の一部は、レスポンシブ画像や動的スクリプト読み込みなどのアプローチで解決できます。ただし、ある時点では、増分調整の回数が多すぎることがあり、異なるバージョンを提供する方がよい場合もあります。

作成する UI の複雑さが増し、単一ページのウェブアプリがますます増えてくると、デバイスの種類ごとに UI をさらにカスタマイズする必要が生じます。この記事では、こうしたカスタマイズを最小限の労力で行う方法について説明します。一般的なアプローチは、ユーザーのデバイスを適切なデバイスクラスに分類して、そのデバイスに適切なバージョンを配信すると同時に、バージョン間でのコードの再利用を最大化することです。

どのデバイスクラスを対象としますか?

インターネットに接続されたデバイスは数多く存在しますが、そのほとんどにブラウザが搭載されています。複雑化の要因として、Mac ノートパソコン、Windows ワークステーション、iPhone、iPad、タップ入力を備えた Android スマートフォン、スクロール ホイール、キーボード、音声入力、感圧機能を備えたデバイス、スマートウォッチ、トースター、冷蔵庫などが挙げられます。広く利用されているデバイスもあれば、非常にまれなデバイスもあります。

さまざまなデバイス。
さまざまなデバイス(出典)。

優れたユーザー エクスペリエンスを作成するには、ユーザーがどのような人々で、どのデバイスを使用しているかを把握する必要があります。マウスとキーボードを備えたパソコン ユーザー向けのユーザー インターフェースを作成し、スマートフォン ユーザーに提供すると、異なる画面サイズと別の入力モダリティ向けに設計されているため、インターフェースに不満を抱くことになります。

アプローチの範囲には次の 2 つの極端があります。

  1. すべてのデバイスで動作する 1 つのバージョンを構築。デバイスによって設計上の考慮事項が異なるため、UX に影響を及ぼします。

  2. サポートするデバイスごとにバージョンをビルドします。作成するアプリケーションのバージョンが多すぎるため、これは一生懸命になります。また、次の新しいスマートフォンがリリースされたときに(ほぼ毎週実施)、さらに別のバージョンの作成が必要になります。

ここには基本的なトレードオフがあります。デバイス カテゴリが多いほど、ユーザー エクスペリエンスは向上しますが、設計、実装、保守には多くの労力がかかります。

パフォーマンス上の理由や、デバイスクラスごとに提供するバージョンが大きく異なる場合は、選択したデバイスクラスごとに個別のバージョンを作成することをおすすめします。それ以外の場合は、レスポンシブ ウェブ デザインが最適です。

ソリューションの候補

1 つの妥協点として、デバイスをカテゴリに分類し、それぞれのカテゴリに対して可能な限り最適なエクスペリエンスを設計します。どのカテゴリを選択するかは、商品とターゲット ユーザーによって異なります。次に、現在の一般的なウェブ対応デバイスを網羅する分類例を示します。

  1. 小画面 + タップ(主にスマートフォン)
  2. 大画面 + タップ(主にタブレット)
  3. 大画面 + キーボード/マウス(主にデスクトップ/ノートパソコン)

これは、考えられる多くの内訳の一つにすぎませんが、この記事の執筆時点では非常に理にかなっています。上記のリストには、タッチ スクリーンを搭載していないモバイル デバイス(フィーチャー フォン、一部の専用の電子書籍リーダーなど)はありません。ただし、ほとんどのデバイスにはキーボード ナビゲーションやスクリーン リーダー ソフトウェアがインストールされており、ユーザー補助機能を念頭に置いてサイトを構築すると問題なく機能します。

フォーム ファクタ別のウェブアプリの例

さまざまなフォーム ファクタでまったく異なるバージョンを提供するウェブ プロパティの例は数多くあります。Facebook と同様に Google 検索がこれに該当しますパフォーマンス(アセットの取得、ページのレンダリング)だけでなく、より一般的なユーザー エクスペリエンスも考慮する必要があります。

ネイティブ アプリの世界では、多くのデベロッパーがデバイスクラスに合わせてエクスペリエンスを調整しています。たとえば、iPad の Flipboard の UI は、iPhone の Flipboard とは大きく異なります。タブレット版は両手の使用と左右反転向けに最適化されており、スマートフォン版は片手操作と上下反転が想定されています。また、Things(To-do リスト)や Showyou(ソーシャル動画)など、他の多くの iOS アプリでも、スマートフォンとタブレットのバージョンが大幅に異なります。

スマートフォンやタブレットの UI を大幅にカスタマイズする。
スマートフォンやタブレットの UI の大幅なカスタマイズ。

アプローチ 1: サーバーサイドの検出

サーバーでは、扱っているデバイスについての理解はかなり限定的です。最も有用な手がかりは、おそらくユーザー エージェント文字列でしょう。これは、すべてのリクエストで User-Agent ヘッダーを介して提供されます。このため、同じ UA スニッフィング アプローチがここで機能します。実際、DeviceAtlas と WURFL のプロジェクトはすでにこの処理を行っており、デバイスに関する多くの追加情報を提供しています。

残念ながら、それぞれに独自の課題があります。WURFL は非常に大規模で、20 MB の XML が含まれるため、リクエストごとにサーバー側でかなりのオーバーヘッドが発生する可能性があります。パフォーマンス上の理由から XML を分割しているプロジェクトもあります。DeviceAtlas はオープンソースではなく、使用するには有料ライセンスが必要です。

また、Detect Mobile Browsers プロジェクトなど、よりシンプルで無料の代替サービスもあります。ただし欠点は、必然的にデバイス検出の包括性が低くなることです。また、モバイル デバイスとモバイル以外のデバイスを区別するのみで、アドホックな調整によってのみ、タブレットに対する限定的なサポートを提供します。

アプローチ 2: クライアントサイドの検出

機能検出を使用することで、ユーザーのブラウザとデバイスについて多くのことを学習できます。判定する必要がある主なことは、デバイスにタッチ機能があるかどうかと、デバイスの画面が大きいか小さいかです。

大小のタッチデバイスを区別するために、どこかに線を引く必要があります。5 インチの Galaxy Note のようなエッジケースはどうでしょうか?次の図は、一般的な Android デバイスと iOS デバイスを重ね合わせた画像(対応する画面解像度)を示しています。アスタリスクは、2 倍の密度で出荷されるデバイスである、または 2 倍の密度でリリースされる可能性があることを示します。ピクセル密度が 2 倍になる場合もありますが、CSS では同じサイズがレポートされます。

CSS のピクセルに関しては、モバイルウェブの CSS ピクセルとスクリーン ピクセルは同じではありません。iOS Retina デバイスでは、ピクセル密度を 2 倍にする手法が導入されています(例: iPhone 3GS と 4、iPad 2 と 3 など)。Retina の Mobile Safari UA では、ウェブを損なわないように、同じデバイス幅でレポートされます。他のデバイス(例:Android など)のディスプレイが高解像度の場合は、デバイス幅が同じになるようにします。

デバイスの解像度(ピクセル単位)。
デバイスの解像度(ピクセル単位)。

ただし、この決定を複雑にしているのは、縦向きモードと横向きモードの両方を検討することが重要です。デバイスの向きを変えるたびにページを再読み込みしたり、追加のスクリプトを読み込まないようにしたりするのは望ましくありませんが、別の方法でページをレンダリングすることは可能です。

次の図では、縦向きと横向きの輪郭が重なって表示される(正方形が完成する)結果、各デバイスの最大サイズが正方形で表されています。

縦向きと横向きの解像度(ピクセル単位)
縦向きと横向きの解像度(ピクセル単位)

しきい値を 650px に設定すると、iPhone、Galaxy Nexus は smalltouch、iPad、Galaxy Tab は「タブレット」に分類されます。この場合、アンドロジナスな Galaxy Note は「スマートフォン」に分類され、スマートフォンのレイアウトを取得します。

そのため、次のような戦略が合理的です。

if (hasTouch) {
  if (isSmall) {
    device = PHONE;
  } else {
    device = TABLET;
  }
} else {
  device = DESKTOP;
}

特徴検出アプローチの実際の操作に関する最小限のサンプルをご覧ください。

もう一つのアプローチは、UA スニッフィングを使用してデバイスタイプを検出することです。基本的に、一連のヒューリスティックを作成し、ユーザーの navigator.userAgent と照合します。擬似コードは次のようになります。

var ua = navigator.userAgent;
for (var re in RULES) {
  if (ua.match(re)) {
    device = RULES[re];
    return;
  }
}

UA 検出アプローチのサンプルをご覧ください。

クライアントサイドの読み込みに関する注意事項

サーバーで UA の検出を行っている場合、新しいリクエストを受け取ったときに配信する CSS、JavaScript、DOM を決めることができます。ただし、クライアントサイドでの検出を行っている場合は、状況はより複雑になります。次のようなオプションがあります。

  1. このデバイスタイプのバージョンを含むデバイスタイプ固有の URL にリダイレクトします。
  2. デバイスタイプ固有のアセットを動的に読み込みます。

最初の方法は簡単で、window.location.href = '/tablet' などのリダイレクトが必要です。ただし、位置情報にはデバイスタイプ情報が追加されるため、History API を使用して URL をクリーンアップすることができます。残念なことに、この方法でリダイレクトを行うため、特にモバイル デバイスではリダイレクトが遅くなる可能性があります。

2 番目のアプローチは実装がかなり複雑です。CSS と JS を動的に読み込むメカニズムが必要です。また(ブラウザによって異なります)、<meta viewport> のカスタマイズなどができない場合があります。また、リダイレクトがないため、配信された元の HTML がそのまま維持されます。もちろん、JavaScript でこれを操作することもできますが、アプリケーションによっては、処理が遅くなったり、煩雑になったりする場合があります。

クライアントまたはサーバーの決定

両者には次のようなトレードオフがあります。

Pro クライアント:

  • UA ではなく画面サイズや機能に基づいているため、将来を見据えた柔軟性が得られます。
  • UA リストを常に更新する必要はありません。

Pro Server:

  • どのバージョンをどのデバイスに配信するかを完全に制御できます。
  • パフォーマンスの向上: クライアントのリダイレクトや動的読み込みが不要になります。

個人的には、device.js とクライアントサイド検出から始めることをおすすめします。アプリケーションの進化に伴って、クライアントサイドのリダイレクトがパフォーマンスの著しい低下を招くと思われる場合は、device.js スクリプトを簡単に削除し、サーバーに UA 検出を実装できます。

device.js のご紹介

device.js は、特別なサーバーサイド構成を必要とせずに、セマンティックなメディアクエリをベースとするデバイス検出を開始するための出発点です。これにより、ユーザー エージェント文字列の解析に必要な時間と労力を節約できます。

具体的には、検索エンジンと相性の良いマークアップ(link rel=alternate)を <head> の先頭で指定し、提供するサイトのバージョンを指定します。

<link rel="alternate" href="http://foo.com" id="desktop"
    media="only screen and (touch-enabled: 0)">

次に、サーバーサイドの UA 検出を行って、独自にバージョン リダイレクトを処理するか、device.js スクリプトを使用して、機能ベースのクライアントサイド リダイレクトを行います。

詳しくは、device.js プロジェクトのページをご覧ください。また、クライアントサイドのリダイレクトに device.js を使用する偽のアプリケーションもご覧ください。

推奨事項: フォーム ファクタ固有のビューを使用した MVC

ここまでで、デバイスタイプごとに 1 つずつ、完全に別々の 3 つのアプリを作成するように指示しているのではないでしょうか。いいえ。コードの共有が鍵です。

Backbone や Ember などの MVC に似たフレームワークをご利用になったことがあると思います。関心の分離の原則、具体的には UI(ビューレイヤ)をロジック(モデルレイヤ)から分離する必要があることはご存じでしょう。初めて使用する場合は、MVC のリソースJavaScript の MVC をいくつかお試しください。

クロスデバイスのストーリーは、既存の MVC フレームワークにぴったりと合っています。ビューは簡単に別々のファイルに移動し、デバイスタイプごとにカスタムビューを作成できます。これにより、ビューレイヤを除くすべてのデバイスに同じコードを配信できます。

クロスデバイス MVC。
クロスデバイス MVC。

プロジェクトが次のような構造になる場合があります(もちろん、アプリケーションに応じて最適な構造を選択できます)。

モデル/(共有モデル) item.js item-collection.js

Controllers/(共有コントローラ) item-controller.js

バージョン/(デバイス固有のもの) table/ desktop/ phone/(スマートフォン固有のコード) style.css index.html view/ item.js item-list.js

このような構造により、デバイスごとにカスタム HTML、CSS、JavaScript を使用できるため、各バージョンを読み込むアセットを完全に制御できます。これは非常に強力で、アダプティブ イメージなどの手法に頼ることなく、クロスデバイス ウェブ向けの開発で最も無駄のない、パフォーマンスに優れた方法を実現できます。

お好みのビルドツールを実行したら、すべての JavaScript と CSS を連結して 1 つのファイルに圧縮し、読み込みを高速化します。本番用の HTML は以下のようになります(スマートフォンでは device.js を使用しています)。

<!doctype html>
<head>
  <title>Mobile Web Rocks! (Phone Edition)</title>

  <!-- Every version of your webapp should include a list of all
        versions. -->
  <link rel="alternate" href="http://foo.com" id="desktop"
      media="only screen and (touch-enabled: 0)">
  <link rel="alternate" href="http://m.foo.com" id="phone"
      media="only screen and (max-device-width: 650px)">
  <link rel="alternate" href="http://tablet.foo.com" id="tablet"
      media="only screen and (min-device-width: 650px)">

  <!-- Viewport is very important, since it affects results of media
        query matching. -->
  <meta name="viewport" content="width=device-width">

  <!-- Include device.js in each version for redirection. -->
  <script src="device.js"></script>

  <link rel="style" href="phone.min.css">
</head>
<body>
  <script src="phone.min.js"></script>
</body>

(touch-enabled: 0) メディアクエリは非標準です(Firefox では moz ベンダー プレフィックスの背後にしか実装されていません)が、device.js では Modernizr.touch のおかげで正しく処理されます。

バージョンのオーバーライド

デバイスの検出は、場合によっては正しく行われず、ユーザーはスマートフォンのタブレットのレイアウトを確認する(Galaxy Note を使用している場合など)ことがあるため、ユーザーが手動でオーバーライドする場合は、使用するサイトのバージョンを選択できるようにすることが重要です。

通常のアプローチは、モバイル版から PC 版へのリンクを提供することです。これは簡単に実装できますが、device.js ではこの機能を device GET パラメータでサポートしています。

まとめ

まとめると、レスポンシブ デザインの世界にぴったり収まらないクロスデバイスのシングルページ UI を構築する場合、次のことを行います。

  1. サポートするデバイスクラスのセットと、デバイスをクラスに分類する際の基準を選択します。
  2. 関心を厳密に分離して MVC アプリをビルドし、他のコードベースからビューを分割します。
  3. device.js を使用して、クライアントサイドのデバイスクラス検出を行います。
  4. 準備ができたら、スクリプトとスタイルシートをデバイスクラスごとにそれぞれ 1 つにパッケージ化します。
  5. クライアントサイドのリダイレクトのパフォーマンスに問題がある場合は、device.js を放棄し、サーバーサイドの UA 検出に切り替えます。