HTML5 サイトをモバイル対応にする

はじめに

モバイルウェブ向けの開発は、最近のホットなトピックです。今年、初めてスマートフォンの販売台数が PC を上回りました。モバイル デバイスを使用してウェブを閲覧するユーザーが増えているため、デベロッパーにとって、モバイル ブラウザ向けにサイトを最適化することが重要になっています。

「モバイル」という戦場は、多くのデベロッパーにとって未開拓の領域です。 多くのユーザーは、モバイル ユーザーを完全に無視した既存のレガシー サイトを所有しています。代わりに、サイトは主にパソコンのブラウザでのブラウジング用に設計されており、モバイル ブラウザでは適切に表示されません。このサイト(html5rocks.com)も例外ではありません。立ち上げ当初は、サイトのモバイル版にはあまり力を入れていませんでした。

モバイル フレンドリーな html5rocks.com の作成

今回は、html5rocks(既存の HTML5 サイト)をモバイル対応バージョンに拡張する演習を行います。主に、スマートフォンをターゲットにするために必要な最小限の作業量に関心がありました。この演習の目的は、まったく新しいモバイルサイトを作成して 2 つのコードベースを維持することではありませんでした。これでは時間がいくらあっても足りず、時間の無駄です。サイトの構造(マークアップ)はすでに定義されています。ルック&フィール(CSS)がありました。コア機能(JS)は存在していました。つまり、多くのサイトが同じ状況にあります。

この記事では、Android デバイスと iOS デバイス向けに最適化された html5rocks のモバイル版を作成した方法について説明します。これらの OS のいずれかをサポートするデバイスで html5rocks.com を読み込むだけで、違いを確認できます。 m.html5rocks.com へのリダイレクトや、そのような類の不適切な処理は行っていません。html5rocks をそのまま利用できるだけでなく、モバイル デバイスでも見栄えが良く、適切に動作するというメリットもあります。

デスクトップ html5rocks.com モバイル html5rocks.com
パソコン(左)とモバイル(右)での html5rocks.com

CSS メディアクエリ

HTML4 と CSS2 は、以前からメディア依存のスタイルシートをサポートしています。次に例を示します。

<link rel="stylesheet" media="print" href="printer.css">

は印刷デバイスをターゲットとし、印刷時にページ コンテンツに特定のスタイルを適用します。CSS3 では、メディアタイプのアイデアをさらに一歩進め、メディアクエリで機能を強化しています。メディアクエリは、スタイルシートのラベル付けをより正確に行えるようにすることで、メディアタイプの有用性を高めます。これにより、コンテンツ自体を変更することなく、特定の範囲の出力デバイスに合わせてコンテンツの表示をカスタマイズできます。変更が必要な既存のレイアウトに ぴったりですね

外部スタイルシートの media 属性でメディアクエリを使用すると、画面幅、デバイス幅、向きなどをターゲットに設定できます。詳細については、W3C メディアクエリ仕様をご覧ください。

画面サイズをターゲットに設定する

次の例では、phone.css は、ブラウザが「ハンドヘルド」と見なすデバイス、または画面幅が 320 ピクセル以下のデバイスに適用されます。

 <link rel='stylesheet'
  media='handheld, only screen and (max-device-width: 320px)' href='phone.css'>

メディアクエリの先頭に「only」キーワードを付けると、CSS3 に準拠していないブラウザではルールが無視されます。

次の例では、641 ~ 800 ピクセルの画面サイズをターゲットにしています。

 <link rel='stylesheet'
  media='only screen and (min-width: 641px) and (max-width: 800px)' href='ipad.css'>

メディアクエリは、インライン <style> タグ内に表示されることもあります。次の例では、縦向きの場合に all メディアタイプをターゲットにしています。

 <style>
  @media only all and (orientation: portrait) { ... }
 </style>

media="handheld"

ここで少し時間を取って、media="handheld" について話し合う必要があります。 実際、Android と iOS は media="handheld" を無視します。media="screen" をターゲットとするスタイルシートによって提供されるハイエンド コンテンツをユーザーが利用できなくなり、デベロッパーが低品質の media="handheld" バージョンを維持する可能性が低くなるという主張です。そのため、「フルウェブ」をモットーとする最新のスマートフォン ブラウザのほとんどは、ハンドヘルド スタイルシートを無視します。

