メインコンテンツへスキップ
見出し画像
Photo byimasaranotami

【第617回】Personalization:Affinity に合わせてポップアップを表示する

    これまでの記事では、TrailNest の顧客の興味を Affinity で数値化し、Web サイトの Hero Banner やおすすめ商品、メールの表示 に活用してきました。

    今回は、同じ Affinity を使って、顧客の興味に合わせたポップアップを表示します。

    ポップアップの中身は、商品カードだけに限りません。

    今回は 3 つのカテゴリを対象に、次のように表示形式を変えます。

    • Backpacks:画像を中心としたバナー

    • Tents:YouTube の動画

    • Apparel:画像・見出し・説明文・ボタンを組み合わせた HTML

    一方で、ポップアップは表示しすぎると、商品の閲覧を妨げてしまいます。

    そこで今回は、表示内容だけでなく、どのページで、いつ、どのくらいの頻度で表示するかも設定します。


    Personalization 基礎シリーズ

    Sitemap Builder シリーズ

    Data 360 設定シリーズ

    Personalization 設定シリーズ


    今回の表示ルール

    今回のポップアップは、次の条件で表示します。

    • 表示場所:商品詳細ページ

    • 表示する環境:画面幅が 1,024px 以上で、マウスなどの操作に対応する環境

    • 表示タイミング:ページのスクロール可能な範囲を 50% 以上進んだとき

    • 表示頻度:同じブラウザでは、表示から 24 時間に 1 回まで

    • 閉じ方:右上の ×、背景のクリック、または Esc キー

    トップページには、すでに Hero Banner やチャットのスタータープロンプトがあります。そのため、今回のポップアップは商品詳細ページに限定します。

    また、モバイルでは画面が狭く、チャットやカートの操作とも重なりやすいため、表示しません。

    今回は端末名で PC・スマートフォンを判定するのではなく、画面幅とポインターの特性で判定します。PC でもブラウザの幅が狭い場合は表示されません。

    別の商品ページへ移動したら、また表示される?

    表示から 24 時間以内は、別の商品詳細ページへ移動しても表示しません。

    例えば、Backpacks のポップアップを表示した後に Apparel の商品ページへ移動しても、すぐに別のポップアップが出ることはありません。

    3 カテゴリで共通の表示履歴を使用するためです。

    閉じた時刻ではなく、表示した時刻を記録します。ポップアップを閉じずに別のページへ移動した場合も、連続表示を抑えられます。


    閲覧中の商品ではなく、顧客の興味に合わせる

    今回、表示内容を決めるのは、閲覧中の商品カテゴリではありません。

    対象顧客の raw_affinity_score が最も高いカテゴリを使用します。

    例えば、Tents の商品詳細ページを開いていても、その顧客が最も興味を持っているカテゴリが Backpacks なら、Backpacks のポップアップを表示します。

    今回の対象は、次の 3 カテゴリです。

    • backpacks

    • tents

    • apparel

    Sleeping、Footwear、Accessories が最高スコアの場合や、Affinity のデータがない場合には、ポップアップを表示しない構成にします。


    WPM と CloudPages の役割を分ける

    WPM には、標準で Add an Overlay という表示方法があり、スクロール量などをきっかけにコンテンツを表示できます。

    今回は、ページをまたいだ 24 時間の表示制限や、動画を閉じたときの停止処理までまとめて管理するため、次の構成にします。

    • Personalization Decision:顧客の Affinity に応じて、表示するカテゴリを決める

    • Experience Template:画像・動画の配置場所・HTML を用意する

    • WPM:商品詳細ページの専用の場所にコンテンツを差し込む

    • CloudPages:画面幅・スクロール量・表示履歴を確認してポップアップを開く

    WPM の表示方法には、Replace an Element を使用します。

    今回の表示頻度や開閉の処理は、CloudPages に追加するカスタムコードです。WPM の標準設定だけで実現しているものではありません。


    ① Content Schema を作成する

    Personalization アプリで Content Schemas を開き、新規作成します。

    • Personalization Type:Dynamic Content

    • Name:TrailNest Popup Content

    • API Name:TrailNest_Popup_Content

    画像

    属性を 1 つ追加します。

    • Attribute Label:Popup Category

    • API Name:Popup_Category

    画像

    この項目では、backpacks、tents、apparel のような カテゴリコードを返します。画像 URL や HTML 全体を Decision から返すのではなく、表示を選ぶための値だけを返す構成です。


    ② Personalization Point を作成する

    Personalization Points を開き、新規作成します。

    • Profile Data Graph:Real-Time > TrailNest Profile

    • Name:TrailNest Product Popup

    • API Name:TrailNest_Product_Popup

    • Personalization Type:Dynamic Content

    • Content Schema:TrailNest Popup Content(上で作ったもの)

    • Authentication Required:未チェック

    画像

    Hero Banner や商品レコメンドの Point とは分けておくことで、ポップアップの設定を独立して変更できます。


    ③ 最高スコアのカテゴリを返す Decision を作成する

    今回は、1 つの Decision から、顧客の Affinity が最も高いカテゴリを返します。

    以前の Hero Banner の記事と同じく、Merge Field で Category を raw_affinity_score の降順に並べ、先頭のカテゴリを取得する方法です。

    作成した Personalization Point で、Decision を新規作成します。

    • Decision Name:TrailNest Top Category Popup

    • Decision API Name:TrailNest_Top_Category_Popup

    • Decision State:Live

    画像

    Decision Configuration の Popup Category では、カテゴリを直接入力せず、Add Merge Field をクリックします。

    次の内容を設定します。

    • Attribute:計算済みインサイト TrailNest Category Affinity Category の Category

    • Merge Field API Name:Popup_Category

    • Default Text:default(Affinity のデータがなく、カテゴリを取得できなかったときに、代わりに返す文字)

    • SORT BY の Attribute:raw_affinity_score

    • SORT BY の Operator:Descending(降順)

    画像

    保存すると、Popup Category の入力欄に差し込みトークンが設定されます。生成されたトークンは、そのまま残してください。

    次へ進み、ターゲティングルールを設定します。

    • Targeting Rules:Always(条件なし)

    ここでの Always は、「全員に同じカテゴリを返す」という意味ではありません。この Decision を評価対象とし、返すカテゴリは顧客ごとの Affinity から取得するという構成です。

    保存して完了します。

    画像

    対象外のカテゴリや、データがない場合

    今回のポップアップが対応するカテゴリは、次の 3 つです。

    • backpacks

    • tents

    • apparel

    それ以外のカテゴリや、データがなく default が返った場合は、CloudPages の表示コードがポップアップを表示せずに終了します。

    なお、最高スコアが同点の場合は、降順だけでは同点内の順番は確定しません。今回は、そのいずれかのカテゴリが返る構成として扱います。


    ④ Experience Template を作成する

    Experience Templates を開き、新規作成します。

    • Channel:Web

    • Name:TrailNest Category Popup

    • API Name:TrailNest_Category_Popup

    画像
    • Connected Websites:現在の TrailNest の Website Connector

    画像
    • Template Type:Handlebars

    • Creation Method:Start from scratch

    画像
    • Content Schema:TrailNest Popup Content

    変数を追加して、次へ進みます。

    • Label:Popup Category

    • API Name:Popup_Category

    • Default Value:Attributes > Popup Category

    画像

    今回は Turn off auto-engagement tracking(自動エンゲージメント計測の無効化)にチェックを入れます。

    この実装では、WPM が差し込んだコンテンツを一度非表示で保持し、CloudPages のコードが必要な部分をコピーしてポップアップとして表示します。

    そのため、WPM の差し込みと、実際にユーザーへ表示したタイミングが一致しません。

    今回のコードには、ポップアップの実表示・クリックを Personalization へ送信する処理は含めません。 効果測定まで行う場合は、この表示処理に合わせた計測を別途追加します。

    画像

    続いて、以下を Experience Template に貼り付けます。

    <div
      id="tn_popup_slot"
      data-tn-popup-source
      data-category="{{attributes.Popup_Category}}"
      hidden
      style="display:none !important;"
    >
      <!-- Backpacks: image banner -->
      <article class="tn-promo-panel" data-panel="backpacks">
        <a
          class="tn-promo-image-link"
          href="https://mcxkknz3m0zj3jqsxn4hm3dp3q-4.pub.sfmc-content.com/f0sn3uhu0p0?view=category&amp;category=backpacks"
          aria-label="Explore TrailNest backpacks"
        >
          <div
            data-image="https://image.s12.sfmc-content.com/lib/fe3311727364047e721170/m/1/Backpacks_Hero.png"
            data-alt="Explore the TrailNest backpack collection"
          ></div>
        </a>
      </article>
    
      <!-- Tents: YouTube video -->
      <article class="tn-promo-panel" data-panel="tents">
        <div class="tn-promo-copy">
          <p class="tn-promo-eyebrow">TRAILNEST · FIELD NOTES</p>
          <h2 class="tn-promo-title">A little inspiration for your next escape.</h2>
          <p class="tn-promo-description">
            Take a moment to explore. Your next adventure starts here.
          </p>
        </div>
    
        <div class="tn-promo-video" data-video></div>
    
        <div class="tn-promo-footer">
          <a
            class="tn-promo-cta"
            href="https://mcxkknz3m0zj3jqsxn4hm3dp3q-4.pub.sfmc-content.com/f0sn3uhu0p0?view=category&amp;category=tents"
          >
            Explore Tents &amp; Shelter
            <span aria-hidden="true">→</span>
          </a>
        </div>
      </article>
    
      <!-- Apparel: rich HTML -->
      <article
        class="tn-promo-panel tn-promo-apparel"
        data-panel="apparel"
      >
        <div
          class="tn-promo-apparel-visual"
          data-image="https://image.s12.sfmc-content.com/lib/fe3311727364047e721170/m/1/Apparel_Hero.png"
          data-alt="TrailNest outdoor apparel"
        ></div>
    
        <div class="tn-promo-copy">
          <p class="tn-promo-eyebrow">TRAILNEST · OUTDOOR APPAREL</p>
          <h2 class="tn-promo-title">Ready for every element.</h2>
          <p class="tn-promo-description">
            From cool mornings to changing skies, find your next layer
            for life outdoors.
          </p>
    
          <div class="tn-promo-tags" aria-label="Collection themes">
            <span>Everyday trails</span>
            <span>Weekend escapes</span>
          </div>
    
          <a
            class="tn-promo-cta"
            href="https://mcxkknz3m0zj3jqsxn4hm3dp3q-4.pub.sfmc-content.com/f0sn3uhu0p0?view=category&amp;category=apparel"
          >
            Explore Outdoor Apparel
            <span aria-hidden="true">→</span>
          </a>
        </div>
      </article>
    </div>

    保存して有効化します。

    画像

    この HTML の段階では、画像や YouTube の動画を読み込みません。後ほど追加するコードが、実際に表示するカテゴリの画像や動画を読み込みます。

    動画には、指定した YouTube 動画を使用します。自動再生は行いません。


    ⑤ CloudPages に専用の差し込み場所を追加する

    CloudPages の商品詳細ページ部分に、次の HTML を追加します。

    <!-- Content slot for the personalized popup -->
    <div id="tn_popup_slot" hidden></div>

    配置先は、商品詳細ページの #trailnest-app の内側です。

    既存の商品詳細レコメンドの直後など、商品詳細ページ用の HTML の末尾付近に追加してください。

    例えば、次のように並びます。

    <!-- Existing product recommendations -->
    <div
      id="product_detail_wpm_recs"
      class="tn-wpm-recommendation-zone"
    ></div>
    
    <!-- New popup content slot -->
    <div id="tn_popup_slot" hidden></div>

    既存のレコメンド表示場所を置き換える必要はありません。 今回は別の場所を追加します。

    なぜ hidden を付けるのか

    ここは、ポップアップを直接表示する場所ではなく、WPM から受け取ったコンテンツを保持する場所だからです。

    この内容をそのまま商品ページに表示せず、条件を満たしたときに、後ほど追加するコードがポップアップとして開きます。


    ⑥ CloudPages にデザインと表示制御を追加する

    次のコードも、Experience Template ではなく CloudPages 側に追加します。

    配置場所は、CloudPages の HTML の末尾、</body> の直前です。

    既存のチャットやレコメンドのコードは残したまま、このコードを 1 回だけ追加します。

    また、Sitemap の init や initSitemap を追加・変更する必要はありません。

    ポップアップのデザイン

    <style>
    dialog.tn-promo-dialog {
      box-sizing: border-box;
      position: fixed;
      inset: 0;
      width: min(880px, calc(100vw - 64px));
      max-width: 880px;
      max-height: calc(100dvh - 64px);
      margin: auto;
      padding: 0;
      overflow: auto;
      border: 1px solid #dce5d8;
      border-radius: 22px;
      background: #f6f7ef;
      color: #193526;
      box-shadow: 0 28px 90px rgba(8, 25, 17, .32);
      font-family: Arial, Helvetica, sans-serif;
    }
    
    dialog.tn-promo-dialog::backdrop {
      background: rgba(9, 24, 16, .62);
    }
    
    .tn-promo-dialog *,
    .tn-promo-dialog *::before,
    .tn-promo-dialog *::after {
      box-sizing: border-box;
    }
    
    .tn-promo-dialog .tn-promo-close {
      position: absolute;
      top: 14px;
      right: 14px;
      z-index: 2;
      display: grid;
      place-items: center;
      width: 44px;
      height: 44px;
      padding: 0;
      border: 1px solid rgba(25, 53, 38, .15);
      border-radius: 50%;
      background: #fff;
      color: #193526;
      box-shadow: 0 3px 12px rgba(0, 0, 0, .12);
      font-size: 28px;
      line-height: 1;
      cursor: pointer;
    }
    
    .tn-promo-dialog .tn-promo-close:hover {
      background: #edf2e8;
    }
    
    .tn-promo-dialog a:focus-visible,
    .tn-promo-dialog button:focus-visible {
      outline: 3px solid #b7813d;
      outline-offset: -4px;
    }
    
    .tn-promo-dialog .tn-promo-panel {
      margin: 0;
    }
    
    .tn-promo-dialog .tn-promo-image-link,
    .tn-promo-dialog .tn-promo-image {
      display: block;
      width: 100%;
    }
    
    .tn-promo-dialog .tn-promo-image {
      max-width: none;
      height: auto;
    }
    
    .tn-promo-dialog .tn-promo-copy {
      padding: 42px;
    }
    
    .tn-promo-dialog .tn-promo-eyebrow {
      margin: 0 48px 14px 0;
      color: #55715a;
      font-size: 11px;
      font-weight: 700;
      line-height: 1.5;
      letter-spacing: 2px;
    }
    
    .tn-promo-dialog .tn-promo-title {
      margin: 0;
      color: #193526;
      font-size: clamp(28px, 3vw, 42px);
      font-weight: 700;
      line-height: 1.12;
      letter-spacing: -1px;
    }
    
    .tn-promo-dialog .tn-promo-description {
      margin: 20px 0 0;
      color: #5c6d60;
      font-size: 16px;
      line-height: 1.7;
    }
    
    .tn-promo-dialog .tn-promo-video {
      width: 100%;
      aspect-ratio: 16 / 9;
      background: #14251b;
    }
    
    .tn-promo-dialog .tn-promo-video iframe {
      display: block;
      width: 100%;
      height: 100%;
      border: 0;
    }
    
    .tn-promo-dialog .tn-promo-footer {
      padding: 28px 42px 36px;
    }
    
    .tn-promo-dialog .tn-promo-cta {
      display: inline-flex;
      align-items: center;
      justify-content: space-between;
      gap: 24px;
      padding: 16px 22px;
      border-radius: 999px;
      background: #234b35;
      color: #fff;
      font-size: 14px;
      font-weight: 700;
      line-height: 1.4;
      text-decoration: none;
    }
    
    .tn-promo-dialog .tn-promo-cta:hover {
      background: #173923;
    }
    
    .tn-promo-dialog .tn-promo-apparel {
      display: grid;
      grid-template-columns: 44% 56%;
    }
    
    .tn-promo-dialog .tn-promo-apparel-visual {
      min-height: 480px;
      background: #e4eadc;
    }
    
    .tn-promo-dialog .tn-promo-apparel-visual img {
      width: 100%;
      height: 100%;
      object-fit: cover;
    }
    
    .tn-promo-dialog .tn-promo-apparel .tn-promo-copy {
      display: flex;
      flex-direction: column;
      justify-content: center;
    }
    
    .tn-promo-dialog .tn-promo-tags {
      display: flex;
      flex-wrap: wrap;
      gap: 8px;
      margin: 24px 0 30px;
    }
    
    .tn-promo-dialog .tn-promo-tags span {
      padding: 8px 11px;
      border: 1px solid #d8e1d2;
      border-radius: 999px;
      color: #55715a;
      font-size: 11px;
    }
    
    .tn-promo-dialog .tn-promo-apparel .tn-promo-cta {
      align-self: flex-start;
    }
    </style>

    表示タイミングと表示頻度の制御

    続けて、次のコードを追加します。

    <script>
    (() => {
      if (window.__tnPopupInstalled) return;
      window.__tnPopupInstalled = true;
    
      function start() {
        const page = document.querySelector('#trailnest-app');
        if (page?.dataset.pageType !== 'product_detail') return;
    
        const desktop = matchMedia(
          '(min-width: 1024px) and (hover: hover) and (pointer: fine)'
        );
    
        const key = 'trailnest:category-popup:last-shown:v1';
        const interval = 24 * 60 * 60 * 1000;
        const allowed = new Set(['backpacks', 'tents', 'apparel']);
    
        let shown = false;
        let busy = false;
        let active = null;
        let scheduled = false;
    
        function eligible() {
          if (shown || !desktop.matches || document.hidden) return false;
          if (page.dataset.pageType !== 'product_detail') return false;
    
          const height =
            document.documentElement.scrollHeight - window.innerHeight;
    
          if (height <= 0 || window.scrollY / height < 0.5) return false;
    
          try {
            const saved = localStorage.getItem(key);
            if (saved === null) return true;
    
            const time = Number(saved);
    
            return Number.isFinite(time) &&
              time > 0 &&
              Date.now() - time >= interval;
          } catch {
            return false;
          }
        }
    
        function display() {
          if (!eligible()) return;
    
          const source = page.querySelector('[data-tn-popup-source]');
          const category = source?.dataset.category;
    
          if (!allowed.has(category)) return;
    
          const panel = source.querySelector(
            `[data-panel="${category}"]`
          );
    
          if (
            !panel ||
            typeof HTMLDialogElement === 'undefined' ||
            !HTMLDialogElement.prototype.showModal
          ) return;
    
          const dialog = document.createElement('dialog');
          dialog.className = 'tn-promo-dialog';
          dialog.setAttribute(
            'aria-label',
            'TrailNest featured collection'
          );
    
          const close = document.createElement('button');
          close.type = 'button';
          close.className = 'tn-promo-close';
          close.setAttribute('aria-label', 'Close promotion');
          close.autofocus = true;
          close.textContent = '×';
          close.addEventListener('click', () => dialog.close());
    
          const content = panel.cloneNode(true);
          dialog.append(close, content);
    
          dialog.addEventListener('click', event => {
            if (event.target !== dialog) return;
    
            const rect = dialog.getBoundingClientRect();
    
            if (
              event.clientX < rect.left ||
              event.clientX > rect.right ||
              event.clientY < rect.top ||
              event.clientY > rect.bottom
            ) {
              dialog.close();
            }
          });
    
          dialog.addEventListener('close', () => {
            dialog.querySelectorAll('iframe').forEach(frame => {
              frame.remove();
            });
    
            dialog.remove();
    
            if (active === dialog) active = null;
          }, { once: true });
    
          document.body.append(dialog);
    
          try {
            dialog.showModal();
            localStorage.setItem(key, String(Date.now()));
          } catch (error) {
            dialog.remove();
            console.warn('[TrailNest popup] Display skipped.', error);
            return;
          }
    
          shown = true;
          active = dialog;
          observer.disconnect();
    
          content.querySelectorAll('[data-image]').forEach(slot => {
            const image = document.createElement('img');
            image.className = 'tn-promo-image';
            image.src = slot.dataset.image;
            image.alt = slot.dataset.alt || '';
            slot.replaceChildren(image);
          });
    
          const video = content.querySelector('[data-video]');
    
          if (video) {
            const frame = document.createElement('iframe');
    
            frame.src =
              'https://www.youtube.com/embed/ADKEjMZM3bw' +
              '?autoplay=0&playsinline=1';
    
            frame.title = 'TrailNest featured YouTube video';
            frame.allow =
              'encrypted-media; picture-in-picture; fullscreen';
    
            frame.allowFullscreen = true;
            frame.referrerPolicy = 'strict-origin-when-cross-origin';
    
            video.replaceChildren(frame);
          }
        }
    
        async function attempt() {
          if (busy || !eligible()) return;
    
          // Skip display if cross-tab coordination is unavailable.
          if (!navigator.locks?.request) return;
    
          busy = true;
    
          try {
            await navigator.locks.request(
              'trailnest-category-popup',
              display
            );
          } catch (error) {
            console.warn('[TrailNest popup] Display skipped.', error);
          } finally {
            busy = false;
          }
        }
    
        function schedule() {
          if (scheduled || shown) return;
    
          scheduled = true;
    
          requestAnimationFrame(() => {
            scheduled = false;
            void attempt();
          });
        }
    
        const observer = new MutationObserver(schedule);
    
        observer.observe(page, {
          childList: true,
          subtree: true,
          attributes: true
        });
    
        window.addEventListener('scroll', schedule, { passive: true });
    
        window.addEventListener('resize', () => {
          if (!desktop.matches && active?.open) active.close();
          schedule();
        });
    
        desktop.addEventListener('change', () => {
          if (!desktop.matches && active?.open) active.close();
          schedule();
        });
    
        document.addEventListener('visibilitychange', schedule);
    
        window.addEventListener('pagehide', () => {
          if (active?.open) active.close();
        });
    
        schedule();
      }
    
      if (document.readyState === 'loading') {
        document.addEventListener(
          'DOMContentLoaded',
          start,
          { once: true }
        );
      } else {
        start();
      }
    })();
    </script>

    このコードで行っていること

    • 商品詳細ページであることを確認する

    • 表示対象の画面幅・操作環境であることを確認する

    • スクロール位置が 50% 以上になっているかを確認する

    • 前回の表示から 24 時間経過したかを確認する

    • WPM が返したカテゴリのコンテンツをポップアップとして表示する

    • 表示時刻をブラウザに保存する

    • 閉じたときに動画の iframe を削除し、再生を止める

    複数のタブを同時に開いた場合の重複表示を抑えるため、ブラウザの Web Locks API も使用しています。この API は、同じオリジンのタブ間で処理を調整するための仕組みです。

    保存領域や必要なブラウザ機能を利用できない場合は、ポップアップを表示しません。

    表示履歴の範囲

    表示履歴は、同じブラウザ・同じサイトのオリジン内で共有されます。顧客 ID に対して Data 360 に保存するものではありません。

    別のブラウザや別の端末には引き継がれません。また、サイトの保存データを削除した場合は、表示履歴も消えます。

    CloudPages を保存して再公開します。

    画像

    この時点の Cloudpages 全体の HTML は以下からダウンロードできます。


    ⑦ WPM で商品詳細ページへ紐付ける

    公開済みの TrailNest の「商品詳細ページ」を開き、WPM を起動し、ログインします。

    商品詳細ページの URL にすでに Query String がある場合は、末尾に次を追加します。

    &sf_personalization_wpm
    画像

    Personalization Experience を新規作成します。

    • 種類:Embedded Content

    • Personalization Point:TrailNest Product Popup

    • Experience Template:TrailNest Category Popup

    画像

    Location では、次の内容を指定します。

    • 表示対象:Current Page Type の商品詳細ページ

    • Display Method:Replace an Element

    • Page Element の Selector:#tn_popup_slot

    画像

    空の非表示要素なので、ページ上をクリックして選ぶのではなく、Selector の入力欄に直接コピペで指定します。

    今回は Add an Overlay を選択しません。 スクロール量とポップアップの開閉は、CloudPages に追加したコードが管理します。

    WPM の Current Page Type を商品詳細ページに指定すると、その Page Type に該当する商品詳細ページが対象になります。

    Content タブで Engagement Destination の指定が必要な場合は、Website Engagement を選択します。ただし、今回のテンプレートでは自動エンゲージメント計測を無効にしているため、この指定だけで実表示やクリックが計測されるわけではありません。

    画像

    ⑧ プレビューで表示と制御を確認する

    今回は、1 つの Decision が、顧客ごとの最高スコアのカテゴリを返します。

    そのため、確認する顧客を切り替えて、画像・動画・HTML の出し分けを確認します。

    プレビューの準備

    画面幅を 1,024 px 以上にし、マウス操作が可能なブラウザで、TrailNest の商品詳細ページを開きます。

    WPM の Preview Settings で、次の内容を設定します。

    • Show Overlays:無効

    • Show All Personalization Experiences:無効

    • Individual ID:確認する顧客の Individual ID

    今回の構成では、Web 側の deviceId を Individual ID にマッピングしています。そのため、これまでの検証で Affinity を確認できているブラウザの deviceId を指定します。

    Show Overlays は、WPM のプレビューで表示領域を強調するための設定です。今回作成したポップアップの表示を有効・無効にする設定ではありません。


    Current User Decision でプレビューする

    Personalization Decisions and Cohorts のプルダウンで、Current User Decision を選択し、Preview をクリックします。

    今回の Decision は、Targeting Rules を Always にしています。選択された Decision の Merge Field が、指定した顧客の Affinity を参照し、最高スコアのカテゴリを返します。

    その後、商品詳細ページを下へスクロールします。

    Preview をクリックしただけでは、ポップアップは開きません。 スクロール可能な範囲を 50% 以上進み、表示頻度などの条件を満たした場合に開きます。


    カテゴリごとの表示を確認する

    確認する顧客を切り替え、次の内容を確認します。

    • 例えば、Tents が最高スコアの顧客の場合、YouTube の動画プレイヤーが表示されました。

    画像

    Affinity がない場合は、Merge Field の Default Text に設定した default が返ります。今回の表示コードでは対象の 3 カテゴリ以外を表示しないため、ポップアップは開きません。


    テスト中に 24 時間の制限を解除する

    一度ポップアップが表示されると、プレビューでも 24 時間の表示制限が適用されます。

    この履歴は、プレビューで指定した顧客ではなく、操作しているブラウザに保存されます。 Individual ID を別の顧客に変えただけでは、制限は解除されません。

    別の顧客を続けて確認するときは、TrailNest を開いているブラウザの開発者ツールの Console で、次を実行します。

    localStorage.removeItem('trailnest:category-popup:last-shown:v1');

    その後、ページを再読み込みし、WPM で確認する顧客の Individual ID を指定して、再度プレビューします。

    削除するのは、今回のポップアップの表示履歴だけです。Cookie やサイトデータ全体を削除する必要はありません。


    リンクと閉じる操作を確認する

    表示できたら、次の操作を確認します。

    • Backpacks の画像をクリックすると、Backpacks のカテゴリページへ移動する

    • Tents のボタンをクリックすると、Tents のカテゴリページへ移動する

    • Apparel のボタンをクリックすると、Apparel のカテゴリページへ移動する

    • 右上の × で閉じられる

    • ポップアップの外側の背景をクリックすると閉じられる

    • Esc キーでも閉じられる

    • YouTube の動画を再生した状態で閉じると、音声も止まる

    動画は自動再生しない設定です。プレイヤーの再生ボタンを押して確認します。


    連続表示されないことを確認する

    表示履歴を残した状態で、次の順番で確認します。

    1. 商品詳細ページでポップアップを表示する

    2. ポップアップを閉じる

    3. 別の商品詳細ページへ移動する

    4. ページを 50% 以上スクロールする

    5. ポップアップが再表示されないことを確認する

    表示から 24 時間以内は、別の商品ページへ移動しても、ページを再読み込みしても表示しません。

    カテゴリ別の履歴ではないため、興味のあるカテゴリが変わっても、24 時間以内は再表示しません。


    表示対象外のページ・画面も確認する

    最後に、次の環境では表示されないことを確認します。

    • トップページ

    • 注文完了ページ

    • 画面幅が 1,024px 未満のブラウザ

    • スマートフォンなど、今回の画面幅・ポインター条件を満たさない環境

    非表示の確認をする際は、先に今回の表示履歴を解除しておくと、24 時間制限による非表示と区別できます。

    また、スクロールする余地がない短いページでは、今回のコードはポップアップを表示しません。

    ここまで確認できたら、次の手順で Experience を公開します。


    ⑨ 公開する

    プレビューで問題がなければ、WPM の Experience の State を Enabled にし、Save をクリックします。

    画像

    公開後は、WPM を閉じた通常のページでも確認します。シークレットモードで確認します。

    • ① backpack の商品詳細ページ を一度開いて、F5 をクリックしたら、下にスクロールします。すると、backpack のバナーが表示されました。

    画像

    表示ができたら、すべてのシークレットモードを削除して、再度シークレットモードでページを立ち上げます。すべてのシークレットモードを削除しないと「匿名 ID 」が切り替わりません。

    • ② tents の商品詳細ページ を一度開いて、F5 をクリックしたら、下にスクロールします。すると、Youtube が表示されます。

    画像

    表示ができたら、すべてのシークレットモードを削除して、再度シークレットモードでページを立ち上げます。

    • ③ apparel の商品詳細ページ を一度開いて、F5 をクリックしたら、下にスクロールします。すると、少し複雑な HTML ポップアップを表示することができました。

    画像

    なお、今回の 24 時間制限やモバイル非表示は、画面への表示を制御するものです。WPM による Personalization Request 自体を、必ず停止する構成ではありません。表示されない場合にも、Decision の取得やコンテンツの差し込みが行われることがあります。


    いかがでしたでしょうか。

    今回は、TrailNest の Affinity を使い、顧客の興味に合わせて、単純な画像・動画(YouTube)・複雑な HTML のポップアップを切り替える 構成を紹介しました。

    同じパーソナライズでも、商品を並べるだけでなく、ブランドの世界観を伝える画像や動画、カテゴリに合わせた案内にも活用できます。

    また、ポップアップでは「何を表示するか」と同じくらい、「いつ表示するか」「何度表示するか」も大切です。

    今回は商品詳細ページに限定し、モバイルでは表示せず、同じブラウザでは 24 時間に 1 回までにすることで、商品の閲覧を妨げにくい構成にしました。

    今回は以上です。


    次の記事はこちら

    前回の記事はこちら

    私の note のトップページはこちら

     
     
    Salesforce Marketing Cloud、Agentforce、Data Cloud、Salesforce 認定資格に関する実践的な情報を発信しています。これらの記事が、皆さまの学習や日々の業務に少しでもお役に立てば幸いです。