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

# الأحداث

> كل أحداث Aftersell Cart SDK: متى يُطلق كل منها، وما الذي يمرره لك، وفيمَ تستخدمه، والأخطاء التي تسبب حلقات لا نهائية.

تتيح لك الأحداث تشغيل كود **عندما يحدث شيء ما** في السلة. توجد تحت `window.aftersell.cart.events`.

الاشتراك هو استدعاء إعداد، لذا فهو آمن في أعلى سكربتك، دون حاجة لانتظار `ready()`.

<div id="available-events">
  ## الأحداث المتاحة
</div>

| الحدث                                         | الحمولة                                               | يُطلق عندما                             |
| --------------------------------------------- | ----------------------------------------------------- | --------------------------------------- |
| [`cart_loaded`](#cart_loaded)                 | [`AftersellCart`](/ar/aftersell/cart/sdk-cart-object) | تُحمَّل السلة، مرة واحدة لكل صفحة.      |
| [`cart_updated`](#cart_updated)               | [`AftersellCart`](/ar/aftersell/cart/sdk-cart-object) | تتغير محتويات السلة، بعد التحميل الأول. |
| [`item_added`](#item_added)                   | `{ item }`                                            | يظهر بند جديد في السلة.                 |
| [`item_removed`](#item_removed)               | `{ item }`                                            | يختفي بند من السلة.                     |
| [`cart_opened`](#cart_opened-and-cart_closed) | لا شيء                                                | يفتح الدرج.                             |
| [`cart_closed`](#cart_opened-and-cart_closed) | لا شيء                                                | يُغلق الدرج.                            |
| [`checkout`](#checkout)                       | لا شيء                                                | يُنقر زر إتمام الشراء.                  |

<div id="subscribing">
  ## الاشتراك
</div>

يسجّل `events.on(event, handler)` معالجًا و**يُرجع دالة تلغي اشتراكه**:

```js theme={"theme":{"light":"snazzy-light","dark":"github-dark"}}
const off = window.aftersell.cart.events.on('cart_updated', (state) => {
  console.log('Cart total is now', state.totalPrice);
});

// later, to stop listening:
off();
```

* `events.once(event, handler)`: يعمل مرة واحدة، ثم يلغي اشتراكه بنفسه.
* `events.off(event, handler)`: يزيل معالجًا محددًا.

المعالج الذي يرمي خطأً يُعزل ويُسجَّل في وحدة التحكم؛ وتستمر بقية المعالجات في العمل.

***

<div id="the-two-rules">
  ## القاعدتان
</div>

يعود كل خطأ متعلق بالأحداث تقريبًا إلى إحدى هاتين القاعدتين.

<div id="dont-change-the-cart-from-cart_updated-without-a-guard">
  ### لا تغيّر السلة من `cart_updated` دون حارس
</div>

تغيير السلة داخل معالج `cart_updated` يطلق `cart_updated` مرة أخرى. وإذا غيّر ذلك المعالج السلة مجددًا، فلديك حلقة لا نهائية. سيشاهد المتسوق سلته تضطرب بينما تقصف الصفحة Shopify بالطلبات.

<Warning>
  **لا تستدعِ أبدًا إجراءً دون شرط من داخل `cart_updated` أو `cart_loaded`.** احمِه بفحص للحالة التي أنت على وشك إنشائها، بحيث لا تفعل التمريرة الثانية شيئًا.
</Warning>

```js theme={"theme":{"light":"snazzy-light","dark":"github-dark"}}
// ❌ Loops forever: every add triggers an update, which triggers another add.
window.aftersell.cart.events.on('cart_updated', (state) => {
  window.aftersell.cart.actions.addItem(GIFT_VARIANT_ID, 1);
});

// ✅ Guarded: once the gift is present, the condition is false and it stops.
window.aftersell.cart.events.on('cart_updated', (state) => {
  const hasGift = state.items.some((line) => line.variantId === GIFT_VARIANT_ID);
  if (state.totalPrice >= 5000 && !hasGift) {
    window.aftersell.cart.actions.addItem(GIFT_VARIANT_ID, 1);
  }
});
```

تمنحك السلة شبكة أمان واحدة: التحديث الذي ينتج سلة **مطابقة** لا يُصدر شيئًا، لذا فإن إعادة الجلب التي لا تغيّر شيئًا لن تعيد تشغيل الدورة. هذا يحميك من حلقات عديمة الأثر عرضية. لكنه **لا** يحميك من معالج يغيّر السلة فعليًا في كل مرة.

<div id="treat-the-payload-as-read-only">
  ### تعامل مع الحمولة على أنها للقراءة فقط
</div>

كل معالج لحدث واحد يستقبل الكائن *نفسه*. تعديله يغيّر ما تراه المعالجات التالية لمعالجك، بما فيها معالجات تخص تطبيقات أخرى على المتجر.

```js theme={"theme":{"light":"snazzy-light","dark":"github-dark"}}
// ❌ Corrupts the payload for every later handler.
window.aftersell.cart.events.on('cart_updated', (state) => {
  state.items = state.items.filter((line) => line.finalLinePrice > 0);
});

// ✅ Copy first.
window.aftersell.cart.events.on('cart_updated', (state) => {
  const paidItems = state.items.filter((line) => line.finalLinePrice > 0);
});
```

لتغيير السلة فعليًا، استخدم [إجراءً](/ar/aftersell/cart/sdk-actions). ولتغيير كيفية عرض البنود، استخدم [`registerLineTransform`](/ar/aftersell/cart/sdk-hooks#registerlinetransform).

***

<div id="cart_loaded">
  ## cart\_loaded
</div>

يُطلق **مرة واحدة**، عندما تُحمَّل السلة أول مرة على الصفحة. الحمولة هي [كائن السلة](/ar/aftersell/cart/sdk-cart-object) الكامل.

```js theme={"theme":{"light":"snazzy-light","dark":"github-dark"}}
window.aftersell.cart.events.on('cart_loaded', (state) => {
  console.log('Page loaded with', state.itemCount, 'items');
});
```

**استخدمه من أجل:** أي شيء يحتاج إلى العمل على حالة السلة الابتدائية، مثل مطابقة هدية مجانية، أو تهيئة أداة، أو إبلاغ التحليلات بمحتويات السلة عند تحميل الصفحة.

**يُعاد تشغيل `cart_loaded` للمشتركين المتأخرين.** إذا اشتركت بعد تحميل السلة بالفعل، يُستدعى معالجك فورًا بالسلة الحالية. ترتيب الاشتراك لا يهم أبدًا، لذا لا داعي للقلق مما إذا كان سكربتك قد سبق السلة.

<Tip>
  المنطق الذي يجب أن يكون صحيحًا عند تحميل الصفحة وعند كل تغيير لاحق ينبغي أن يشترك في **كلٍ من** `cart_loaded` و`cart_updated` بالدالة نفسها. هذا هو النمط القياسي لـ "إبقاء X متزامنًا مع السلة".
</Tip>

<div id="cart_updated">
  ## cart\_updated
</div>

يُطلق في كل مرة تتغير فيها محتويات السلة **بعد** التحميل الأول، سواء من الدرج أو من إجراءاتك أو من القالب أو من تطبيق آخر. الحمولة هي [كائن السلة](/ar/aftersell/cart/sdk-cart-object) الكامل.

```js theme={"theme":{"light":"snazzy-light","dark":"github-dark"}}
window.aftersell.cart.events.on('cart_updated', (state) => {
  document.querySelector('#my-total').textContent =
    window.aftersell.cart.actions.formatMoney(state.totalPrice);
});
```

**استخدمه من أجل:** إبقاء شيء خارج السلة متزامنًا، مثل إجمالي مخصص أو شريط تقدم أو شارة ترويسة أو حدث تحليلات عند كل تغيير.

التحديث الذي ينتج سلة مطابقة لا يُصدر شيئًا. إعادة فتح الدرج، أو العودة إلى التبويب، أو إعادة الجلب التي تُرجع المحتويات نفسها لن تطلقه.

<Warning>
  أعد قراءة [القاعدتين](#the-two-rules) قبل استدعاء إجراء هنا.
</Warning>

<div id="item_added">
  ## item\_added
</div>

يُطلق عندما يظهر **بند جديد** في السلة. الحمولة هي `{ item }`، حيث `item` هو [بند السلة](/ar/aftersell/cart/sdk-cart-object#cart-lines).

```js theme={"theme":{"light":"snazzy-light","dark":"github-dark"}}
window.aftersell.cart.events.on('item_added', (payload) => {
  myAnalytics.track('Added to cart', {
    id: payload.item.variantId,
    title: payload.item.title,
    quantity: payload.item.quantity,
  });
});
```

**استخدمه من أجل:** تتبّع الإضافة إلى السلة في أداة تحليلات تابعة لطرف ثالث. هذا هو الاستخدام الأكثر شيوعًا لـ SDK على الإطلاق. راجع [تتبع الإضافة إلى السلة](/ar/aftersell/cart/sdk-use-case-analytics).

أمران يجب معرفتهما حول كيفية اشتقاقه:

<Warning>
  **تغيير الكمية ليس إضافة.** تستنتج السلة الإضافات والإزالات بمقارنة *البنود*، لا الكميات. متسوق يرفع بندًا من 1 إلى 3 يطلق `cart_updated` وليس `item_added`. إذا احتجت إلى التقاط زيادات الكمية أيضًا، فقارن مع الحالة السابقة داخل معالج `cart_updated`.
</Warning>

كما أنه لا يُطلق للعناصر التي كانت في السلة بالفعل عند تحميل الصفحة؛ فتلك تصل عبر `cart_loaded`. إضافة عدة منتجات مختلفة دفعة واحدة تطلق الحدث مرة لكل بند.

<div id="item_removed">
  ## item\_removed
</div>

يُطلق عندما يختفي بند من السلة. الحمولة هي `{ item }`، وهي البند كما كان قبل اختفائه مباشرةً، لذا لا يزال بإمكانك قراءة `key` و`variantId` و`title` الخاصة به.

```js theme={"theme":{"light":"snazzy-light","dark":"github-dark"}}
window.aftersell.cart.events.on('item_removed', (payload) => {
  console.log('Removed', payload.item.title);
});
```

**استخدمه من أجل:** عكس شيء فعلته عند الإضافة، مثل مسح علامة، أو إعادة إظهار عرض رفضه المتسوق، أو إبلاغ التحليلات بعمليات الإزالة.

نفس التحذير الخاص بـ `item_added`: خفض الكمية دون بلوغ الصفر ليس إزالة.

<div id="cart_opened-and-cart_closed">
  ## cart\_opened وcart\_closed
</div>

يُطلقان عند فتح الدرج وإغلاقه. بلا حمولة.

```js theme={"theme":{"light":"snazzy-light","dark":"github-dark"}}
window.aftersell.cart.events.on('cart_opened', () => {
  myAnalytics.track('Cart viewed');
});

window.aftersell.cart.events.on('cart_closed', () => {
  document.body.classList.remove('cart-is-open');
});
```

**استخدمهما من أجل:** تتبّع المشاهدات، أو إيقاف فيديو أو عرض شرائح خلف الدرج مؤقتًا، أو تبديل صنف (class) على الصفحة.

لا يُطلق أي منهما عند التحميل الأولي للصفحة، بل فقط عند فتح أو إغلاق فعلي.

<div id="checkout">
  ## checkout
</div>

يُطلق عندما ينقر المتسوق زر إتمام الشراء، مباشرةً قبل انتقال المتصفح. بلا حمولة.

```js theme={"theme":{"light":"snazzy-light","dark":"github-dark"}}
window.aftersell.cart.events.on('checkout', () => {
  myAnalytics.track('Checkout started');
});
```

**استخدمه من أجل:** تتبّع نية إتمام الشراء.

<Warning>
  **لا يمكنك إلغاء إتمام الشراء من هذا المعالج.** الحدث إشعار وليس بوابة؛ يحدث الانتقال بغض النظر عما يفعله كودك. أبقِ المعالج سريعًا ومتزامنًا: قد لا يكتمل `await` أو استدعاء شبكة بطيء قبل تفريغ الصفحة. استخدم [`navigator.sendBeacon`](https://developer.mozilla.org/en-US/docs/Web/API/Navigator/sendBeacon) لأي شيء تحتاج إلى إرساله بشكل موثوق.
</Warning>

***

<div id="listening-from-outside-the-sdk">
  ## الاستماع من خارج SDK
</div>

يُبثّ كل حدث أيضًا كـ `CustomEvent` من DOM على `window`، لذا يمكنك الاستماع دون لمس `window.aftersell.cart`. هذا مفيد من ملف قالب أو تطبيق تابع لطرف ثالث أو سكربت يُحمَّل مستقلًا عن السلة.

| حدث الناقل     | حدث DOM                       |
| -------------- | ----------------------------- |
| `cart_loaded`  | `aftersell:cart:cart-loaded`  |
| `cart_updated` | `aftersell:cart:cart-updated` |
| `item_added`   | `aftersell:cart:item-added`   |
| `item_removed` | `aftersell:cart:item-removed` |
| `cart_opened`  | `aftersell:cart:cart-opened`  |
| `cart_closed`  | `aftersell:cart:cart-closed`  |
| `checkout`     | `aftersell:cart:checkout`     |

انتبه للتسمية: يستخدم الناقل `snake_case`، بينما تستخدم أحداث DOM صيغة `kebab-case` خلف البادئة `aftersell:cart:`.

```js theme={"theme":{"light":"snazzy-light","dark":"github-dark"}}
window.addEventListener('aftersell:cart:cart-updated', (event) => {
  console.log('Cart total is now', event.detail.totalPrice);
});
```

تصل الحمولة على `event.detail` وتطابق [كائن السلة](/ar/aftersell/cart/sdk-cart-object). تُبثّ الأحداث على `window`، لذا يستقبلها مستمع في أي مكان على الصفحة. تُعرض السلة داخل shadow root، لكن حد الظل لا يقع أبدًا في مسار الحدث. كل بثّ يستنسخ الحمولة، لذا فإن مستمعًا يعدّل `event.detail` لا يمكن أن يؤثر على أي طرف آخر، ومستمعًا يرمي خطأً لا يمكن أن يعطّل SDK.

<Warning>
  **لا يُعاد تشغيل `cart-loaded` على DOM.** يعيد الناقل تشغيل `cart_loaded` للمشتركين المتأخرين، لكن ذلك المسار يتجاوز بثّ DOM، لذا فإن `window.addEventListener('aftersell:cart:cart-loaded')` المسجَّل بعد تحميل السلة بالفعل لن يعمل أبدًا. إذا لم يكن ترتيب تحميل سكربتك مضمونًا، فاستخدم `window.aftersell.cart.events.on('cart_loaded', …)` الذي يُعاد تشغيله، أو استمع أيضًا إلى `aftersell:cart:cart-updated`.
</Warning>

<div id="shopify-standard-cart-events">
  ### أحداث سلة Shopify القياسية
</div>

بشكل منفصل، تنشر السلة [أحداث السلة القياسية](https://shopify.dev/docs/storefronts/themes/best-practices/standard-events) الخاصة بـ Shopify على `document` كلما غيّرت السلة، بحيث يستطيع كود القالب والتطبيقات الأخرى التفاعل مع تعديلات Aftersell بالطريقة نفسها التي تتفاعل بها مع تعديلات القالب:

| الحدث                          | الحمولة على كائن الحدث                                                           |
| ------------------------------ | -------------------------------------------------------------------------------- |
| `shopify:cart:lines-update`    | `action: 'add' \| 'update' \| 'remove'`، `context: 'cart' \| 'product'`، `lines` |
| `shopify:cart:note-update`     | `context: 'cart'`، `note`                                                        |
| `shopify:cart:discount-update` | `discountCodes: [{ code }]`                                                      |

<Warning>
  **الحمولة ليست على `event.detail`.** يحمل `detail` فقط `{ source: 'aftersell' }` — الوسم الذي تستخدمه السلة لتجاهل أحداثها الخاصة بدلًا من الدوران في حلقة. كل ما في الجدول أعلاه يُسند مباشرةً على كائن الحدث، لذا اقرأ `event.action` وليس `event.detail.action`.
</Warning>

يحمل كل حدث أيضًا `promise` يسوّيه Aftersell عند اكتمال عملية الكتابة الأساسية، بما يطابق معيار Shopify — انتظره بـ await ولا تحلّه بنفسك. تُبثّ هذه الأحداث على `document` وتتصاعد، لذا يستقبلها مستمع على `window` أيضًا.

<div id="where-to-go-next">
  ## إلى أين تذهب بعد ذلك
</div>

* **[كائن السلة](/ar/aftersell/cart/sdk-cart-object)**: الشكل الكامل للحمولات أعلاه.
* **[الإجراءات](/ar/aftersell/cart/sdk-actions)**: كيفية تغيير السلة من داخل معالج.
* **[الخطافات](/ar/aftersell/cart/sdk-hooks)**: لتغيير كيفية عرض السلة، بدلًا من التفاعل معها.
* **[حالات الاستخدام](/ar/aftersell/cart/sdk-use-cases)**: تتبّع التحليلات والهدايا المجانية وأمثلة كاملة أخرى.