この機能を使用してモバイル デバイスをターゲットに設定するのが理想的ですが、さまざまなブラウザでさまざまな方法で実装されています。

  • 一部のブラウザは、ハンドヘルド スタイルシートのみを読み取ります。
  • ハンドヘルド スタイルシートがあればそれを読み取り、なければデフォルトで画面スタイルシートを読み取るものもあります。
  • 一部のブラウザは、ハンドヘルド スタイルシートと画面スタイルシートの両方を読み取ります。
  • 画面スタイルシートのみを読み取るものもあります。

Opera Mini は media="handheld" を無視しません。Windows Mobile で media="handheld" を認識させるには、画面スタイルシートの media 属性値を大文字にします。

 <!-- media="handheld" trick for Windows Mobile -->
 <link rel="stylesheet" href="screen.css" media="Screen">
 <link rel="stylesheet" href="mobile.css" media="handheld">

html5rocks でのメディアクエリの使用方法

メディアクエリは、モバイル版 html5rocks 全体で多用されています。Django テンプレートのマークアップを大幅に変更することなく、レイアウトを調整できました。本当に助かりました。また、さまざまなブラウザでのサポートも充実しています。

各ページの <head> には、次のスタイルシートが表示されます。

 <link rel='stylesheet'
  media='all' href='/static/css/base.min.css' />
 <link rel='stylesheet'
  media='only screen and (max-width: 800px)' href='/static/css/mobile.min.css' />

base.css は、html5rocks.com のメインのルック アンド フィールを定義していましたが、現在は、画面幅が 800 ピクセル未満の場合に新しいスタイル(mobile.css)を適用しています。メディアクエリは、スマートフォン(約 320 ピクセル)と iPad(約 768 ピクセル)に対応しています。 効果: base.css のスタイルを段階的にオーバーライドして(必要な場合のみ)、モバイルでの表示を改善しています。

mobile.css が適用するスタイル変更の一部を以下に示します。

  • サイト全体の余分な空白 / パディングを減らします。画面が小さいということは、スペースが貴重であるということです。
  • :hover 状態を削除します。タッチデバイスには表示されません。
  • レイアウトを 1 列に調整します。詳細については後述します。
  • サイトのメインコンテナ div の周囲の box-shadow を削除します。大きなボックス シャドウは、ページのパフォーマンスを低下させます。
  • CSS フレックス ボックス モデル box-ordinal-group を使用して、ホームページの各セクションの順序を変更しました。 ホームページでは「LEARN BY MAJOR HTML5 FEATURE GROUPS」が「TUTORIALS」セクションの前に表示されますが、モバイル版では後に表示されます。この順序はモバイルに適しており、マークアップの変更は必要ありませんでした。CSS フレックスボックス
  • opacity の変更を削除します。アルファ値を変更すると、モバイルでのパフォーマンスが低下します。

モバイル メタタグ

モバイル WebKit は、特定のデバイスでユーザーのブラウジング エクスペリエンスを向上させるいくつかの機能をサポートしています。

ビューポートの設定

最初のメタ設定(最もよく使用する設定)は、ビューポート プロパティです。 ビューポートを設定すると、コンテンツをデバイスの画面に合わせる方法がブラウザに指示され、サイトがモバイル向けに最適化されていることがブラウザに通知されます。次に例を示します。

 <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes">

は、ビューポートをデバイスの幅に設定し、初期スケールを 1 にするようブラウザに指示します。この例では、ズームも許可されています。これはウェブサイトでは望ましいかもしれませんが、ウェブアプリでは望ましくない場合があります。user-scalable=no を使用してズームを禁止したり、スケーリングを特定のレベルに制限したりできます。

 <meta name=viewport
  content="width=device-width, initial-scale=1.0, minimum-scale=0.5 maximum-scale=1.0">

Android では、デベロッパーがサイトの開発対象となる画面解像度を指定できるようにすることで、ビューポート メタタグを拡張しています。

 <meta name="viewport" content="target-densitydpi=device-dpi">

target-densitydpi に指定できる値は、device-dpi、high-dpi、medium-dpi、low-dpi です。

さまざまな画面密度に合わせてウェブページを変更する場合は、-webkit-device-pixel-ratio CSS メディアクエリや JavaScript の window.devicePixelRatio プロパティを使用して、target-densitydpi メタプロパティを device-dpi に設定します。これにより、Android がウェブページのスケーリングを行わなくなり、CSS と JavaScript を使用して、密度ごとに必要な調整を行うことができます。

対象デバイスの解像度をターゲットに設定する方法について詳しくは、Android の WebView のドキュメントをご覧ください。

全画面表示

iOS 固有のメタ値が他に 2 つあります。apple-mobile-web-app-capable と apple-mobile-web-app-status-bar-style は、アプリのような全画面モードでページ コンテンツをレンダリングし、ステータスバーを半透明にします。

 <meta name="apple-mobile-web-app-capable" content="yes">
 <meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">

