Triggers

Comprehensive guide to all trigger types, their options and configurations in Hood Web SDK.
developer

Trigger System

Hood Web SDK uses an advanced trigger system that enables precise control over when modals, tags, and other actions are executed.

Trigger Object Structure

Triggers are defined as objects with specific fields that determine when and how actions should be executed. Each trigger object contains:

Basic Trigger Fields

  • type (string, required): Defines which trigger mechanism to use (scroll, click, timer, etc.). This determines how the SDK will monitor for the triggering condition.
  • config (object, optional): Contains trigger-specific parameters that customize behavior. Required for timer (config.time) and visibility (config.s). If triggers is missing or [], the unit executes immediately.

Trigger Definition

{
  "triggers": [
    {
      "type": "scroll",
      "config": {
        "vertical": 75,
        "v_unit": "%"
      }
    },
    {
      "type": "pageView"
    }
  ]
}

Configuration Object Structure

Each trigger type has its own configuration object with specific fields:

{
  "type": "visibility",
  "config": {
    "s": ".my-element",
    "dur": 2000,
    "fire": 1,
    "dom": true
  }
}

How Matching Works

When an event occurs, the SDK checks all registered tags/modals:

  1. Event type/ID must match the trigger
  2. If matched, filters (if provided) are evaluated
  3. If filters pass, action executes (e.g., show modal, mutate DOM)

Configuration Example

{
  "triggers": [
    { "type": "pageView" },
    { "type": "scroll", "config": { "vertical": 50, "v_unit": "%" } }
  ],
  "filters": [{ "c": "{{data.user_segment}}", "o": "eq", "m": "prospect" }]
}

This example fires on first pageView or after user scrolls 50% vertically, then shows the action only for prospect users.

Detailed Trigger Type Descriptions

The following sections describe the commonly used trigger types. The SDK also captures a few lifecycle events that take no config (loading, interactive, complete, consentInitialization, visibilitychange, pagehide) — they are matchable by type but are not a public catalog.

Page Load Triggers

These triggers fire at different stages of page loading, from DOM parsing to full resource loading. Choose the appropriate trigger based on when you need your action to execute.

DOMContentLoaded

Fires when DOM structure is ready, before all resources finish loading. Enables fast DOM manipulation and quick user interactions without waiting for assets.

Configuration: None required

Usage:

{
  "type": "DOMContentLoaded"
}

When to use:

  • Quick actions that don’t wait for resources
  • DOM manipulation before full page load
  • Fast user interactions

load

Waits for all resources (images, CSS, JS) to fully load before firing. Ideal for complex modals that need complete page state or heavy content delivery.

Configuration: None required

Usage:

{
  "type": "load"
}

When to use:

  • Complex modals requiring all resources
  • Actions that should execute only when page is fully ready
  • Heavy content delivery

pageView

Fires only on SPA navigation changes (history API events). Does NOT fire on initial page load - use load or DOMContentLoaded for that. Perfect for tracking virtual page views in single-page applications.

Configuration: None required

Usage:

{
  "type": "pageView"
}

When to use:

  • SPA navigation tracking
  • Virtual page view analytics
  • Route change monitoring
  • Internal navigation events

Important: This trigger only fires when there are internal navigation changes in SPA applications. For initial page load tracking, use load or DOMContentLoaded triggers instead.

User Behavior Triggers

These triggers monitor consent changes and page unload. They help you respond after a consent decision changes, or when the document is about to go away.

Fires when consent changes — Hood('consent', …) or a TCF CMP routing into setConsent when a category key changes. TCF can also emit it when the TC string or GDPR flag changes, even if the mapped category booleans stay the same.

On init, stored or default consent emits consentInitialization instead (no config). Use that type for the load-time snapshot.

userConsent is not replayed for stored or default consent alone, but it can fire during initialization when autoconf applies a truthy consent key, or when a CMP/TCF settles during boot (including before the DOM helper is ready).

Configuration: None required

Usage:

{
  "type": "userConsent"
}

When to use:

  • Reacting to a later consent change
  • Updating UI after the user revises a CMP choice

