AMP HTML のカスタム JavaScript

今回は AMP HTML で独自の JavaScript を実装する方法についてです。

AMP はモバイルページを高速表示するプロジェクトやフレームワークです。

AMP では高速表示を実現しつつ様々な表現ができるよう、JavaScript は AMP 専用のコンポーネントで実装します。基本はコンポーネントの処理で動きを実装しますが、特定の制限下であれば独自のカスタム JavaScript を動かすことも可能です。AMP そのものの詳しい仕組みは下記の記事で紹介しています。

AMP | fff

AMPはモバイルページを高速表示する技術です。AMPの特徴や高速表示の仕組み、AMPを実装する方法やルールを紹介します。

カスタム JavaScript

AMP で動きのある表現をする場合は AMP で用意された専用のコンポーネントを利用します。それらのコンポーネントの中には独自の JavaScript を実行できるamp-scriptコンポーネントがあります。用意されたコンポーネントでできない処理もカスタム JavaScript を使うことで実装することができるようになります。

<!DOCTYPE html>
<html ⚡ lang="ja">
<head>
  <script async custom-element="amp-script" src="https://cdn.ampproject.org/v0/amp-script-0.1.js"></script>
</head>
<body>
  <amp-script src="sample.js">
    <p>Hello, world!</p>
  </amp-script>
</body>
</html>

amp-scriptは JavaScript ファイルを読み込み、メインページとは別スレッドで動く Web Worker を登録し実行します。

Web Worker は Worker 内から直接 DOM を操作することができません。amp-scriptで実行される Web Worker には amp-scriptの自身の DOM のコピーが渡されます。メインスレッドと Web Worker スレッドはメッセージシステムでデータ送信をし、メインスレッドの DOM に変更が発生した場合は Worker 内の DOM に反映し、逆に Worker 内で DOM に変更があればメインスレッドの DOM に反映します。
Web Worker に渡されるスコープは<amp-script></amp-script>で囲んだ範囲の DOM の為、Worker 内のdocument.bodyはメインスレッドのamp-script要素になります。

その他にカスタム JavaScript は各ページ 150KB の制限があるので注意してください。

カスタム JavaScript の実装方法

AMP でカスタム JavaScript を実装する方法をサンプルを使って説明します。今回は FizzBuzz と呼ばれるゲームに近い処理を JavaScript で実装します。

FizzBuzz

数字を入力してください。
3の倍数の時は Fizz、5の倍数の時は Buzz、15の倍数の時は FizzBuzz を返します。

メインページの AMP HTML と Web Worker で実行する JavaScript ファイルを実装します。

カスタム JavaScript の実装にはamp-scriptコンポーネントを使います。head要素内で下記のようにコンポーネントを読み込みます。

<script async custom-element="amp-script" src="https://cdn.ampproject.org/v0/amp-script-0.1.js"></script>

メインの HTML は下記のコードです。

<amp-script src="sample.js">
  <p class="sample_title">FizzBuzz</p>
  <p class="sample_desc">数字を入力してください。<br>3の倍数の時は Fizz、5の倍数の時は Buzz、15の倍数の時は FizzBuzz を返します。</p>
  <input class="sample_input" type="number" inputmode="numeric">
  <output class="sample_output"></output>
</amp-script>

Worker 内で操作したい DOM を<amp-script></amp-script>で囲みます。囲んだ範囲は Worker に DOM のコピーが渡され Worker 内から操作が可能です。また Web Worker として登録し実行したい JavaScript ファイルの URL をamp-script要素のsrc属性に指定します。

続いてamp-scriptが Web Worker で実行する sample.js を実装します。今回のサンプルでは下記のコードで実装しています。

const input  = document.querySelector('.sample_input');
const output = document.querySelector('.sample_output');
if (typeof input !== 'undefined' && typeof output !== 'undefined') {
  input.addEventListener('blur', function() {
    let value = input.value;
    let result;

    if (value == '') {
      result = '';
    } else if (value % 15 == 0) {
      result = 'FizzBuzz';
    } else if (value % 3 == 0) {
      result = 'Fizz';
    } else if (value % 5 == 0) {
      result = 'Buzz';
    } else {
      result = value;
    }   

    output.textContent = result;
  }); 
}

上記の JavaScript ではamp-script要素内のinput要素の値を取得しoutput要素に FizzBuzz の結果を格納しています。このコードには AMP 特有の処理は含まれておらず、通常のページと同じ JavaScript で動かしています。ただし、AMP では使えない処理もあるので注意が必要です。

amp-scriptコンポーネントを使うことで制約はあるものの AMP でも独自のカスタム JavaScript を実装できるようになります。

AMP キャッシュページと CORS

AMP で JavaScript を使う場合は CORS の設定も必要になります。CORS はCross-Origin Resource Sharingというオリジン間リソース共有の仕組みです。

JavaScript は通常別のオリジン (ホスト) のリソースにアクセスすることができません。これは同一オリジンポリシーというポリシーで CSRF のようなセキュリティ攻撃を防ぐ為に、ブラウザは別オリジンへのリクエストのレスポンスを JavaScript からアクセスできないようにします。

AMP は高速化の仕組みとして Google AMP Cache というキャッシュページを生成します。キャッシュページは Google のサーバーに生成されるので AMP 対応ページとはオリジンが異なり、同一オリジンポリシーにより JavaScript を扱うことができません。その為、AMP で JavaScript を利用する時はキャッシュページからも利用できるように CORS を許可する必要があります。

今回は .htaccess で CORS の設定をします。下記は特定のオリジンのみ CORS を許可する記述です。

SetEnvIf Origin "^https?://(www\.)?(fff-create\.com|www-fff--create-com\.cdn\.ampproject\.org)" ALLOWED_ORIGIN=$0
Header set Access-Control-Allow-Origin %{ALLOWED_ORIGIN}e env=ALLOWED_ORIGIN

1行目は CORS を許可するオリジンを正規表現で指定しています。詳細な説明は割愛しますがアクセスされたオリジンがfff-create.comもしくはwww-fff--create-com.cdn.ampproject.orgであるかを判定しています。
2行目は1行目でマッチしたオリジンに対して CORS の許可を設定します。

許可する対象のオリジンを変更する場合は(fff-create\.com|www-fff--create-com\.cdn\.ampproject\.org)の正規表現を変更してください。.は正規表現では特別な意味を持つメタ文字なのでエスケープした\.と記述します。Google AMP Cache の URL は下記リンクの URL 換算式で知ることができます。

AMP キャッシュ URL 形式とリクエスト処理

CORS を設定しないと自身で用意したオリジンのサイトからは動きますが、検索結果から遷移したキャッシュページでは JavaScript が動かない事態になりかねないので注意が必要です。