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

# カスタムスクリプト

> Initialization と On cart update のスクリプトスロットを使って、Aftersell Cart でカスタム JavaScript を実行します。

カスタムスクリプトを使うと、[Cart SDK](/ja/aftersell/cart/sdk-overview) を使って独自の JavaScript をカートに対して実行できます。カートエディタの **Cart settings → Custom script** で追加でき、ドロップダウンで **Initialization** と **On cart update** の 2 つのスロットを切り替えられます。

これらのエディタには `<script>` タグなしのプレーンな JavaScript を記述します。**On cart update** には初期テンプレートを復元する **Reset to default** アクションがありますが、**Initialization** にはないため、削除する前に自分でコピーを保管しておいてください。

<Note>
  かつてマーチャントがスクリプトで実現していたことの多くは、現在では組み込みの設定になっています。まず[スクリプトを書く前に](/ja/aftersell/cart/sdk-use-cases#before-you-write-a-script)を確認してください。設定はカートの再設計後も機能し続けますが、スクリプトはそうとは限りません。
</Note>

<div id="which-slot-to-use">
  ## どちらのスロットを使うか
</div>

|             | Initialization                                                                                                                                                 | On cart update                           |
| ----------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------- |
| **実行タイミング** | カートの読み込み時に一度だけ。                                                                                                                                                | 初回読み込み後のカートの変更ごと。                        |
| **記述する範囲**  | スクリプト全体。                                                                                                                                                       | ハンドラ本体のみ。`cart_updated` のラッパーはロックされています。 |
| **用途**      | 動作を一度だけ登録する場合: [`configure`](/ja/aftersell/cart/sdk-configure)、[`events.on`](/ja/aftersell/cart/sdk-events)、[`hooks.register*`](/ja/aftersell/cart/sdk-hooks)。 | カートの現在の内容に対して再評価が必要なルール。                 |
| **例**       | ライン変換で無料ギフトのラインを非表示にする。                                                                                                                                        | 無料ギフトを購入金額のしきい値と同期させる。                   |

<div id="initialization">
  ## Initialization
</div>

**Initialization** スクリプトは**カートの読み込み時に一度だけ**実行されます。カートの動作の設定、イベントの購読、フックの登録など、セットアップのエントリーポイントです。[SDK](/ja/aftersell/cart/sdk-overview) は `window.aftersell.cart` として利用できます。

ここで行うセットアップの呼び出し（[`configure(...)`](/ja/aftersell/cart/sdk-configure)、[`events.on(...)`](/ja/aftersell/cart/sdk-events)、[`hooks.*`](/ja/aftersell/cart/sdk-hooks)）は、カートの起動が完了する前でもスクリプトの冒頭で安全に呼び出せます。バッファリングされ、起動後に適用されます。カートを読み取ったり変更したりするアクション（[`addItem`](/ja/aftersell/cart/sdk-actions#additemvariantid-quantity) や [`getCart`](/ja/aftersell/cart/sdk-actions#getcart) など）は、[`ready()`](/ja/aftersell/cart/sdk-overview#ready) またはイベントハンドラの中で実行してください。

このスロットには最初、**コメントアウトされた** 3 つの例（追加のたびにドロワーを開く、`cart_loaded` に反応する、無料ギフトのラインを非表示にする）が入っているため、手を加えていない Initialization スクリプトは何もしません。試すにはいずれかのコメントを解除するか、置き換えてください。

このスロットに自然な形は、**イベントを介さない一度きりの登録**です。動作を一度登録すれば、以降はカートがそれを適用し続けます。合計金額を変えずに無料ギフトのラインをドロワーから非表示にするのが、同梱されているその例です:

```js theme={"theme":{"light":"snazzy-light","dark":"github-dark"}}
window.aftersell.cart.hooks.registerLineTransform((line) => {
  if (line.finalLinePrice === 0) line.setHidden(true);
});
```

[`registerLineTransform`](/ja/aftersell/cart/sdk-hooks#registerlinetransform) はレンダリング時にすべてのラインに対して実行され、`setHidden` は表示のみに作用するため、ラインはカートに残り合計金額にも計上されたまま、ドロワーに表示されなくなるだけです。変換でできることの詳細は、[カートラインの非表示とラベル変更](/ja/aftersell/cart/sdk-use-case-hide-lines)を参照してください。

カートを読み取るアクションは `ready()` の中に置きます:

```js theme={"theme":{"light":"snazzy-light","dark":"github-dark"}}
window.aftersell.cart.ready().then(() => {
  const state = window.aftersell.cart.actions.getCart();
  console.log('Cart loaded with', state.itemCount, 'items');
});
```

カートの DOM にアクセスする場合も同じ待機が必要で、さらに [`shadowRoot`](/ja/aftersell/cart/sdk-overview#shadowroot) が必要です。カートは shadow root の中でレンダリングされるため、`document.querySelector` ではドロワー内の要素は見つかりません。

<Tip>
  カートの読み込み**前**にマーケット、国、通貨で分岐したい場合は、代わりに [`context`](/ja/aftersell/cart/sdk-overview#context) を読み取ってください。`ready()` なしで同期的に利用できるため、ルールが適用されない買い物客に対してはハンドラの登録自体をスキップできます。
</Tip>

<div id="on-cart-update">
  ## On cart update
</div>

**On cart update** スクリプトは、カートが変更されるたびに実行されます。`cart_updated` の購読を囲むロックされたラッパーになっており、編集できるのは本体だけで、コードは更新後の `cart` を受け取ります。

このスロットは、**カートの変更ごとに再評価が必要な**ルールのためのものです。無料ギフトのしきい値が典型的なケースです（\$75 の購入で無料のトートバッグを進呈）。答えは現在の内容次第で、内容が変わったことを教えてくれるものは他にないためです:

```js theme={"theme":{"light":"snazzy-light","dark":"github-dark"}}
window.aftersell.cart.events.on('cart_updated', (cart) => {
  const GIFT_VARIANT_ID = 1234567890;
  const THRESHOLD = 7500;   // $75.00, in cents

  let giftLine = null;
  let subtotal = 0;
  (cart.items ?? []).forEach((line) => {
    if (line.variantId === GIFT_VARIANT_ID) giftLine = line;
    else subtotal += line.finalLinePrice;   // the gift itself never counts toward the threshold
  });

  const shouldHaveGift = subtotal >= THRESHOLD;
  const hasGift = Boolean(giftLine);

  // Bail when the cart already matches. This is the part that matters: adding or
  // removing an item fires cart_updated again, so without this check the handler
  // re-enters itself forever.
  if (shouldHaveGift === hasGift) return;

  if (shouldHaveGift) window.aftersell.cart.actions.addItem(GIFT_VARIANT_ID, 1);
  else window.aftersell.cart.actions.removeItem(giftLine.key);
});
```

<div id="keeping-the-cart-in-a-desired-state">
  ### カートを望ましい状態に保つ
</div>

これを安全にしているのが `if (shouldHaveGift === hasGift) return;` の行で、この考え方はカートを望ましい状態に保つあらゆるスクリプトに一般化できます。このスロットはカートの変更に反応すると同時に変更を引き起こすため、`addItem` や `removeItem` を呼ぶたびに再入します。望む状態を記述し、現在の状態と比較して、すでに一致していれば早期リターンすることで、ハンドラはループせずに 1 回のパスで収束します。避けるべきガードなしのバージョンと、ペイロードが読み取り専用である理由については、[2 つのルール](/ja/aftersell/cart/sdk-events#the-two-rules)を参照してください。

動作の遅いストアでは、モジュールレベルの実行中フラグも保持しておく価値があります。これにより、2 つの連続した変更が、最初の追加が完了する前にどちらも追加を開始してしまうことを防げます。

<Note>
  `cart_updated` は初回読み込み**後**の変更でのみ発火するため（[イベントのタイミング](/ja/aftersell/cart/sdk-events#cart_updated)）、このスロットのスクリプトは、ページ読み込み時点ですでに条件を満たしているカートを調整しません。両方に対応するバージョンにするには、**Initialization** スロットから同じ関数で `cart_loaded` と `cart_updated` を購読してください。[しきい値で無料ギフトを自動追加する](/ja/aftersell/cart/sdk-use-case-free-gift)を参照してください。
</Note>

<div id="when-a-script-breaks">
  ## スクリプトが壊れたとき
</div>

各スロットは独自のサンドボックス内で実行されるため、壊れた **Initialization** スクリプトが **On cart update** の実行を止めることはなく、どちらもカート自体を壊すことはできません。

ただし、スロット内では実行は**最初のエラーで停止**します。その行より下はすべてスキップされるため、それ以降にある `configure`、`events.on`、`hooks.register*` は登録されません。コードは正しく見えるのに「ハンドラが発火しない」場合の典型的な原因がこれです。

カートはブラウザのコンソールで失敗した行を示し、各スロットは独自のファイル名（`aftersell-cart-init.js` と `aftersell-cart-cart-update.js`）で実行されるため、DevTools の Sources パネルからどちらも開いてブレークポイントを設定できます。具体的なメッセージや、コンソールに出ないフックの失敗を捕捉するデバッグチャンネルについては、[デバッグ](/ja/aftersell/cart/sdk-overview#debugging)を参照してください。

`cart_loaded` は[遅れて購読した場合も再送される](/ja/aftersell/cart/sdk-events#cart_loaded)ため、登録の順序は問題になりません。最も安全な構成は、まずすべてを登録し、リスクのある処理はハンドラの中で行うことです。ハンドラ内での throw はそのハンドラに隔離されます。

<div id="where-to-go-next">
  ## 次のステップ
</div>

* **[Cart SDK](/ja/aftersell/cart/sdk-overview)**: SDK コードの実行手段がカスタムスクリプトです。API 全体については [configure](/ja/aftersell/cart/sdk-configure)、[イベント](/ja/aftersell/cart/sdk-events)、[アクション](/ja/aftersell/cart/sdk-actions)、[フック](/ja/aftersell/cart/sdk-hooks)の各リファレンスを、ハンドラが受け取るデータの形については[カートオブジェクト](/ja/aftersell/cart/sdk-cart-object)を、すぐ使えるスニペットについては[ユースケース](/ja/aftersell/cart/sdk-use-cases)を参照してください。
* **[カスタムコードブロック](/ja/aftersell/cart/custom-code-blocks)**: カートにマークアップを追加するためのものです。Custom code ブロックの HTML モードは JavaScript を実行**しない**点に注意してください。ロジックにはカスタムスクリプト（またはブロックの React モード）を使用します。
