> ## 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.

# Пользовательские скрипты

> Запускайте пользовательский JavaScript в Aftersell Cart с помощью слотов скриптов Initialization и On cart update.

Пользовательские скрипты позволяют запускать ваш собственный JavaScript для корзины с помощью [Cart SDK](/ru/aftersell/cart/sdk-overview). Добавляйте их в редакторе корзины в разделе **Cart settings → Custom script**, где выпадающий список переключает два слота: **Initialization** и **On cart update**.

Пишите в этих редакторах обычный JavaScript, без тегов `<script>`. У **On cart update** есть действие **Reset to default**, восстанавливающее его стартовый шаблон; у **Initialization** его нет, поэтому сохраните собственную копию, прежде чем очищать его.

<Note>
  Многое из того, что мерчанты раньше делали скриптами, теперь является встроенной настройкой. Сначала посмотрите [Прежде чем писать скрипт](/ru/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`](/ru/aftersell/cart/sdk-configure), [`events.on`](/ru/aftersell/cart/sdk-events), [`hooks.register*`](/ru/aftersell/cart/sdk-hooks). | Правил, которые нужно переоценивать по текущему содержимому корзины. |
| **Пример**          | Скрыть строки бесплатных подарков через трансформацию строк.                                                                                                                         | Синхронизировать бесплатный подарок с порогом суммы.                 |

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

Скрипт **Initialization** запускается **один раз при загрузке корзины**. Это ваша точка входа для настройки: конфигурирования поведения корзины, подписки на события и регистрации хуков. [SDK](/ru/aftersell/cart/sdk-overview) доступен как `window.aftersell.cart`.

Вызовы настройки, которые вы делаете здесь ([`configure(...)`](/ru/aftersell/cart/sdk-configure), [`events.on(...)`](/ru/aftersell/cart/sdk-events), [`hooks.*`](/ru/aftersell/cart/sdk-hooks)), безопасно вызывать в начале скрипта даже до полной загрузки корзины; они буферизуются и применяются после её готовности. Действия, читающие или изменяющие корзину (такие как [`addItem`](/ru/aftersell/cart/sdk-actions#additemvariantid-quantity) или [`getCart`](/ru/aftersell/cart/sdk-actions#getcart)), должны выполняться внутри [`ready()`](/ru/aftersell/cart/sdk-overview#ready) или обработчика события.

Слот изначально содержит три **закомментированных** примера — открытие выдвижной корзины при каждом добавлении, реакция на `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`](/ru/aftersell/cart/sdk-hooks#registerlinetransform) выполняется для каждой строки при её отображении, а `setHidden` действует только на отображение, поэтому строка остаётся в корзине и по-прежнему учитывается в итоге — она просто не показывается в выдвижной корзине. Подробнее о возможностях трансформации см. [Скрытие и переименование строк корзины](/ru/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`](/ru/aftersell/cart/sdk-overview#shadowroot): корзина отображается внутри shadow root, поэтому `document.querySelector` не видит ничего в выдвижной корзине.

<Tip>
  Ветвитесь по рынку, стране или валюте **до** загрузки корзины? Вместо этого читайте [`context`](/ru/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` заново запускает его. Опишите желаемое состояние, сравните с текущим и завершайте выполнение раньше, когда они уже совпадают, чтобы обработчик сходился за один проход, а не зацикливался. См. [два правила](/ru/aftersell/cart/sdk-events#the-two-rules) — о незащищённом варианте, которого следует избегать, и о том, почему полезная нагрузка доступна только для чтения.

В медленном магазине также стоит держать флаг выполнения на уровне модуля, чтобы два быстрых изменения не могли начать добавление одновременно, пока первое не завершилось.

<Note>
  `cart_updated` срабатывает только при изменениях **после** первой загрузки ([тайминг событий](/ru/aftersell/cart/sdk-events#cart_updated)), поэтому скрипт в этом слоте не согласует корзину, которая уже соответствует условию при загрузке страницы. Для версии, обрабатывающей оба случая, подпишитесь на `cart_loaded` и `cart_updated` одной и той же функцией из слота **Initialization**. См. [Автодобавление бесплатного подарка при пороге](/ru/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`), поэтому вы можете открыть любой из них в панели Sources DevTools и поставить точки останова. См. [Отладка](/ru/aftersell/cart/sdk-overview#debugging) для точных сообщений и канала отладки, который перехватывает сбои хуков, не попадающие в консоль.

Поскольку `cart_loaded` [воспроизводится для поздних подписчиков](/ru/aftersell/cart/sdk-events#cart_loaded), порядок регистрации не имеет значения. Самая безопасная структура — сначала зарегистрировать всё, а рискованную работу выполнять внутри обработчиков, где выброшенное исключение изолируется в пределах этого обработчика.

<div id="where-to-go-next">
  ## Что дальше
</div>

* **[Cart SDK](/ru/aftersell/cart/sdk-overview)**: пользовательские скрипты — это способ запуска SDK-кода. См. справочники по [configure](/ru/aftersell/cart/sdk-configure), [событиям](/ru/aftersell/cart/sdk-events), [действиям](/ru/aftersell/cart/sdk-actions) и [хукам](/ru/aftersell/cart/sdk-hooks) для полного описания, [объект корзины](/ru/aftersell/cart/sdk-cart-object) для структуры того, что получают обработчики, и [сценарии использования](/ru/aftersell/cart/sdk-use-cases) для готовых сниппетов.
* **[Блоки пользовательского кода](/ru/aftersell/cart/custom-code-blocks)**: для добавления разметки в корзину. Обратите внимание, что HTML-режим блока Custom code **не** выполняет JavaScript; используйте пользовательские скрипты (или React-режим блока) для логики.
