このページはAパターンのコンテンツを表示しています。

このページはBパターンのコンテンツを表示しています。

ページを作成するうえで、どちらの構成・文章がいいだろう?と思うことは多いと思います。

そんな時のために、HTML内にコードを入れるだけで簡単にAパターン、Bパターンの出しわけができる方法をお伝えします。

コンセプトはできるだけ楽に。

実装要件

今回作成したABテスト機能の要件は以下です。

  • Javascript+HTML/CSS で実装する
  • ページ初回アクセス時にAパターン、Bパターンどちらを表示するか振り分ける
  • 2回目以降のアクセス時は初回で振り分けたパターンを表示する
  • 振り分けられたパターンに応じてコンテンツを出し分ける
  • SEOマイナス評価となるフリッカーやCLS低下を防ぐ
  • 検証用にABいずれかのパターンを任意に指定可能とする
  • 検証用にABパターン振り分けのリセットを可能とする

ABテスト用コードの使い方

作成したものがこちらです。

Javascript + CSS コード

まずはこちらをheadタグ内、body開始直後など、必ず出し分けたいエリアの前に来るよう設置してください。

<!-- AB test -->
<script>
  (function() {
    const STORAGE_KEY = 'ab_test_pattern';
    
    // ①検証用パラメータの処理
    const urlParams = new URLSearchParams(window.location.search);
    const forceParam = urlParams.get('ab_test');

    if (forceParam === 'reset') {
      localStorage.removeItem(STORAGE_KEY);
    } else if (forceParam === 'A' || forceParam === 'B') {
      localStorage.setItem(STORAGE_KEY, forceParam);
    }

    // ②パターンの取得・決定
    let currentPattern = localStorage.getItem(STORAGE_KEY);
    if (!currentPattern) {
      currentPattern = Math.random() < 0.5 ? 'A' : 'B';
      localStorage.setItem(STORAGE_KEY, currentPattern);
    }

    // ③HTMLタグにクラスを付与
    document.documentElement.classList.add('ab-pattern-' + currentPattern);

    // ④計測用にグローバル変数にパターンを保持
    window.currentABPattern = currentPattern;
  })();
</script>

<style>
  /* ⑤HTMLタグに付与されたクラスによって出し分け */
  .ab-content-A, .ab-content-B { display: none; }
  html.ab-pattern-A .ab-content-A { display: block; }
  html.ab-pattern-B .ab-content-B { display: block; }
</style>

<noscript>
  <style>
    /* ⑥JS無効時はAを強制表示 */
    .ab-content-A {
      display: block;
    }
  </style>
</noscript>
<!-- End AB -->

HTMLコード

こちらはシンプルで、Aパターン、Bパターンそれぞれで表示させたい要素に以下のclassを追加します。
ab-content-A:Aパターンで表示したい要素
ab-content-B:Bパターンで表示したい要素

例えば、このページ最上部のテキストは以下のようにclassを指定しています。

<p class="ab-content-A"><strong>このページはAパターンのコンテンツを表示しています。</strong></p>
<p class="ab-content-B"><strong>このページはBパターンのコンテンツを表示しています。</strong></p>

解説

簡単に解説します。

    // ①検証用パラメータの処理
    const urlParams = new URLSearchParams(window.location.search);
    const forceParam = urlParams.get('ab_test');

    if (forceParam === 'reset') {
      localStorage.removeItem(STORAGE_KEY);
    } else if (forceParam === 'A' || forceParam === 'B') {
      localStorage.setItem(STORAGE_KEY, forceParam);
    }

①の処理は検証用の記述です。

?ab_test=rest:振り分けられたパターンをリセットし、再度ランダムな振り分けを行います。
?ab_test=A:強制的にAパターンを表示します。
?ab_test=B:強制的にBパターンを表示します。

今回は LocalStorage に振り分けたABパターンを保持します。
LocalStorage はブラウザに半永久的(Safariのみ最終アクセスから7日間)に情報を保持することができる機能です。
保存できるのは文字のみですが、今回のようなA、Bどちらかを保持させるには最適です。
詳しい仕様はMDN Web Docsをご覧ください。

    // ②パターンの取得・決定
    let currentPattern = localStorage.getItem(STORAGE_KEY);
    if (!currentPattern) {
      currentPattern = Math.random() < 0.5 ? 'A' : 'B';
      localStorage.setItem(STORAGE_KEY, currentPattern);
    }

②の処理はAパターン、Bパターンの振り分け処理です。
Math.random() < 0.5の 0.5 の部分を書き換えることで振り分けの比率を変えることができます。
指定した数字がAパターンの比率になり、残りがBパターンになります。

    // ③HTMLタグにクラスを付与
    document.documentElement.classList.add('ab-pattern-' + currentPattern);

③の処理は出し分けのキモとなる部分です。
振り分けられたパターンに応じてHTMLタグにclassとして ad-pattern-A もしくは ad-pattern-B を付与します。

これによりコード内⑤のCSSが有効になり、コンテンツ内のABパターンが可能となります。

    // ④計測用にグローバル変数にパターンを保持
    window.currentABPattern = currentPattern;

④は出し分け自体には使用しない、計測などの処理を行うためのコードです。
window.currentABPatternにAかBが入るので、それを使って計測タグでどのパターンが表示されているか集計を行いましょう。

どの計測タグを使用しているかによって変わるものなので、本記事では割愛します。

<style>
  /* ⑤HTMLタグに付与されたクラスによって出し分け */
  .ab-content-A, .ab-content-B { display: none; }
  html.ab-pattern-A .ab-content-A { display: block; }
  html.ab-pattern-B .ab-content-B { display: block; }
</style>

⑤は実際に表示・非表示を指定するCSSコードです。
③でHTMLタグにclassとしてab-pattern-Aもしくはab-pattern-Bが付与され、どちらが付与されたかによってab-content-Aもしくはab-content-Bのどちらかが有効(block)となります。

<noscript>
  <style>
    /* ⑥JS無効時はAを強制表示 */
    .ab-content-A {
      display: block;
    }
  </style>
</noscript>

⑥はscriptが無効となっているユーザーに対して、Aを有効とする処理です。
ほぼいないとは思いますが、念のための処理です。

まとめ

今回は簡易的なABパターンの出し分けコードを作成してみました。

実際にはGA等を使用してパターンごとに計測を行うことになるかと思いますが、④のコードで判定すれば簡単ではないかと思います。

ということで次の記事はGA4での計測方法になりそうです。