> ## Documentation Index
> Fetch the complete documentation index at: https://documentation.onesignal.com/llms.txt
> Use this file to discover all available pages before exploring further.

# ボタン駆動のオンボーディングカルーセルを構築する

> ボタンナビゲーションと自動終了機能を備えたHTML アプリ内メッセージを使用して、マルチステップのオンボーディングフローを作成します。

## 概要

このチュートリアルでは、単一のHTML アプリ内メッセージを使用して**マルチステップのオンボーディングカルーセル**を作成する方法を説明します。スワイプジェスチャーに依存する従来のカルーセルとは異なり、このアプローチは**ボタン駆動のナビゲーション**を使用し、すべてのステップを1つのメッセージ内に保持します。

**構築する内容：**

* 画像、テキスト、ボタンを含む2ステップのオンボーディングフロー
* ボタンナビゲーション（「次へ」をタップして進む、「始める」をタップして閉じる）
* 進行状況インジケータードット
* ステップ間のスムーズなフェード遷移

<Frame caption="ボタン駆動ナビゲーションを使用したオンボーディングカルーセルの例">
  <img src="https://mintcdn.com/onesignal/t_Vr4vLAhranGx-O/images/docs/onboarding-carousel-demo.gif?s=39418d8af9896323694f46f9cb6eebec" alt="画像、テキスト、次へボタンを含むウェルカム画面を表示するオンボーディングカルーセル" style={{width: '200px', maxWidth: '100%'}} width="768" height="1660" data-path="images/docs/onboarding-carousel-demo.gif" />
</Frame>

**このアプローチを使用する場合：**

* ユーザーを**短いオンボーディングまたは教育フロー**（2-5ステップ）を通じてガイドする
* ユーザーに続行するために**明示的にボタンをタップ**させる必要がある（スワイプジェスチャーなし）
* すべてを**1つのHTML アプリ内メッセージ**内に保持してシンプルにする
* フローが完了したときにメッセージを自動的に閉じる

