document.write() に対する介入

最近、Chrome の Developer Console に次のような警告が表示されました。これは何だろうと思ったことはありませんか?

(index):34 A Parser-blocking, cross-origin script,
https://paul.kinlan.me/ad-inject.js, is invoked via document.write().
This may be blocked by the browser if the device has poor network connectivity.

コンポーザビリティはウェブの強みの一つです。これにより、サードパーティが構築したサービスと簡単に統合して、優れた新プロダクトを構築できます。コンポーザビリティの欠点の一つは、ユーザー エクスペリエンスに対する責任の共有が示唆されることです。統合が最適でない場合、ユーザー エクスペリエンスに悪影響が及びます。

パフォーマンス低下の原因の 1 つとして、ページ内での document.write() の使用、特にスクリプトを挿入することがあります。一見すると無害ですが、ユーザーに大きな問題を引き起こす可能性があります。

document.write('<script src="https://example.com/ad-inject.js"></script>');

ブラウザは、ページをレンダリングする前に、HTML マークアップを解析して DOM ツリーを構築する必要があります。パーサーがスクリプトを検出すると、HTML の解析を続行する前にスクリプトを停止して実行する必要があります。スクリプトが別のスクリプトを動的に挿入する場合、パーサーはリソースのダウンロードにさらに時間がかかることを余儀なくされます。そのため、1 つ以上のネットワーク ラウンドトリップが発生し、ページの最初のレンダリングが遅れる可能性があります。

2G などの低速の接続を利用するユーザーの場合、document.write() を介して動的に挿入される外部スクリプトにより、メインページのコンテンツの表示が数十秒遅れたり、ページの読み込みに失敗したり、ユーザーが長時間利用を止めたりする可能性があります。Chrome のインストルメンテーションによると、document.write() を介して挿入されたサードパーティ スクリプトを掲載しているページは、2G の場合、他のページよりも読み込みが通常 2 倍遅くなることがわかっています。

Google は、Chrome の安定版ユーザーの 1% を対象として、2G 接続のユーザーに限定した 28 日間のフィールド トライアルからデータを収集しました。2G の全ページ読み込みの 7.6% に、トップレベルのドキュメントの document.write() を介して挿入された、少なくとも 1 つのクロスサイトのパーサー ブロック スクリプトが含まれていました。これらのスクリプトの読み込みをブロックした結果、これらの読み込みに関して次のような改善が見られました。

  • First Contentful Paint(ページを効果的に読み込んでいることをユーザーに視覚的に確認する情報)に達するページ読み込みが 10% 増加し、完全に解析された状態に到達するページ読み込みが 25% 増加し、再読み込み数 10% が減少したことを意味します。
  • First Contentful Paint までの平均時間が 21% 短縮(1 秒以上で短縮)
  • ページの平均解析時間が 38% 短縮され、約 6 秒短縮され、ユーザーにとって重要な情報を表示するまでの時間が大幅に短縮されました。

こうしたデータを踏まえ、Chrome のバージョン 55 以降では、既知の不正なパターンを検出した場合、Chrome での document.write() の処理方法を変更して、すべてのユーザーに代わって介入します(Chrome のステータスをご覧ください)。具体的には、次の条件がすべて満たされている場合、document.write() を介して挿入された <script> 要素は Chrome で実行されません。

  1. お客様の接続速度が遅い(特に 2G を利用している場合)。(将来的には、3G や Wi-Fi など、接続速度が遅い他のユーザーにもこの変更が適用される可能性があります)。
  2. document.write() は最上位のドキュメント内にある。iframe 内の document.writing スクリプトには、メインページのレンダリングがブロックされないため、介入は適用されません。
  3. document.write() のスクリプトはパーサー ブロックです。async 属性または defer 属性を持つスクリプトは引き続き実行されます。
  4. スクリプトが同じサイトでホストされていない。つまり、Chrome は eTLD+1 が一致するスクリプト(例: js.example.org でホストされているスクリプトが www.example.org に挿入される)には介入しません。
  5. スクリプトがブラウザの HTTP キャッシュにまだない場合。キャッシュ内のスクリプトはネットワーク遅延を発生させず、引き続き実行されます。
  6. ページのリクエストは再読み込みではありません。ユーザーが再読み込みを行っても Chrome は介入せず、ページを通常どおり実行します。

