コンテンツにスキップ

ランディングページでのカスタムフォームブロックの作成

Brazeランディングページのフォームブロックは、テキストフィールド、チェックボックス、ドロップダウンなどの標準入力をキャプチャします。カスタムフォームブロックは、星評価、絵文字センチメントピッカー、スクラッチカードなど、独自のインタラクティブ要素を構築できるようにすることで、可能性を広げます。

訪問者がカスタムフォームを送信すると、選択した値はバリデーションされ、標準フィールドと一緒に保存された後、カスタムユーザー属性としてBrazeに送信されます。これにより、ランディングページエディターを離れることなく、より豊かで魅力的な入力を収集できます。

カスタムフォームブロックは、単一のJavaScriptヘルパー window.brazeHelpers.forms.registerFormInput を使用して構築します。これはランディングページのカスタムコードブロックから呼び出します。

仕組み

カスタムフォーム入力は、ランディングページ上の任意の要素で、その値をキャプチャしてフォームと一緒に送信したいものです。その要素を登録することで、Brazeのフォームシステムに接続します。登録によって、Brazeはどの要素を監視するか、現在の値をどのように読み取るか、フォームの送信時にその値をどう処理するかを把握します。

  1. ランディングページ上のカスタムコードブロック内にカスタムUIを構築し、idなどの安定したCSSセレクタを付与します。
  2. 設定オブジェクトを使用してwindow.brazeHelpers.forms.registerFormInputを呼び出し、要素を登録します。
  3. Brazeは必要に応じてgetValue関数を呼び出し、現在の値を読み取ります。
  4. フィールドが必須の場合、またはonValidate関数を指定した場合、Brazeは値が検証を通過するまで送信をブロックし、無効な要素にスタイル設定可能なCSSクラスを付与します。バリデーションと必須フィールドを参照してください。
  5. フォームが送信されてバリデーションが通過すると、BrazeはonSubmit関数を呼び出します。この関数内で、Braze SDKを呼び出してカスタム属性などの情報を記録できます。

値の読み取り、バリデーション、送信を行う関数はすべて開発者が提供するため、このアプローチはほぼすべてのカスタムフォーム要素に対応でき、エディターの標準フィールドタイプに限定されません。

基本的なフレームワーク

最もシンプルな登録では、1つの要素を対象にして必須として扱い、データ属性から値を読み取り、フォーム送信時にその値をカスタムユーザー属性に書き込みます。このページの例のように、registerFormInputが実行される前に要素が存在するよう、呼び出しをDOMContentLoadedリスナーで囲みます。

document.addEventListener("DOMContentLoaded", () => {
  window.brazeHelpers.forms.registerFormInput({
    selector: "#my-custom-input",
    isRequired: true,
    getValue: (element) => element.dataset.value ?? null,
    onSubmit: (value) => {
      window.brazeBridge.getUser().setCustomUserAttribute("my_attribute", value);
    },
  });
});

ページ上のカスタム入力ごとにregisterFormInputを1回呼び出します。ランディングページエディターで配置した標準フォームフィールドは登録する必要はありません。登録が必要なのは、Custom Codeブロックで構築したカスタム入力のみです。

設定リファレンス

registerFormInput は単一の設定オブジェクトを受け取ります。関数シグネチャとして表すと、完全な構造は次のとおりです。

window.brazeHelpers.forms.registerFormInput({
  // Provide exactly one of `selector` or `element` to identify the input.
  selector?: string,
  element?: HTMLElement,
  isRequired?: boolean | Promise<boolean>,
  getValue: (element: HTMLElement) => value,
  onValidate?: (value, element: HTMLElement) => boolean | Promise<boolean>,
  onSubmit?: (value, element: HTMLElement) => void | Promise<void>,
});

最低限、要素を特定する方法(selector または element)と getValue 関数を指定する必要があります。それ以外はすべてオプションです。