beforeunload

The browser beforeunload event: tab/window close or full document navigation away. It is not exit intent (mouse leaving the viewport) and it does not fire on SPA back/forward (popstate) that leaves the document loaded. SPA route changes are pageView, not beforeunload.

Configuration: None required

Usage:

{
  "type": "beforeunload"
}

When to use:

  • Last activity before the document unloads
  • Retention or cart messages on real navigation away (not SPA routing)

Interaction Triggers

These triggers respond to user interactions with your page, including clicks, scrolling, form submissions, and element visibility. They enable engagement-based targeting and user behavior analytics.

click

Matches captured document clicks. Clicks on document, <html> (documentElement), or document.body are not captured. The captured event includes text (trimmed innerText) and target. Text-less targets (images, icons) are still captured so config.i can match.

Configuration: Optional

  • No config / no i → any captured click matches
  • config.i (string, CSS selector) → matches if event.target.closest(i) succeeds (the target or an ancestor)
  • Invalid selector → no match, no throw

Usage:

{
  "type": "click"
}
{
  "type": "click",
  "config": { "i": ".signup-cta" }
}

When to use:

  • User interaction analytics
  • Click-based personalization
  • General user behavior tracking

scroll

Monitors user scroll behavior and fires at specified depths. Enables engagement-based targeting, progressive content reveals, and scroll-based analytics.

Configuration Object:

{
  "type": "scroll",
  "config": {
    "vertical": 75,
    "horizontal": 50,
    "v_unit": "%",
    "h_unit": "px"
  }
}

Configuration Fields:

  • vertical (number): Threshold for vertical scroll. Percentage (0–100) unless v_unit is "px". If omitted, the vertical axis is not checked (always matches). Example: 75
  • horizontal (number): Threshold for horizontal scroll. Percentage (0–100) unless h_unit is "px". If omitted, the horizontal axis is not checked (always matches). Example: 50
  • v_unit (string, default: "%"): Unit for vertical scroll. Values: "%", "px". Omit the key → percentage. Only the literal "px" switches to pixels. Example: "%"
  • h_unit (string, default: "%"): Unit for horizontal scroll. Values: "%", "px". Omit the key → percentage. Only the literal "px" switches to pixels. Example: "%"

If both vertical and horizontal are set, both must be satisfied. { "type": "scroll", "config": { "vertical": 50 } } means 50% of scrollable height. A page that cannot scroll is treated as 100% scrolled, so a percentage threshold still matches.

The scroll manager deletes the config after it fires. trigger_fire: "each" will not make the same scroll trigger fire again.

When to use:

  • Engagement tracking
  • Displaying content when user shows interest
  • A/B testing based on scroll behavior

visibility

Tracks when specific elements enter the viewport. Perfect for element-based engagement tracking, lazy loading triggers, and viewport-based analytics.

Configuration Object:

{
  "type": "visibility",
  "config": {
    "s": ".my-element",
    "dur": 2000,
    "fire": 1,
    "dom": true
  }
}

Configuration Fields:

  • s (string, required): CSS selector targeting specific elements to monitor. Supports any valid CSS selector (classes, IDs, attributes). Example: ".my-element"
  • dur (number, default: 0): Duration in milliseconds the element must be visible before firing. Omitted or 0 fires immediately. Example: 2000
  • fire (number, default: 1): Observer cleanup only — it does not decide how many times the tag/modal action runs. That is trigger_fire on the unit (once / each). Values: 1, 2, 3. Example: 1
  • dom (boolean, default: false): Watch for later-added nodes with a MutationObserver. The observer walks addedNodes with node.querySelectorAll(config.s) — it matches descendants, not the added node itself. If the inserted element is the selector match, it is missed. Example: true

fire (observer cleanup, not unit once/each):

  • 1 or omitted: cleanupEventType only when exactly one element is in the config set. Multiple matching elements are not “once per page” at this layer.
  • 2: Removes that element from the set; the event is still emitted on later intersects.
  • 3: No cleanup.

captureEvent always runs. How many times the unit action runs is trigger_fire on the tag/modal.

