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

# Placement-Based Banners with A/B Testing and Targeting

> Attach targeted, scheduled banners to specific DOM elements using CSS selectors or data attributes, with built-in A/B testing and automatic analytics.

Banners in the Tappd Web SDK are placement-based content units that attach to specific DOM elements on your page. Unlike in-app messages — which are trigger-driven and float above your layout — banners live inside defined HTML containers and support rich targeting, scheduling, frequency capping, and A/B testing directly from the dashboard.

## Quick Start

The SDK handles fetching, rendering, and event tracking in a single call:

```javascript theme={null}
import { TappdSDK } from '@tappd/web-sdk';

const tappd = new TappdSDK({
  appId: 'YOUR_APP_ID',
  apiUrl: 'https://sdk.gotappd.com/api/v1/sdk'
});

// Identify the user first — required for targeting to work
await tappd.identify({
  external_id: 'user_123',
  email: 'john@example.com',
  name: 'John Doe'
});

// Fetch and render banners for a placement — that's all you need
await tappd.getBanners('#header-banner');
```

When you call `getBanners()`, the SDK automatically:

1. Fetches eligible banners from the API for the identified user
2. Renders them inside the target DOM element
3. Tracks display, click, and dismiss events

## Banner Placement System

Banners use a placement identifier to find the right DOM element. The Tappd dashboard lets you configure each banner's placement type.

<Tabs>
  <Tab title="CSS Selector">
    Target an element by its CSS selector. The example below attaches a banner to `<header id="header-banner">`.

    ```html theme={null}
    <!-- Your HTML -->
    <header id="header-banner">
      <!-- Banner content is injected here automatically -->
    </header>
    ```

    ```javascript theme={null}
    await tappd.getBanners('#header-banner');
    ```
  </Tab>

  <Tab title="Data Attribute">
    Target an element using a `data-tappd-banner` attribute. This keeps your CSS selectors stable even if element IDs change.

    ```html theme={null}
    <!-- Your HTML -->
    <div data-tappd-banner="promo">
      <!-- Banner content is injected here automatically -->
    </div>
    ```

    ```javascript theme={null}
    await tappd.getBanners('data-tappd-banner="promo"');
    ```
  </Tab>

  <Tab title="Both">
    When a banner is configured with `placement.type: 'both'` in the dashboard, it matches elements that satisfy either the CSS selector or the data attribute.

    ```javascript theme={null}
    // The SDK resolves the correct element automatically
    await tappd.getBanners('#header-banner');
    ```
  </Tab>
</Tabs>

## Auto-Rendering (Default)

By default, `getBanners()` renders banners directly into the target element. You can make this explicit with the `autoRender` option, and use `forceRefresh` to bypass the local cache and fetch the latest banner data from the API:

```javascript theme={null}
// Both calls below produce the same result
await tappd.getBanners('#header-banner');
await tappd.getBanners('#header-banner', { autoRender: true });

// Force a fresh fetch from the API, ignoring any cached banner data
await tappd.getBanners('#header-banner', { autoRender: true, forceRefresh: true });
```

The SDK handles the full rendering cycle for you:

* Locates the target element using the selector
* Creates and styles the banner element
* Injects content (title, message, image, CTA button)
* Appends a close button if the banner is dismissible
* Applies the configured animation (fade or slide)
* Tracks the display event automatically
* Wires up click and dismiss event handlers

## Custom UI Rendering

If you want full control over the banner's appearance, disable auto-rendering and build your own UI with the returned banner data:

```javascript theme={null}
// Fetch banner data without rendering
const banners = await tappd.getBanners('#header-banner', { autoRender: false });

banners.forEach(async ({ banner, variant }) => {
  const container = document.querySelector('#header-banner');

  const bannerEl = document.createElement('div');
  bannerEl.className = 'custom-banner';
  bannerEl.innerHTML = `
    <img src="${variant.content.imageUrl}" alt="${variant.content.title}">
    <div>
      <h3>${variant.content.title}</h3>
      <p>${variant.content.message}</p>
      <button class="cta-btn">${variant.content.cta.text}</button>
      <button class="close-btn">×</button>
    </div>
  `;

  // Track the display and store the displayId for later
  const displayId = await tappd.banners.display(banner._id, variant._id, '#header-banner');
  bannerEl.dataset.displayId = displayId;
  bannerEl.dataset.bannerId = banner._id;

  // Track CTA click
  bannerEl.querySelector('.cta-btn').addEventListener('click', async () => {
    await tappd.banners.click(banner._id, displayId);
    window.location.href = variant.content.cta.link;
  });

  // Track dismiss
  bannerEl.querySelector('.close-btn').addEventListener('click', async () => {
    await tappd.banners.dismiss(banner._id, displayId);
    bannerEl.remove();
  });

  container.appendChild(bannerEl);
});
```