サードパーティ スニペットは、スクリプトを読み込むために document.write() を使用することがあります。幸いなことに、ほとんどのサードパーティは非同期読み込みの代替手段を提供しています。これにより、ページ上の残りのコンテンツの表示をブロックすることなく、サードパーティのスクリプトを読み込むことができます。

どうすればよいですか?

シンプルな答えは、document.write() を使用してスクリプトを挿入しないことです。非同期ローダをサポートする既知のサービスがいくつか用意されているため、確認することをおすすめします。

ご利用のプロバイダが一覧に記載されておらず、非同期のスクリプト読み込みをサポートしている場合は、Google までご連絡ください。すべてのユーザーをサポートできるようにページを更新します。

ページにスクリプトを非同期で読み込む機能をプロバイダでサポートされていない場合は、プロバイダに連絡して、どのような影響を受けるかを Google に知らせていただくことをおすすめします。

プロバイダから document.write() を含むスニペットが提供されている場合は、スクリプト要素に async 属性を追加するか、document.appendChild()parentNode.insertBefore() などの DOM API を使用してスクリプト要素を追加できる場合があります。

サイトが影響を受けるタイミングを検出する方法

制限が適用されるかどうかを判断する基準は多数ありますが、影響を受けるかどうかを知るにはどうすればよいでしょうか。

ユーザーが 2G を利用しているかどうかを検出する

この変更による影響を把握するには、まず 2G を利用するユーザーの数を把握する必要があります。Chrome で利用可能な Network Information API を使用して、ユーザーの現在のネットワークの種類と速度を検出し、分析システムまたはリアルユーザー指標(RUM)システムにその通知を送信できます。

if(navigator.connection &&
    navigator.connection.type === 'cellular' &&
    navigator.connection.downlinkMax <= 0.115) {
    // Notify your service to indicate that you might be affected by this restriction.
}

Chrome DevTools で警告を検出する

Chrome 53 以降、DevTools では、問題のある document.write() ステートメントに対して警告が表示されます。具体的には、document.write() リクエストが条件 2 ~ 5 を満たしている場合(Chrome はこの警告を送信する際に接続条件を無視します)、次のような警告が表示されます。

ドキュメント書き込み警告。

Chrome DevTools で警告が表示されるのは良いことですが、どうすれば大規模に検出できるのでしょうか。介入の発生時にサーバーに送信される HTTP ヘッダーを確認できます。

スクリプト リソースの HTTP ヘッダーを確認する

document.write を介して挿入されたスクリプトがブロックされた場合、Chrome は次のヘッダーをリクエストされたリソースに送信します。

Intervention: <https://shorturl/relevant/spec>;

document.write を介して挿入されたスクリプトが検出され、さまざまな状況でブロックされる可能性がある場合、Chrome から次のメッセージが送信されることがあります。

Intervention: <https://shorturl/relevant/spec>; level="warning"

介入ヘッダーは、スクリプトの GET リクエストの一部として(実際の介入の場合は非同期で)送信されます。

未来に待ち受けているものとは

初期の計画では、満たされている条件が検出されたときにこの介入を実行します。Chrome 53 では、Play Console に警告のみを表示することから始めました。 (ベータ版は 2016 年 7 月にリリースされました。Stable は 2016 年 9 月にすべてのユーザーが利用できるようになる予定です)。

Google は、2016 年 10 月中旬にすべてのユーザーに安定版がリリースされると予想される Chrome 54 から、2G ユーザーに対して挿入されたスクリプトのブロックに一時的に介入します。最新情報については、Chrome のステータスのエントリをご覧ください。

今後、ユーザーの接続速度が遅い場合(3G や Wi-Fi など)に介入することを目指しています。こちらの Chrome のステータスをご覧ください。

もっと詳しくお知りになりたい場合は、

詳細については、以下の参考情報をご覧ください。