使用可能なすべてのメタ オプションの詳細については、Safari のリファレンス ドキュメントをご覧ください。

ホーム画面のアイコン

iOS および Android デバイスでは、リンクにrel="apple-touch-icon"(iOS)とrel="apple-touch-icon-precomposed"(Android)も使用できます。これらにより、ユーザーがサイトをブックマークしたときに、ユーザーのホーム画面にアプリのような派手なアイコンが作成されます。

 <link rel="apple-touch-icon"
      href="/static/images/identity/HTML5_Badge_64.png" />
 <link rel="apple-touch-icon-precomposed"
      href="/static/images/identity/HTML5_Badge_64.png" />

html5rocks でのモバイル メタタグの使用方法

すべてをまとめると、html5rocks の <head> セクションのスニペットは次のようになります。

 <head>
  ...
   <meta name="viewport"
        content="width=device-width, initial-scale=1.0, minimum-scale=1.0" />

   <link rel="apple-touch-icon"
        href="/static/images/identity/HTML5_Badge_64.png" />
   <link rel="apple-touch-icon-precomposed"
        href="/static/images/identity/HTML5_Badge_64.png" />
  ...
 </head>

縦方向のレイアウト

小さな画面では、横方向よりも縦方向にスクロールする方がはるかに便利です。モバイルでは、コンテンツを 1 列の縦型レイアウトにすることをおすすめします。 html5rocks では、CSS3 メディアクエリを使用してこのようなレイアウトを作成しました。マークアップを変更する必要はありません。

チュートリアル インデックス。 チュートリアル。 HTML5 機能ページ。 著者プロフィール ページ。
サイト全体で 1 列の縦型レイアウトを使用する。

モバイルの最適化

行った最適化のほとんどは、最初から行うべきだったことです。たとえば、ネットワーク リクエスト数の削減、JS / CSS の圧縮、gzip 圧縮(App Engine では無料)、DOM 操作の最小化などです。これらの手法は一般的なベスト プラクティスですが、サイトを急いで公開する際に見落とされることがあります。

アドレスバーを自動的に非表示にする

モバイル ブラウザには、パソコン版のような画面領域がありません。さらに悪いことに、プラットフォームによっては、ページの読み込みが完了した後も、画面の上部に大きなアドレスバーが表示されることがあります。

これに対処する簡単な方法の一つは、JavaScript を使用してページをスクロールすることです。1 ピクセルでもスクロールすれば、アドレスバーは表示されなくなります。 html5rocks でアドレスバーを強制的に非表示にするには、onload イベント ハンドラを window オブジェクトにアタッチし、ページを垂直方向に 1 ピクセル スクロールします。

アドレスバー。
見栄えの悪いアドレスバーが画面のスペースを占有している。
  // Hides mobile browser's address bar when page is done loading.
  window.addEventListener('load', function(e) {
    setTimeout(function() { window.scrollTo(0, 1); }, 1);
  }, false);

また、このリスナーはパソコンでは不要なため、is_mobile テンプレート変数でラップしました。

ネットワーク リクエストを減らし、帯域幅を節約する

HTTP リクエストの数を減らすと、パフォーマンスが大幅に向上することはよく知られています。モバイル デバイスでは、ブラウザが確立できる同時接続数がさらに制限されるため、モバイルサイトでは、これらの余分なリクエストを減らすことでさらに大きなメリットが得られます。さらに、スマートフォンでは帯域幅が制限されることが多いため、1 バイトでも削減することが重要です。ユーザーに費用がかかる可能性があります。

html5rocks でネットワーク リクエストを最小限に抑え、帯域幅を削減するために採用したアプローチをいくつか紹介します。

  • iframe を削除する - iframe は処理が遅くなります。レイテンシの大部分は、チュートリアル ページにあるサードパーティの共有ウィジェット(Buzz、Google Friend Connect、Twitter、Facebook)に起因していました。これらの API は <script> タグを介して含まれており、ページの速度を低下させる iframe を作成します。ウィジェットはモバイル向けに削除されました。

  • display:none - モバイル プロファイルに適合しない場合、マークアップが非表示になることがありました。良い例は、ホームページの上部にある 4 つの角丸ボックスです。

ホームページのボックスボタン。
ホームページのボックスボタン。