<Info>
  このガイドでは完全なコントロールのためにHTML アプリ内メッセージを使用します。[ドラッグアンドドロップエディタでカードベースのオンボーディングフローを構築する](./design-your-in-app-message#carousels)こともできます—それらのカードはスワイプ可能ですが、カスタマイズ性は低くなります。
</Info>

***

## 前提条件

開始する前に、以下を確認してください：

* アプリ内メッセージが有効になっているアクティブなOneSignalアプリ
* [HTML アプリ内メッセージを作成または編集する権限](./manage-team-members#team-members)
* モバイルアプリに[Mobile SDK](./mobile-sdk-setup)がインストールされている
* HTML、CSS、JavaScriptの基本的な理解

***

## マルチステップフローの仕組み

コードに入る前に、技術的なアプローチを理解することが重要です。この実装は**1つのHTML アプリ内メッセージ**を使用し、複数の別々のメッセージをロードするのではなく、**コンテンツの表示と非表示**によってステップを切り替えます。

アーキテクチャは**4つのコアコンポーネント**に依存しています：

<Steps>
  <Step title="各ステップのカードコンテナ">
    各ステップは`card`クラスと一意のIDを持つ`<div>`でラップされています：

    ```html theme={null}
    <div id="card-0" class="card active">...</div>
    <div id="card-1" class="card">...</div>
    ```

    * すべてのカードは同時にDOMに存在します
    * 一度に1枚のカードのみが表示されます（`active`クラスで制御）
  </Step>

  <Step title="CSS可視性コントロール">
    CSSは透明度とポインターイベントを使用して表示/非表示ロジックを処理します：

    ```css theme={null}
    .card {
      opacity: 0;
      pointer-events: none;  /* 非表示カードとの対話を防止 */
      transition: opacity .25s ease;
    }

    .card.active {
      opacity: 1;
      pointer-events: auto;  /* 表示カードとの対話を許可 */
    }
    ```

    **これが重要な理由：**

    * `opacity: 0`はカードを視覚的に非表示にしますが、レイアウトには残ります
    * `pointer-events: none`は非表示カードへの誤クリックを防止します
    * `transition`はスムーズなフェード効果を作成します
  </Step>

  <Step title="JavaScript状態管理">
    `setActive(i)`関数がどのカードを表示するかを制御します：

    ```javascript theme={null}
    function setActive(i) {
      // カードの可視性を更新
      document.getElementById("card-0").className = i === 0 ? "card active" : "card";
      document.getElementById("card-1").className = i === 1 ? "card active" : "card";

      // 進行状況ドットを更新
      var dots = document.getElementById("dots").children;
      dots[0].classList.toggle("active", i === 0);
      dots[1].classList.toggle("active", i === 1);
    }
    ```

    この関数は：

    * すべてのカードから`active`を削除
    * ターゲットカードに`active`を追加
    * 進行状況インジケータードットを更新
  </Step>

  <Step title="ボタンイベントリスナー">
    ボタンがナビゲーションまたは終了をトリガーします：

    ```javascript theme={null}
    // 次のステップに進む
    document.getElementById("next-0").addEventListener("click", function () {
      setActive(1);
    });

    // アプリ内メッセージを閉じる
    document.getElementById("done").addEventListener("click", function (e) {
      if (window.OneSignalIamApi && OneSignalIamApi.close) {
        OneSignalIamApi.close(e);
      }
    });
    ```

    **重要：** `OneSignalIamApi.close(e)`はHTML内からアプリ内メッセージを閉じるOneSignal SDKメソッドです。
  </Step>
</Steps>

<Note>
  **重要なポイント：** これはアプリ内メッセージ内の\*\*シングルページアプリケーション（SPA）\*\*パターンです。すべてのコンテンツは一度ロードされ、JavaScriptがリロードなしで状態変更を管理します。
</Note>

***

## ステップ1：新しいHTML アプリ内メッセージを作成

1. OneSignalダッシュボードで、**Messages → In-App Messages**に移動します
2. **New In-App Message**をクリックします
3. メッセージタイプとして**HTML**を選択します
4. **Full Screen**または**Large**レイアウトを選択します（視覚的インパクトを最大化するためオンボーディングに推奨）
5. HTMLエディタに進みます

<Note>
  HTMLエディタのプレビューはランタイムの動作を完全に反映しない場合があります。アニメーション、ボタンの動作、閉じるアクションを確認するために、必ず実際のデバイスまたはテストユーザーでテストしてください。
</Note>

***

## ステップ2：HTMLテンプレートを追加

エディタの内容を以下のテンプレートに置き換えます。このテンプレートには以下が含まれます：

* **自己完結型コード：** すべてのHTML、CSS、JavaScriptが1つのファイルに
* **ボタン駆動ナビゲーション：** スワイプジェスチャーなし（デバイス間でより信頼性が高い）
* **フェード遷移：** ステップ間のスムーズな透明度変化
* **OneSignal SDK統合：** メッセージを閉じるために`OneSignalIamApi.close(e)`を使用
* **モバイル最適化：** viewportメタタグを使用したレスポンシブレイアウト

<Accordion title="完全なHTMLテンプレートを表示">
  ```html theme={null}
  <!doctype html>
  <html>
  <head>
    <meta charset="UTF-8" />
    <!-- viewport-fit=coverはノッチ付きデバイスでセーフエリアをカバーします -->
    <meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover" />
    <style>
      /* ベーススタイル - リセットとシステムフォント */
      html, body {
        margin: 0;
        padding: 0;
        background: #ffffff;
        font-family: -apple-system, system-ui;
      }

      /* パディング付きメインコンテナ */
      .wrap {
        padding: 28px 22px 24px;
      }

      /* ステージコンテナ - すべてのカードを同じ位置に保持 */
      .stage {
        position: relative;
        min-height: 74vh;  /* 十分な垂直スペースを確保 */
      }

      /* カード - オンボーディングフローの各ステップ */
      .card {
        position: absolute;  /* すべてのカードが同じ位置に重なる */
        inset: 0;            /* ステージを完全にカバー */
        display: flex;
        flex-direction: column;
        align-items: center;
        opacity: 0;               /* デフォルトで非表示 */
        pointer-events: none;     /* 非表示時のクリックを防止 */
        transition: opacity .25s ease;  /* スムーズなフェード効果 */
      }

      /* アクティブカードは表示され、対話可能 */
      .card.active {
        opacity: 1;
        pointer-events: auto;
      }

      /* タイポグラフィ */
      h1 {
        margin: 44px 0 12px;
        font-size: 26px;
        text-align: center;
      }

      p {
        margin: 0;
        color: #6b7280;
        text-align: center;
        max-width: 260px;
        line-height: 1.35;
      }

      /* 画像コンテナ - 角丸の正方形 */
      .image {
        width: 240px;
        height: 240px;
        border-radius: 16px;
        margin: 24px 0 12px;
        background-size: cover;
        background-position: center;
      }

      /* プライマリボタン */
      .btn {
        margin-top: auto;  /* ボタンをカードの下部に押し出す */
        width: 100%;
        max-width: 260px;
        height: 52px;
        border: 0;
        border-radius: 12px;
        background: #3b82f6;  /* 青 - ブランドに合わせてカスタマイズ */
        color: #fff;
        font-size: 18px;
        font-weight: 600;
      }

      /* 進行状況インジケータードット */
      .dots {
        display: flex;
        justify-content: center;
        gap: 8px;
        padding: 12px 0 8px;
      }

      .dot {
        width: 8px;
        height: 8px;
        border-radius: 999px;
        background: #d1d5db;  /* 非アクティブドットの色 */
      }

      .dot.active {
        background: #6b7280;  /* アクティブドットの色 */
        transform: scale(1.15);  /* アクティブ時にわずかに大きく */
      }
    </style>
  </head>

  <body>
    <div class="wrap">
      <div class="stage">

        <!-- ステップ1：ウェルカムカード（"active"クラスで表示開始） -->
        <div id="card-0" class="card active">
          <h1>ようこそ</h1>
          <div
            class="image"
            style="background-image: url('https://images.pexels.com/photos/6153129/pexels-photo-6153129.jpeg');">
          </div>
          <p>数分で穏やかな日課を築きましょう。</p>
          <button
            id="next-0"
            class="btn"
            data-onesignal-unique-label="onboarding_next_0">
            次へ
          </button>
        </div>

        <!-- ステップ2：ブリーズカード（非表示で開始、ユーザーが「次へ」をタップすると表示） -->
        <div id="card-1" class="card">
          <h1>呼吸</h1>
          <div
            class="image"
            style="background-image: url('https://images.pexels.com/photos/417173/pexels-photo-417173.jpeg');">
          </div>
          <p>リセットが必要なときはいつでもガイド付き呼吸法を。</p>
          <button
            id="done"
            class="btn"
            data-onesignal-unique-label="onboarding_done">
            始める
          </button>
        </div>

      </div>

      <!-- 進行状況インジケーター：2つのドット、最初がアクティブで開始 -->
      <div class="dots" id="dots">
        <div class="dot active"></div>
        <div class="dot"></div>
      </div>
    </div>

    <script>
      (function () {
        /**
         * "active"クラスを切り替えてカード間を切り替える
         * @param {number} i - 表示するカードのインデックス（0または1）
         */
        function setActive(i) {
          // カードの可視性を更新
          document.getElementById("card-0").className = i === 0 ? "card active" : "card";
          document.getElementById("card-1").className = i === 1 ? "card active" : "card";

          // 進行状況ドットを更新
          var dots = document.getElementById("dots").children;
          dots[0].classList.toggle("active", i === 0);
          dots[1].classList.toggle("active", i === 1);
        }

        // ボタン：次へ（カード0 → カード1）
        document.getElementById("next-0").addEventListener("click", function () {
          setActive(1);
        });

        // ボタン：始める（アプリ内メッセージを閉じる）
        document.getElementById("done").addEventListener("click", function (e) {
          // OneSignal IAM APIが利用可能か確認
          if (window.OneSignalIamApi && OneSignalIamApi.close) {
            OneSignalIamApi.close(e);  // メッセージを閉じる
          }
        });
      })();
    </script>
  </body>
  </html>
  ```
</Accordion>

***

## ステップ3：コンテンツをカスタマイズ

### 安全にカスタマイズできる項目

機能を壊さずにこれらの要素を変更できます：

**コンテンツ：**

* `<h1>`タグ内の見出しテキスト
* `<p>`タグ内の本文コピー
* ボタンラベル（`次へ`、`始める`）
* `background-image: url('...')`スタイル内の画像URL

**視覚的スタイル：**

* 色：`.btn`の背景、テキストカラー、またはドットの色を変更
* 間隔：パディングとマージンを調整
* タイポグラフィ：font-family、font-size、font-weightを変更
* 角丸：ボタンと画像の`border-radius`値を更新

### ステップを追加

3番目のステップを追加するには、次のパターンに従います：

1. **HTMLカードを追加：**

```html theme={null}
<div id="card-2" class="card">
  <h1>あなたのタイトル</h1>
  <div class="image" style="background-image: url('your-image-url');"></div>
  <p>あなたの説明</p>
  <button id="next-2" class="btn">次へ</button>
</div>
```

2. **進行状況ドットを追加：**

```html theme={null}
<div class="dots" id="dots">
  <div class="dot active"></div>
  <div class="dot"></div>
  <div class="dot"></div> <!-- 新しいドット -->
</div>
```

3. **`setActive()`関数を更新：**

```javascript theme={null}
function setActive(i) {
  document.getElementById("card-0").className = i === 0 ? "card active" : "card";
  document.getElementById("card-1").className = i === 1 ? "card active" : "card";
  document.getElementById("card-2").className = i === 2 ? "card active" : "card"; // 新しいカード

  var dots = document.getElementById("dots").children;
  dots[0].classList.toggle("active", i === 0);
  dots[1].classList.toggle("active", i === 1);
  dots[2].classList.toggle("active", i === 2); // 新しいドット
}
```

4. **前のステップのボタンIDを更新：**
   カード1のボタンで`id="done"`を`id="next-1"`に変更し、クリックリスナーを追加：

```javascript theme={null}
document.getElementById("next-1").addEventListener("click", function () {
  setActive(2);
});
```

5. **新しい最後のカード（card-2）に閉じるボタンを追加：**

```javascript theme={null}
document.getElementById("done").addEventListener("click", function (e) {
  if (window.OneSignalIamApi && OneSignalIamApi.close) {
    OneSignalIamApi.close(e);
  }
});
```

<Warning>
  オンボーディングフローは短く保ちましょう（最大2-4ステップ）。ユーザーは長いフローではすぐに離脱します。[クリックトラッキング](./in-app-message-api#click-name)で完了率をテストしてください。
</Warning>

***

## ステップ4：アプリ内メッセージをテスト

### テストチェックリスト

1. OneSignalダッシュボードでメッセージを**保存**
2. **配信設定を構成：**
   * トリガー条件を設定（例：セッション開始、特定のページビュー）
   * ターゲットオーディエンスを選択するか、テストユーザーを選択
3. **テストデバイスに送信：**
   * [テストユーザー](./test-users)を使用して本番ユーザーに影響を与えずにプレビュー
   * 物理デバイスにアプリをインストール（正確な動作のためシミュレーターより推奨）
4. **機能を確認：**
   * ✓ 最初のカードが正しいコンテンツで表示される
   * ✓ 「次へ」ボタンがカード2に進む
   * ✓ 進行状況ドットが正しく更新される
   * ✓ フェード遷移がスムーズ
   * ✓ 「始める」ボタンがメッセージを閉じる
   * ✓ メッセージがすぐに再表示されない（頻度制限設定を確認）

<Note>
  シミュレーター/エミュレーターは、特にタッチ操作やSDK統合において、実際のデバイスの動作を正確に反映しない場合があります。本番環境にリリースする前に、必ず物理デバイスでテストしてください。
</Note>

### 一般的な問題のトラブルシューティング

| 問題               | 考えられる原因                 | 解決策                                                      |
| ---------------- | ----------------------- | -------------------------------------------------------- |
| メッセージが表示されない     | トリガー条件が満たされていない         | [アプリ内メッセージトリガー](./iam-triggers)を確認し、テストユーザーが条件を満たしているか確認 |
| ボタンが動作しない        | JavaScriptエラーまたはIDの不一致  | ブラウザコンソールでエラーを確認；ボタンIDがイベントリスナーIDと一致しているか確認              |
| 画像が読み込まれない       | CORSの問題または無効なURL        | HTTPS URLを使用；まずブラウザで画像URLをテスト                            |
| メッセージは表示されるが閉じない | OneSignal SDKが読み込まれていない | [Mobile SDKセットアップ](./mobile-sdk-setup)が完了しているか確認         |

***

## 次のステップ

**ユーザーエンゲージメントをトラッキング：**

* ステップ間の離脱を測定するために[`data-onesignal-unique-label`](./in-app-message-api#click-name)属性を使用してクリックトラッキングを追加（テンプレートに既に含まれています）
* **Messages → In-App Messages → \[あなたのメッセージ] → Analytics**でクリック分析を表示

**エクスペリエンスをパーソナライズ：**

* オンボーディングを完了した[ユーザーにタグを付ける](./in-app-message-api#tag-user)（例：`onboarding_completed: true`）
* タグを使用して[ユーザーをセグメント化](./segmentation)し、オンボーディングフローの再表示を防止
* [ユーザーデータを追加](./add-user-data-tags)して将来のメッセージでコンテンツをパーソナライズ

**高度なカスタマイズ：**

* 終了後にユーザーを特定の画面に[ディープリンク](./deep-linking#send-in-app-messages-with-deep-links)
* [Liquid構文](./using-liquid-syntax)を使用してユーザー名や属性で見出しをパーソナライズ
* 完了率を最適化するために異なるオンボーディングフローでA/Bテストを実装
