ポータルのハンズオン: ウェブ上でのシームレスなナビゲーション

提案されている Portals API がナビゲーションの UX を改善する方法について学びます。

Yusuke Utsunomiya
Yusuke Utsunomiya

優れたユーザー エクスペリエンスを提供するには、ページの読み込みを速くすることが重要です。しかし、見落としがちなのがページ遷移です。ユーザーがページ間を移動する際に表示されるものです。

新しいウェブ プラットフォーム API プロポーザルである ポータルは、ユーザーがサイト内を移動する際のエクスペリエンスを効率化することで、この問題を解決することを目的としています。

ポータルの動作を確認:

ポータルによるシームレスな埋め込みとナビゲーション。作成者: Adam Argyle

ポータルで可能になること

シングルページ アプリケーション(SPA)は、優れた遷移を提供しますが、構築の複雑さが増します。マルチページ アプリケーション(MPA)は作成がはるかに簡単ですが、ページ間に空白の画面が表示されます。

ポータルは、MPA の複雑さの低さと SPA のシームレスな遷移という、両方の長所を備えています。埋め込みが可能な点では <iframe> に似ていますが、<iframe> とは異なり、コンテンツに移動するための機能も備わっています。

百聞は一見にしかず: まず、Chrome Dev Summit 2018 で紹介された内容をご覧ください。

従来のナビゲーションでは、ブラウザがリンク先のレンダリングを完了するまで、ユーザーは空白の画面を待つ必要があります。Portals ではユーザーにアニメーションを表示できますが、<portal> はコンテンツを事前レンダリングしてシームレスなナビゲーション エクスペリエンスを実現します。

ポータルを導入する前は、<iframe> を使用して別のページをレンダリングすることができました。ページ内でフレームを移動するアニメーションを追加することもできます。ただし、<iframe> ではコンテンツに移動できません。ポータルは、このギャップを埋めて、興味深いユースケースを可能にします。

ポータルを試す

about://flags から有効にする

Chrome 85 以降のバージョンでポータルをお試しいただくには、試験運用版フラグを有効にします。

  • 同じオリジンのナビゲーションに対して about://flags/#enable-portals フラグを有効にします。
  • クロスオリジン ナビゲーションをテストするには、about://flags/#enable-portals-cross-origin フラグも有効にします。

ポータル テストのこの初期段階では、--user-data-dir コマンドライン フラグを設定して、テストに完全に別のユーザーデータ ディレクトリを使用することもおすすめします。ポータルを有効にしたら、DevTools で新しい HTMLPortalElement が使用されていることを確認します。

HTMLPortalElement が表示されているデベロッパー ツール コンソールのスクリーンショット

ポータルを実装する

基本的な実装例を見ていきましょう。

// Create a portal with the wikipedia page, and embed it
// (like an iframe). You can also use the <portal> tag instead.
portal = document.createElement('portal');
portal.src = 'https://en.wikipedia.org/wiki/World_Wide_Web';
portal.style = '...';
document.body.appendChild(portal);

// When the user touches the preview (embedded portal):
// do fancy animation, e.g. expand …
// and finish by doing the actual transition.
// For the sake of simplicity, this snippet will navigate
// on the `onload` event of the Portals element.
portal.addEventListener('load', (evt) => {
   portal.activate();
});

手順は簡単です。DevTools コンソールでこのコードを試すと、Wikipedia のページが開きます。

プレビュー ポータル スタイルのデモの GIF

Chrome Dev Summit で紹介した、上記のデモと同様の機能を構築したい場合は、次のスニペットが役に立ちます。

// Adding some styles with transitions
const style = document.createElement('style');
style.innerHTML = `
  portal {
    position:fixed;
    width: 100%;
    height: 100%;
    opacity: 0;
    box-shadow: 0 0 20px 10px #999;
    transform: scale(0.4);
    transform-origin: bottom left;
    bottom: 20px;
    left: 20px;
    animation-name: fade-in;
    animation-duration: 1s;
    animation-delay: 2s;
    animation-fill-mode: forwards;
  }
  .portal-transition {
    transition: transform 0.4s;
  }
  @media (prefers-reduced-motion: reduce) {
    .portal-transition {
      transition: transform 0.001s;
    }
  }
  .portal-reveal {
    transform: scale(1.0) translateX(-20px) translateY(20px);
  }
  @keyframes fade-in {
    0%   { opacity: 0; }
    100% { opacity: 1; }
  }
`;
const portal = document.createElement('portal');
// Let's navigate into the WICG Portals spec page
portal.src = 'https://wicg.github.io/portals/';
// Add a class that defines the transition. Consider using
// `prefers-reduced-motion` media query to control the animation.
// https://developers.google.com/web/updates/2019/03/prefers-reduced-motion
portal.classList.add('portal-transition');
portal.addEventListener('click', (evt) => {
  // Animate the portal once user interacts
  portal.classList.add('portal-reveal');
});
portal.addEventListener('transitionend', (evt) => {
  if (evt.propertyName == 'transform') {
    // Activate the portal once the transition has completed
    portal.activate();
  }
});
document.body.append(style, portal);