If filters fail, the unit stays registered and filters are re-evaluated on later visibility ticks. The observer uses thresholds 0%, 1%, … 100% so percentage-based filters (for example via md.fn._dom) can succeed on a later tick.

When to use:

  • Element-based engagement tracking
  • Dynamic content visibility
  • SPA applications with changing DOM

formSubmit

Captures any form submission event on the page. Enables conversion tracking, thank you messages, and lead generation workflows.

Configuration: None required

Usage:

{
  "type": "formSubmit"
}

When to use:

  • Thank you messages
  • Lead generation
  • Conversion tracking

Time-based Triggers

These triggers execute actions based on time delays and custom events. They enable delayed interactions, progressive engagement, and custom event handling.

timer

Fires after a specified delay. Enables delayed popups, progressive engagement, and time-based user experience flows.

Configuration Object:

{
  "type": "timer",
  "config": {
    "time": 5000
  }
}

Configuration Fields:

  • time (number, required when config is present): Delay in milliseconds before firing. Example: 5000

{ "type": "timer" } with no config throws during setup and the timer is not registered. config.time omitted on a present config object uses 0.

When to use:

  • Delayed popups
  • Progressive engagement
  • Time-based user experience flows

Custom Triggers

These triggers enable custom event handling and third-party integrations. They allow you to create sophisticated user flows and respond to custom JavaScript events.

event

Listens for custom JavaScript events you emit. Enables complex user flows, third-party integrations, and custom interaction tracking.

Configuration Object:

{
  "type": "event",
  "config": {
    "name": "myCustomEvent",
    "target": "document"
  }
}

Configuration Fields:

  • name (string, required): Name of the custom event to listen for. Without it, no listener is attached. Example: "myCustomEvent"
  • target (string, optional): Where to attach the DOM listener.
    • omitted → listeners on both document and window
    • "document" or "window" → that node only
    • any other string → document.querySelector(target); if nothing matches, a console warning and no listener

Hood('trackevent', name, data) fires the same trigger via queue.fireNamedEvent(name, data) as { type: 'event', name, data }. That path matches config.name.

How to emit:

// A) SDK path — matches config.name
Hood('trackevent', 'myCustomEvent', { someData: 'value' });

// B) DOM path — dispatch on the same node as target, with bubbles: true
document.dispatchEvent(
  new CustomEvent('myCustomEvent', {
    bubbles: true,
    detail: { someData: 'value' },
  }),
);

When to use:

  • Custom user flows
  • Third-party integrations
  • Complex interaction tracking

Advanced Options

These advanced configuration options provide additional control over trigger behavior and execution logic.

If triggers is missing or [], the unit executes immediately (no wait). Filters still apply.

triggers_all

Requires all defined triggers to be met before the action fires. Defaults to false, meaning the first matching trigger is enough.

The flag sits next to triggers on the tag or modal itself, not inside the triggers array.

Configuration:

{
  "triggers": [
    { "type": "scroll", "config": { "vertical": 50 } },
    { "type": "timer", "config": { "time": 5000 } }
  ],
  "triggers_all": true
}

When to use:

  • Complex targeting logic
  • Combining multiple conditions
  • Advanced personalization

trigger_fire

Controls whether a unit may run more than once.

  • once (default): executes on the first matched trigger only, then stays consumed
  • each: re-executes every time a trigger activates

Configuration:

{
  "triggers": [{ "type": "pageView" }],
  "trigger_fire": "each"
}

On single-page applications, each is what makes a unit re-fire on every virtual page navigation.

group

group (string) sits on the tag or modal, next to trigger_fire. At most one unit in that group executes per (virtual) page.

  • Group state resets on SPA navigation (pushState / replaceState / popstate).
  • A trigger_fire: "once" unit that already executed is not revived by that reset. Members that should re-fire on every virtual page need trigger_fire: "each".
  • A skipped group sibling stays registered so it can run after the next reset.

Push prompts internally use group: "push".

Configuration:

{
  "triggers": [{ "type": "pageView" }],
  "group": "entry",
  "trigger_fire": "each"
}