プロパティ 型 必須 説明
selector string はい(または element) カスタム要素に一致するCSSセレクター(例: "#scratch-card")。Brazeはバリデーションおよび送信時に querySelector で遅延解決するため、registerFormInput の実行後にDOMに追加された要素にも一致できます。
element HTMLElement はい(または selector) 要素への直接参照で、selector の代わりに使用します。要素がページにアタッチされている間のみ使用され、両方が指定された場合は selector より優先されます。
isRequired boolean \| Promise<boolean> いいえ true の場合、入力に空でない値が入力されるまでフォームを送信できません。null、undefined、空文字列(空白のみの文字列を含む)、および空の配列はすべて空として扱われます。ブール値に解決されるPromiseを指定することもでき、Brazeは入力がバリデーションされるたびに再評価するため、実行時に必須状態を決定できます。デフォルトは false です。バリデーションの完全な順序については、バリデーションと必須フィールドを参照してください。
getValue function はい 入力の現在の値を返します。Brazeは一致した要素を引数として渡すため、DOMから値を読み取る(例: element.dataset.sentiment)ことも、自身のコード内の変数から読み取ることもできます。まだ値がない場合は null を返してください。
onValidate function いいえ 現在の値と一致した要素を受け取り、boolean \| Promise<boolean>(isRequired と同じ戻り値の型)を返す必要があります。true は値が有効であることを意味し、false は無効であることを意味します。値が存在する以上のルールを適用する場合に使用します(例: 値が許可されたセットの一つであることの検証)。省略した場合、isRequired とネイティブの制約バリデーションのみが適用されます。
onSubmit function いいえ フォームが送信されバリデーションに合格したときに実行されます。現在の値と一致した要素を受け取ります。ここで値をBrazeに記録します(通常は setCustomUserAttribute を使用)。

onSubmitで値を処理する

onSubmit はプレーンなJavaScriptコールバックであるため、インテグレーションの必要に応じてキャプチャした値を処理できます。onSubmit はフォーム送信の一部として実行されるため、その中で行われる brazeBridge の呼び出しは、ランディングページを匿名で開いた訪問者の場合でも期待どおりに動作します。ブリッジ呼び出しが動作するその他の状況については、ブリッジの利用可能性を参照してください。

最も一般的なパターンは、ランディングページで利用可能なBraze JavaScriptブリッジを使用して、キャプチャした値をユーザープロファイルに書き込むことです。

window.brazeBridge.getUser().setCustomUserAttribute("attribute_name", value);

ワークスペースに既存のカスタム属性名、または新たに作成したいカスタム属性名を使用してください。渡した値はユーザーのプロファイルに保存され、セグメンテーション、パーソナライゼーション、およびフォローアップメッセージのトリガーに使用できます。

カスタム属性に限定されません。同じコールバックから、任意のbrazeBridge.getUser() メソッドを呼び出すことができます。例えば、ユーザーを購読グループに追加する、標準属性項目を設定する、カスタムイベントを記録する、または値を独自のAPIエンドポイントに送信するなどが可能です。

例

以下の例はそれぞれ完結した自己完結型のコードです。各例はマークアップ、<script> タグ、<style> タグを含む単一のブロックで構成されており、ランディングページ上の1つのカスタムコード(HTML)ブロックに貼り付けて使用します。各スクリプトの末尾付近にある registerFormInput の呼び出しが、カスタム入力を Braze フォームに接続します。コードサンプルにカーソルを合わせてコピーアイコンを選択すると、コードをコピーできます。

目的: 訪問者が嬉しい顔または悲しい顔を選択し、その選択が feedback_sentiment という名前の文字列カスタム属性に書き込まれます。

以下を1つのカスタムコード(HTML)ブロックに貼り付けてください:

<div id="sentiment-picker" class="sentiment-picker">
  <button type="button" data-sentiment="positive" aria-label="Happy">🙂</button>
  <button type="button" data-sentiment="negative" aria-label="Unhappy">🙁</button>
</div>

