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

メディアクエリは便利ですが...

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

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

  • すべてのデバイスに同じ JavaScript、CSS、アセット(画像、動画)が提供されるため、必要以上に読み込み時間が長くなります。
  • すべてのデバイスが同じ初期 DOM を取得するため、デベロッパーは過度に複雑な CSS を記述しなければならない可能性があります。
  • デバイスごとにカスタマイズされたインタラクションを指定する柔軟性が低い。

ウェブアプリにはメディアクエリ以上のものが必要

誤解しないでください。メディア クエリによるレスポンシブ デザインを嫌っているわけではありません。むしろ、レスポンシブ デザインには確かな価値があると思います。さらに、上記の問題の一部は、レスポンシブ画像や動的スクリプト読み込みなどのアプローチで解決できます。ただし、ある時点で、微調整が多すぎると感じ、別のバージョンを配信したほうがよい場合があります。

作成する UI の複雑さが増し、シングルページ ウェブアプリに移行するにつれて、デバイスの種類ごとに UI をカスタマイズする作業が増えます。この記事では、最小限の労力でこれらのカスタマイズを行う方法について説明します。一般的なアプローチでは、バージョン間のコードの再利用を最大限にしながら、訪問者のデバイスを適切なデバイスクラスに分類し、そのデバイスに適切なバージョンを提供します。

ターゲットとするデバイスクラス

インターネットに接続されたデバイスは数多くあり、そのほとんどにブラウザが搭載されています。複雑なのは、Mac ノートパソコン、Windows ワークステーション、iPhone、iPad、タッチ入力、スクロール ホイール、キーボード、音声入力、圧力感知デバイス、スマートウォッチ、トースター、冷蔵庫など、さまざまなデバイスに対応している点です。これらのデバイスには、一般的なデバイスもあれば、非常に珍しいデバイスもあります。

さまざまなデバイス。
さまざまなデバイス(source)。

優れたユーザー エクスペリエンスを実現するには、ユーザーが誰で、どのようなデバイスを使用しているかを把握する必要があります。マウスとキーボードを使用するデスクトップ ユーザー向けのユーザー インターフェースを作成してスマートフォン ユーザーに提供した場合、そのインターフェースは別の画面サイズと別の入力モード向けに設計されているため、ユーザーは不満を感じることになります。

アプローチには、次の 2 つの極端な側面があります。

  1. すべてのデバイスで動作する 1 つのバージョンをビルドします。デバイスによってデザイン上の考慮事項が異なるため、UX が低下します。

  2. サポートするデバイスごとにバージョンをビルドします。アプリケーションのバージョンが多すぎるため、この方法では時間がかかりすぎます。また、新しいスマートフォンが次に発売されたとき(これはほぼ毎週行われます)、また別のバージョンを作成する必要があります。

ここでは根本的なトレードオフがあります。デバイス カテゴリが多いほど、提供できるユーザー エクスペリエンスは向上しますが、設計、実装、メンテナンスにかかる作業は増えます。

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

考えられる解決策

デバイスをカテゴリに分類し、各カテゴリに最適なエクスペリエンスを設計するという妥協案があります。選択するカテゴリは、商品と対象ユーザーによって異なります。以下は、現在一般的なウェブ対応デバイスを網羅する分類のサンプルです。

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

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

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

さまざまなフォーム ファクタ向けにまったく異なるバージョンを提供するウェブ プロパティは数多くあります。Google 検索や Facebook もそうしています。考慮事項には、パフォーマンス(アセットの取得、ページのレンダリング)と一般的なユーザー エクスペリエンスの両方が含まれます。

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

スマートフォンとタブレットの 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 倍になる可能性がありますが、CSS は同じサイズを報告します。

CSS のピクセルについて補足: モバイルウェブの CSS ピクセルは画面ピクセルとは同じではありません。iOS の Retina デバイスでは、ピクセル密度を 2 倍にする手法が導入されています(例: iPhone 3GS と 4、iPad 2 と 3)。ただし、モバイル Safari の Retina UA は、ウェブの破損を回避するために、引き続き同じデバイス幅を報告します。他のデバイス(Android)で高解像度ディスプレイが採用されるようになり、デバイスの幅に関する同じトリックが使用されています。

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

ただし、この決定を複雑にしているのは、縦向きと横向きの両方のモードを考慮する必要があることです。デバイスの向きを変更するたびにページを再読み込みしたり、追加のスクリプトを読み込んだりする必要はありませんが、ページのレンダリングを変更したい場合があります。

次の図では、正方形は、縦向きと横向きの輪郭を重ねて正方形にした各デバイスの最大サイズを表しています。

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

しきい値を 650px に設定すると、iPhone と Galaxy Nexus は smalltouch に、iPad と Galaxy Tab は「tablet」に分類されます。この場合、Androgynous 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 サーバー:

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

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

device.js のご紹介

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

<head> の上部に、提供するサイトのバージョンを示す検索エンジン向けマークアップ(link rel=alternate)を配置します。

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

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

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

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

ここまで読んで、デバイスタイプごとに 3 つのまったく異なるアプリを作成するように言われていると思うかもしれません。いいえ。コード共有が鍵です。

Backbone や Ember などの MVC のようなフレームワークを使用している場合は、懸念事項の分離の原則、特に UI(ビューレイヤ)をロジック(モデルレイヤ)から分離する必要があることをご存じでしょう。MVC を初めて使用する場合は、MVC に関するリソースJavaScript の MVC をご覧ください。

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

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

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

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

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

versions/(デバイス固有の項目) tablet/ desktop/ phone/(スマートフォン固有のコード) style.css index.html views/ 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) メディアクエリは標準ではありません(moz ベンダー接頭辞の背後で Firefox にのみ実装されています)。ただし、device.js によって(Modernizr.touch のおかげで)正しく処理されます。

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

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

通常は、モバイル バージョンからデスクトップ バージョンへのリンクを指定します。これは簡単に実装できますが、device.js は device GET パラメータでこの機能をサポートしています。

結び

要約すると、レスポンシブ デザインの世界にうまく収まらない、クロスデバイスのシングルページ UI を作成する場合は、次のようにします。

  1. サポートするデバイスクラスのセットと、デバイスをクラスに分類する条件を選択します。
  2. ビューを他のコードベースから分離し、懸念事項を明確に分離して MVC アプリを構築します。
  3. device.js を使用して、クライアントサイドのデバイスクラスを検出します。
  4. 準備ができたら、スクリプトとスタイルシートをデバイスクラスごとに 1 つずつパッケージ化します。
  5. クライアントサイドのリダイレクトのパフォーマンスに問題がある場合は、device.js を放棄し、サーバーサイドの UA 検出に切り替えます。