モバイルサイトに表示されていません。コンテナが display:none で非表示になっているにもかかわらず、ブラウザは各アイコンのリクエストを送信することに注意してください。そのため、これらのボタンを非表示にするだけでは不十分でした。これは帯域幅を浪費するだけでなく、ユーザーは浪費された帯域幅の成果を見ることもできません。解決策は、Django テンプレートに「is_mobile」ブール値を作成して、HTML のセクションを条件付きで省略することでした。ユーザーがスマート デバイスでサイトを閲覧している場合、ボタンは表示されません。

  • アプリケーション キャッシュ - これにより、オフライン サポートが提供されるだけでなく、起動も高速化されます。

  • CSS / JS 圧縮 - Closure Compiler ではなく YUI Compressor を使用しています。これは主に、CSS と JS の両方を処理できるためです。発生した問題の 1 つは、インライン メディアクエリ(スタイルシート内に表示されるメディアクエリ)が YUI Compressor 2.4.2 でエラーになったことです(こちらの問題をご覧ください)。YUI Compressor 2.4.4 以降を使用することで、この問題は解決しました。

  • 可能な場合は CSS 画像スプライトを使用する。

  • 画像圧縮に pngcrush を使用しました。

  • 小さな画像に dataURI を使用しました。Base64 エンコードにより、画像のサイズが 30%以上増加しますが、ネットワーク リクエストを節約できます。

  • google.load() を使用して動的に読み込むのではなく、単一のスクリプトタグを使用して Google カスタム検索を自動読み込みします。後者は追加のリクエストを行います。

<script src="//www.google.com/jsapi?autoload={"modules":[{"name":"search","version":"1"}]}"> </script>
  • コードのプリティ プリントと Modernizr は、使用されていなくてもすべてのページに含まれていました。Modernizr は優れていますが、読み込みのたびに多数のテストが実行されます。これらのテストの一部は、DOM にコストのかかる変更を加え、ページの読み込みを遅くします。現在、これらのライブラリは、実際に必要なページにのみ含まれています。-2 件のリクエスト :)

パフォーマンスをさらに調整する:

  • すべての JS をページの下部に移動しました(可能な場合)。
  • インラインの <style> タグを削除しました。
  • DOM ルックアップのキャッシュと DOM 操作の最小化 - DOM にアクセスするたびに、ブラウザはリフローを実行します。モバイル デバイスでは、リフローのコストがさらに高くなります。
  • クライアントサイドの無駄なコードをサーバーにオフロードしました。具体的には、現在のページのナビゲーション スタイルを設定するチェックです。 js var lis = document.querySelectorAll('header nav li'); var i = lis.length; while (i--) { var a = lis[i].querySelector('a'); var section = a.getAttribute("data-section"); if (new RegExp(section).test(document.location.href)) { a.className = 'current'; } }
  • 幅が固定された要素は、フルードの width:100% または width:auto に置き換えられました。

アプリケーションのキャッシュ

モバイル版の html5rocks では、アプリケーション キャッシュを使用して初期読み込みを高速化し、ユーザーがオフラインでコンテンツを読めるようにしています。

サイトに AppCache を実装する際は、マニフェスト ファイルをキャッシュしないことが非常に重要です(マニフェスト ファイル自体で明示的にキャッシュしない、または強力なキャッシュ制御ヘッダーで暗黙的にキャッシュしない)。マニフェストがブラウザによってキャッシュされると、デバッグが非常に困難になります。iOS と Android は、このファイルをキャッシュする処理に特に優れていますが、パソコンのブラウザのようにキャッシュをフラッシュする便利な方法を提供していません。

サイトでこのようなキャッシュが行われないようにするため、まず、App Engine でマニフェスト ファイルをキャッシュしないように設定します。

- url: /(.*\.(appcache|manifest))
  static_files: \1
  mime_type: text/cache-manifest
  upload: (.*\.(appcache|manifest))
  expiration: "0s"

次に、JS API を使用して、新しいマニフェストのダウンロードが完了したときにユーザーに通知しました。ページを更新するよう求められます。

window.applicationCache.addEventListener('updateready', function(e) {
  if (window.applicationCache.status == window.applicationCache.UPDATEREADY) {
    window.applicationCache.swapCache();
    if (confirm('A new version of this site is available. Load it?')) {
      window.location.reload();
    }
  }
}, false);

ネットワーク トラフィックを節約するには、マニフェストをシンプルに保ちます。つまり、サイトのすべてのページを呼び出さないでください。重要な画像、CSS、JavaScript ファイルのみをリストします。appcache の更新のたびに、モバイル ブラウザに大量のアセットをダウンロードさせることは避けたいものです。代わりに、ユーザーがアクセスしたときにブラウザが HTML ページを暗黙的にキャッシュすること(<html manifest="..."> 属性を含む)に注意してください。