<script>
  document.addEventListener("DOMContentLoaded", () => {
    const picker = document.getElementById("sentiment-picker");

    picker.querySelectorAll("button").forEach((button) => {
      button.addEventListener("click", () => {
        picker.dataset.sentiment = button.dataset.sentiment;
        picker.querySelectorAll("button").forEach((b) => b.classList.remove("selected"));
        button.classList.add("selected");
      });
    });

    window.brazeHelpers.forms.registerFormInput({
      selector: "#sentiment-picker",
      isRequired: true,
      getValue: (element) => element.dataset.sentiment ?? null,
      onSubmit: (value) => {
        window.brazeBridge.getUser().setCustomUserAttribute("feedback_sentiment", value);
      },
    });
  });
</script>

<style>
  .sentiment-picker {
    display: flex;
    gap: 16px;
    justify-content: center;
    font-size: 40px;
  }

  .sentiment-picker button {
    background: none;
    border: 2px solid transparent;
    border-radius: 12px;
    cursor: pointer;
    line-height: 1;
    padding: 8px;
  }

  .sentiment-picker button.selected {
    border-color: #1f2933;
  }

  /* Braze adds this class to the registered element when validation fails. */
  .sentiment-picker.bz-validation-error {
    outline: 3px solid #f94144;
    outline-offset: 4px;
    border-radius: 12px;
  }
</style>

仕組み: ボタンを選択すると、そのボタンの data-sentiment の値がコンテナ要素に保存されます。getValue は Braze が渡すコンテナ要素からその値を読み取ります。isRequired が true に設定されているため、訪問者が顔を選択するまでフォームは送信されず、コンテナが空の間は bz-validation-error クラスが付与されます。送信時に、選択された値("positive" または "negative")が feedback_sentiment に書き込まれます。

目的: 訪問者がカードをスクラッチして3つの割引(10% Off、20% Off、または 25% Off)のいずれかを表示し、その割引が scratch_off_reward という名前の文字列カスタム属性に書き込まれます。

この例では、HTML キャンバス にスクラッチカードを描画します。ページの読み込み時にランダムで報酬が選択され、訪問者がスクラッチして削る不透明なレイヤーの下に隠されます。キャンバス によるアプローチは、お好みのスクラッチウィジェットに置き換えることができます。Braze に接続するのは registerFormInput の呼び出しのみです。以下を1つのカスタムコード(HTML)ブロックに貼り付けてください:

<div id="scratch-card" class="scratch-card" data-reward="">
  <span class="scratch-card__reward"></span>
  <canvas class="scratch-card__surface" width="300" height="150"></canvas>
</div>

<script>
  document.addEventListener("DOMContentLoaded", () => {
    const rewards = ["10% Off", "20% Off", "25% Off"];

    const card = document.getElementById("scratch-card");
    const label = card.querySelector(".scratch-card__reward");
    const canvas = card.querySelector(".scratch-card__surface");
    const ctx = canvas.getContext("2d");

    // Randomly assign which reward this visitor will reveal.
    const reward = rewards[Math.floor(Math.random() * rewards.length)];
    label.textContent = reward;

    // Paint the opaque scratch layer over the reward.
    ctx.fillStyle = "#b3b3b3";
    ctx.fillRect(0, 0, canvas.width, canvas.height);
    ctx.globalCompositeOperation = "destination-out";

    let isScratching = false;

    function scratchAt(event) {
      const rect = canvas.getBoundingClientRect();
      ctx.beginPath();
      ctx.arc(event.clientX - rect.left, event.clientY - rect.top, 18, 0, Math.PI * 2);
      ctx.fill();
    }

    canvas.addEventListener("pointerdown", () => { isScratching = true; });
    canvas.addEventListener("pointermove", (event) => {
      if (isScratching) scratchAt(event);
    });
    canvas.addEventListener("pointerup", () => {
      isScratching = false;
      // The visitor has scratched the card, so record the revealed reward.
      card.dataset.reward = reward;
    });

    window.brazeHelpers.forms.registerFormInput({
      selector: "#scratch-card",
      isRequired: true,
      getValue: (element) => element.dataset.reward || null,
      onValidate: (value) => rewards.includes(value),
      onSubmit: (value) => {
        window.brazeBridge.getUser().setCustomUserAttribute("scratch_off_reward", value);
      },
    });
  });