また、ポータルを使用して特徴検出を行い、ウェブサイトを段階的に拡張することも簡単です。

if ('HTMLPortalElement' in window) {
  // If this is a platform that have Portals...
  const portal = document.createElement('portal');
  ...
}

Portals の使用感をすばやく確認するには、uskay-portals-demo.glitch.me をお試しください。Chrome 85 以降のバージョンでアクセスし、試験運用版フラグをオンにしてください。

  1. プレビューする URL を入力します。
  2. ページは <portal> 要素として埋め込まれます。
  3. プレビューをクリックします。
  4. プレビューはアニメーションの後に有効になります。

ポータルの使用に関するグリッチのデモの使用例の GIF

仕様を確認する

Google は、Web Incubation Community Group(WICG)でポータルの仕様について積極的に議論しています。すぐに使いこなせるように、主なシナリオのいくつかを見てみましょう。以下に、理解しておくべき 3 つの重要な機能について説明します。

  • <portal> 要素: HTML 要素自体。API は非常にシンプルです。これは、src 属性、activate 関数、メッセージ用インターフェース(postMessage)で構成されます。activate は、アクティベーション時にデータを <portal> に渡すオプションの引数です。
  • portalHost インターフェース: portalHost オブジェクトを window オブジェクトに追加します。これにより、ページが <portal> 要素として埋め込まれているかどうかを確認できます。また、ホストにメッセージを返すためのインターフェース(postMessage)も提供します。
  • PortalActivateEvent インターフェース: <portal> が有効化されたときに発生するイベント。adoptPredecessor という便利な関数があり、これを使用して前のページを <portal> 要素として取得できます。これにより、2 つのページ間でシームレスなナビゲーションと合成されたエクスペリエンスを構築できます。

基本的な使用パターン以外にも、ポータルでできることの例とサンプルコードを以下に示します。

<portal> 要素として埋め込む際にスタイルをカスタマイズする

// Detect whether this page is hosted in a portal
if (window.portalHost) {
  // Customize the UI when being embedded as a portal
}

<portal> 要素と portalHost 間のメッセージング

// Send message to the portal element
const portal = document.querySelector('portal');
portal.postMessage({someKey: someValue}, ORIGIN);

// Receive message via window.portalHost
window.portalHost.addEventListener('message', (evt) => {
  const data = evt.data.someKey;
  // handle the event
});

<portal> 要素を有効にして portalactivate イベントを受信する

// You can optionally add data to the argument of the activate function
portal.activate({data: {somekey: 'somevalue'}});

// The portal content will receive the portalactivate event
// when the activate happens
window.addEventListener('portalactivate', (evt) => {
  // Data available as evt.data
  const data = evt.data;
});

前任者の取得

// Listen to the portalactivate event
window.addEventListener('portalactivate', (evt) => {
  // ... and creatively use the predecessor
  const portal = evt.adoptPredecessor();
  document.querySelector('someElm').appendChild(portal);
});

ページが前任者として採用されたことを知る

// The activate function returns a Promise.
// When the promise resolves, it means that the portal has been activated.
// If this document was adopted by it, then window.portalHost will exist.
portal.activate().then(() => {
  // Check if this document was adopted into a portal element.
  if (window.portalHost) {
    // You can start communicating with the portal element
    // i.e. listen to messages
    window.portalHost.addEventListener('message', (evt) => {
      // handle the event
    });
  }
});

ポータルでサポートされているすべての機能を組み合わせることで、洗練されたユーザー エクスペリエンスを構築できます。たとえば、次のデモは、ポータルでウェブサイトとサードパーティの埋め込みコンテンツをシームレスに連携させる方法を示しています。

ユースケースとプラン

ポータルについての簡単なガイドがお役に立てば幸いです。ぜひお試しください。たとえば、ポータルは、商品カテゴリのリスト ページからベストセラー商品のページを事前レンダリングするなど、重要なナビゲーションに使い始めることができます。

また、Portal は <iframe> と同様に、クロスオリジン ナビゲーションで使用できます。相互参照する複数のウェブサイトがある場合は、ポータルを使用して 2 つの異なるウェブサイトをシームレスに移動することもできます。このクロスオリジンのユースケースはポータルに固有のものであり、SPA のユーザー エクスペリエンスの向上にもつながります。

フィードバックをお待ちしています

ポータルは、Chrome 85 以降のバージョンでテストできます。新しい API の設計にはコミュニティからのフィードバックが不可欠です。ぜひお試しいただき、ご意見をお寄せください。機能のリクエストやフィードバックがある場合は、WICG GitHub リポジトリにアクセスしてください。