## Manual Event Tracking

Use these methods when implementing a custom UI. For auto-rendered banners, event tracking is handled for you.

<CodeGroup>
  ```javascript Track Display theme={null}
  // Returns a displayId — store it to track subsequent interactions
  const displayId = await tappd.banners.display(
    bannerId,   // Banner ID
    variantId,  // Variant ID
    selector    // Placement selector, e.g. '#header-banner'
  );
  ```

  ```javascript Track Click theme={null}
  await tappd.banners.click(bannerId, displayId);
  ```

  ```javascript Track Dismiss theme={null}
  await tappd.banners.dismiss(bannerId, displayId);
  ```
</CodeGroup>

## Banner Response Structure

`getBanners()` returns an array of objects, each containing a `banner` and a `variant`:

```json theme={null}
[
  {
    "banner": {
      "_id": "banner_id_123",
      "name": "Summer Sale Banner",
      "placement": {
        "type": "css_selector",
        "cssSelector": "#header-banner",
        "dataAttribute": null,
        "dataAttributeValue": null
      }
    },
    "variant": {
      "_id": "variant_id_456",
      "name": "Variant A",
      "content": {
        "title": "Summer Sale",
        "message": "Get 50% off on all items!",
        "imageUrl": "https://example.com/banner.jpg",
        "link": "https://example.com/sale",
        "cta": {
          "text": "Shop Now",
          "link": "https://example.com/sale",
          "action": "link"
        }
      },
      "design": {
        "position": "top",
        "layout": "horizontal",
        "backgroundColor": "#ffffff",
        "textColor": "#000000",
        "ctaBackgroundColor": "#007bff",
        "ctaTextColor": "#ffffff",
        "fontFamily": "Arial, sans-serif",
        "fontSize": "16px",
        "padding": "16px",
        "borderRadius": "4px"
      },
      "config": {
        "dismissible": true,
        "autoHide": false,
        "autoHideDelay": 5000,
        "animation": "fade",
        "closeButton": true
      }
    }
  }
]
```

## Banner Features

<Accordion title="Targeting">
  Control which users see a banner based on their profile and behavior.

  * **Segments**: Restrict banners to specific customer segments defined in the dashboard.
  * **Custom Rules**: Write targeting rules against any customer attribute.
  * **Toggle**: Enable or disable targeting without deleting your rules.

  Banners with targeting enabled are only returned by `getBanners()` if the identified user matches the criteria.
</Accordion>

<Accordion title="Scheduling">
  Define exactly when a banner is active.

  * **Start / End Dates**: Set a date range for the campaign.
  * **Active Hours**: Limit display to specific hours of the day.
  * **Active Days**: Limit display to specific days of the week.
  * **Timezone**: All schedule calculations respect a configurable timezone.
</Accordion>

<Accordion title="Frequency Capping">
  Prevent banner fatigue by limiting how many times a user sees a banner.

  * **Max Displays**: The total number of times the banner can be shown to a single user.
  * **Period**: The cap resets on a `session`, `hour`, `day`, `week`, or `month` cadence.
  * **Toggle**: Disable frequency capping for always-on banners.
</Accordion>

<Accordion title="A/B Testing">
  Run experiments to find the most effective banner variant.

  * **Multiple Variants**: Create as many variants as you need per banner.
  * **Traffic Allocation**: Set the percentage of traffic each variant receives.
  * **Automatic Assignment**: Variant assignment is deterministic based on customer ID, so a user always sees the same variant.
  * **Analytics**: Views, clicks, dismissals, conversions, and CTR are tracked per variant automatically.
</Accordion>

## API Reference

| Method                                           | Parameters                                                                           | Returns             | Description                                                                                               |
| ------------------------------------------------ | ------------------------------------------------------------------------------------ | ------------------- | --------------------------------------------------------------------------------------------------------- |
| `getBanners(selector, options?)`                 | `selector: string`, `options.autoRender?: boolean`, `options.forceRefresh?: boolean` | `Promise<Banner[]>` | Fetch and optionally render banners for a placement. Pass `forceRefresh: true` to bypass the local cache. |
| `banners.display(bannerId, variantId, selector)` | `string, string, string`                                                             | `Promise<string>`   | Track a banner display; returns `displayId`                                                               |
| `banners.click(bannerId, displayId)`             | `string, string`                                                                     | `Promise<void>`     | Track a banner click                                                                                      |
| `banners.dismiss(bannerId, displayId)`           | `string, string`                                                                     | `Promise<void>`     | Track a banner dismissal                                                                                  |

<Note>
  Always call `identify()` before `getBanners()`. Without an identified user, the SDK cannot evaluate targeting rules or apply frequency caps.
</Note>