</script>

<style>
  .scratch-card {
    position: relative;
    width: 300px;
    height: 150px;
    margin: 0 auto;
    font-family: system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
  }

  /* The reward sits underneath and is revealed as the canvas is scratched away. */
  .scratch-card__reward {
    position: absolute;
    inset: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 32px;
    font-weight: 700;
    color: #1f2933;
  }

  .scratch-card__surface {
    position: absolute;
    inset: 0;
    border-radius: 12px;
    cursor: pointer;
    touch-action: none;
  }

  /* Braze adds this class to the registered element when validation fails. */
  .scratch-card.bz-validation-error {
    outline: 3px solid #f94144;
    outline-offset: 4px;
    border-radius: 12px;
  }
</style>

仕組み: ページが読み込まれると、スクリプトが3つの報酬の中からランダムに1つを選び、キャンバス 上にその上に不透明なレイヤーを描画します。訪問者が キャンバス 上をドラッグすると、"destination-out" の合成モードによってそのレイヤーが消去され、下の報酬が表示されます。pointerup 時に、表示された報酬がカードの data-reward 属性に書き込まれます。getValue がそこから値を読み取り、onValidate が定義された3つの報酬のいずれかであることを確認し、isRequired によってカードがスクラッチされるまで送信が防止されます。送信時に、表示された割引(例:"20% Off")が scratch_off_reward に書き込まれます。

目的: 訪問者をランディングページに誘導したキャンペーンを取得し、ダウンストリームのレポートおよびアトリビューションのためにカスタムイベントプロパティとしてログに記録します。

この例では、ランディングページのフォーム送信を特定のキャンペーンに帰属させる方法を示します。メール、SMS、または WhatsApp メッセージ内のランディングページ URL に {{campaign.${api_id}}} のような Liquid 変数を追加することで、キャンペーン識別子をランディングページに渡すことができます。カスタムフォームブロックは URL からこのパラメーターを読み取り、キャンペーン API ID をイベントプロパティとしてカスタムイベントをログに記録します。これにより、どのキャンペーンがフォーム送信を促進しているかを追跡しやすくなります。

以下を1つのカスタムコード(HTML)ブロックに貼り付けてください:

<input type="hidden" id="campaign-attribution" value="" />

<script>
  document.addEventListener("DOMContentLoaded", () => {
    const hiddenInput = document.getElementById("campaign-attribution");
    const campaignApiId = new URLSearchParams(window.location.search).get("campaign_api_id");

    if (campaignApiId) {
      hiddenInput.value = campaignApiId;
    }

    window.brazeHelpers.forms.registerFormInput({
      selector: "#campaign-attribution",
      isRequired: false,
      getValue: (element) => element.value || null,
      onSubmit: async (value) => {
        if (!value) {
          return;
        }

        await window.brazeBridge.logCustomEvent("landing_page_form_submitted", {
          campaign_api_id: value,
        });
      },
    });
  });
</script>

仕組み: ランディングページにリンクするメール、SMS、または WhatsApp メッセージを作成する際に、Liquid テンプレートを使用して URL にキャンペーン識別子を追加します:https://your-landing-page.com?campaign_api_id={{campaign.${api_id}}}。訪問者がそのメッセージからランディングページにアクセスすると、スクリプトが URL から campaign_api_id パラメーターを読み取り、非表示の入力フィールドに保存します。フォーム送信時にキャンペーン ID が存在する場合、onSubmit コールバックがキャンペーン API ID をイベントプロパティとして landing_page_form_submitted という名前のカスタムイベントをログに記録します。このイベントは Currents に表示され、レポート、セグメンテーション、およびアトリビューション分析に使用できます。

バリデーションと必須フィールド

フォームを送信する前に、入力は該当するすべてのレイヤーを通過する必要があります。

  1. isRequired は、空でない値が存在するかどうかで送信を制御します。入力にまだ値がない場合は getValue から null を返してください。そうすることで、Brazeはその入力が空であると判断できます。空文字列(空白のみの文字列を含む)と空の配列も空として扱われますが、0 と false は値が存在するとみなされます。isRequired はブール値、またはブール値に解決されるPromiseのいずれかを指定でき、入力がバリデーションされるたびに再評価されます。
  2. ネイティブ制約バリデーション。 マッチした要素が標準のHTML checkValidity() APIをサポートしている場合(たとえば required、pattern、min、max を持つネイティブの <input>)、Brazeはそれを実行し、失敗した場合は送信をブロックします。完全にカスタムの非ネイティブ要素(div、canvas など)の場合、このチェックは常にパスするため、独自のロジックに干渉することはありません。
  3. onValidate は、独自のルールに基づいて送信を制御します。現在の値とマッチした要素を受け取り、boolean \| Promise<boolean>(isRequired と同じ戻り値の型)を返す必要があります。true は値が有効であることを意味し、false は無効であることを意味します。許可される値のチェック、フォーマットチェック、範囲チェック、またはJavaScriptで記述できる任意のロジックに使用してください。

エラースタイリング

入力がバリデーションに失敗するたびに、Brazeは selector または element でマッチした要素にCSSクラス bz-validation-error を追加し、入力が有効になるとそのクラスを削除します。無効な状態のスタイルは自由に設定できます。たとえば、問題のある入力に注意を引くアウトラインやボーダーを、要素と bz-validation-error クラスを組み合わせたルールを追加することで実現できます。

#my-custom-input.bz-validation-error {
  outline: 3px solid #f94144;
  outline-offset: 4px;
}

エラー状態のスタイリングは任意ですが推奨されます。これにより、訪問者はどのカスタム入力が送信をブロックしているかを確認できます。このページの各例には bz-validation-error ルールが含まれています。

ベストプラクティス

  • 安定した一意のセレクターを使用してください。idが最も安全な選択です。複数の要素に一致する可能性のあるセレクターは避けてください。
  • 値がない場合は、空文字列やundefinedではなくnullを返してください。これにより、必須チェックが予測どおりに動作します。空文字列や空の配列も空として扱われますが、nullは「値なし」を示す最も明確なシグナルです。
  • getValueは低コストかつ同期的に保ってください。Brazeはこれを複数回呼び出す可能性があるため、重い処理を実行するのではなく、現在の値を読み取って返すようにしてください。
  • bz-validation-error状態のスタイルを設定して、どのカスタム入力が送信をブロックしているかを訪問者が確認できるようにしてください。
  • カスタム属性名を事前に定義し、一貫性を保ってください。これにより、後でデータに基づいて確実にセグメンテーションを行うことができます。
  • 送信の全体をテストしてください。送信後にユーザープロファイルに属性が表示されること、および必須ルールとバリデーションルールが期待どおりに送信をブロックすることを確認してください。

トラブルシューティング

何も選択されていないのにフォームが送信される

isRequired が true に設定されていることを確認してください。Braze は null、undefined、空文字列(空白のみの文字列を含む)、空の配列を値なしとして扱います。何も選択されていないときに getValue が別の値(例えば、空でないデフォルト値やプレースホルダー)を返している場合、必須チェックで検出されません。

プロファイルに値が表示されない

onSubmit が正しい属性名で window.brazeBridge.getUser().setCustomUserAttribute を呼び出していること、および Custom Code ブロックがフォームと同じランディングページ上にあることを確認してください。

登録しても何も起こらない

registerFormInput が実行される時点で、セレクターが DOM 内に存在する要素と一致していること、そしてその要素がレンダリングされた後にスクリプトが実行されていることを確認してください。次に、ブラウザの開発者コンソールを開いてください。registerFormInput は設定を検証し、問題がある場合(例えば、getValue が見つからない、セレクターが有効な CSS セレクターでない、プロパティの型が正しくない場合)、登録を無視し、[brazeHelpers.forms.registerFormInput] というプレフィックス付きで何が無効であったかを説明する警告をログに記録します。

New Stuff!