> Part of the walkerOS documentation. Project overview and full index: <https://www.walkeros.io/llms.txt>

<!-- -->

[Source code](https://github.com/elbwalker/walkerOS/tree/main/packages/web/sources/browser)[ ](https://www.npmjs.com/package/@walkeros/web-source-browser)

<!-- -->

[Package](https://www.npmjs.com/package/@walkeros/web-source-browser)

# Browser source

Captures user interactions directly from the DOM using `data-elb-*` attributes. Extracts tracking data from markup, tracks pageviews automatically or manually, exposes an enhanced `elb` function with `walker init` support, and provides a trigger system covering click, submit, load, hover, scroll, and visible.

<!-- -->

Where this fits

The browser source is a **source** in the walkerOS flow:

It reads \`data-elb-\*\` attributes from your HTML and generates events. The Collector routes these to your configured destinations.

## Installation[​](#installation "Direct link to Installation")

* Integrated
* Bundled

Install the packages:

```
npm install @walkeros/collector @walkeros/web-source-browser @walkeros/web-source-session
```

Configure in your code:

```
import { startFlow } from '@walkeros/collector';
import { sourceBrowser } from '@walkeros/web-source-browser';
import { sourceSession } from '@walkeros/web-source-session';

const { collector, elb } = await startFlow({
  sources: {
    browser: sourceBrowser,
    session: sourceSession, // Add for session tracking
  },
});
```

Add to your `flow.json` sources:

```
"sources": {
  "browser": {
    "package": "@walkeros/web-source-browser"
  },
  "session": {
    "package": "@walkeros/web-source-session",
    "config": {
      "settings": {
        "storage": true
      }
    }
  }
}
```

[See bundled mode setup](https://www.walkeros.io/docs/getting-started/modes/bundled.md) | [CLI reference](https://www.walkeros.io/docs/apps/cli.md)

## Configuration[​](#configuration "Direct link to Configuration")

This <!-- -->source<!-- --> uses the standard <!-- -->source<!-- --> config wrapper (consent, data, env, id, ...). For the shared fields see [source<!-- --> configuration](https://www.walkeros.io/docs/sources.md#configuration). Package-specific fields live under `config.settings` and are listed below.

## Settings[​](#settings "Direct link to Settings")

| Property   | Type                       | Description                                                                                                                                                              | More |
| ---------- | -------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ---- |
| `prefix`   | `string`                   | Prefix for data attributes (default: data-elb)                                                                                                                           |      |
| `scope`    | `string`                   | DOM scope for event tracking (default: document)                                                                                                                         |      |
| `pageview` | `boolean`                  | Enable automatic pageview tracking                                                                                                                                       |      |
| `capture`  | `boolean`                  | Read click/submit triggers in the capture phase so tagged elements are resolved at click time before app handlers run (set false for the previous bubble-phase behavior) |      |
| `elb`      | `string \| boolean`        | Name for global elb function, or false to install none                                                                                                                   |      |
| `name`     | `string`                   | Custom name for source instance                                                                                                                                          |      |
| `elbLayer` | `boolean \| string \| any` | Enable elbLayer for async command queuing (boolean, string, or Elb.Layer)                                                                                                |      |

## Mapping[​](#mapping "Direct link to Mapping")

This package does not define custom rule-level settings. For the standard rule fields (consent, condition, data, batch, name, policy) see [mapping](https://www.walkeros.io/docs/mapping.md).

## Examples

### Click event

A button click with walker data attributes is captured as an entity action event with the mapped label data.

Event

```
"<button data-elb=\"cta\" data-elb-cta=\"label:Sign Up\" data-elbaction=\"click:click\">Sign Up</button>"
```

Out

```
elb({
  "name": "cta click",
  "entity": "cta",
  "action": "click",
  "data": {
    "label": "Sign Up"
  },
  "context": {},
  "globals": {},
  "nested": [],
  "source": {
    "type": "browser",
    "platform": "web",
    "url": "https://example.com/",
    "referrer": ""
  },
  "trigger": "click"
})
```

### Context and globals

Walker context and globals attributes attach ambient metadata to events emitted by the browser source.

Event

```
"<div data-elbcontext=\"test:engagement_flow\" data-elbglobals=\"language:en;plan:premium\"><div data-elb=\"cta\" data-elb-cta=\"label:Try Now\" data-elbaction=\"click:signup\">Try Now</div></div>"
```

Out

```
elb({
  "name": "cta signup",
  "entity": "cta",
  "action": "signup",
  "data": {
    "label": "Try Now"
  },
  "context": {
    "test": [
      "engagement_flow",
      0
    ]
  },
  "globals": {
    "language": "en",
    "plan": "premium"
  },
  "nested": [],
  "source": {
    "type": "browser",
    "platform": "web",
    "url": "https://example.com/",
    "referrer": ""
  },
  "trigger": "click"
})
```

### Data attribute types

Walker data attributes parse scalar, boolean, and array values into typed fields on the emitted event.

Event

```
"<div data-elb=\"product\" data-elb-product=\"price:99.99;available:true;colors[]:red;colors[]:blue\" data-elbaction=\"click:select\"></div>"
```

Out

```
elb({
  "name": "product select",
  "entity": "product",
  "action": "select",
  "data": {
    "price": 99.99,
    "available": true,
    "colors": [
      "red",
      "blue"
    ]
  },
  "context": {},
  "globals": {},
  "nested": [],
  "source": {
    "type": "browser",
    "platform": "web",
    "url": "https://example.com/",
    "referrer": ""
  },
  "trigger": "click"
})
```

### Impression event

An intersection observer impression on a div with walker attributes emits a banner view event.

Event

```
"<div data-elb=\"banner\" data-elb-banner=\"type:promotional;position:sidebar\" data-elbaction=\"impression:view\"></div>"
```

Out

```
elb({
  "name": "banner view",
  "entity": "banner",
  "action": "view",
  "data": {
    "type": "promotional",
    "position": "sidebar"
  },
  "context": {},
  "globals": {},
  "nested": [],
  "source": {
    "type": "browser",
    "platform": "web",
    "url": "https://example.com/",
    "referrer": ""
  },
  "trigger": "impression"
})
```

### Nested entities

A page load produces a page view plus a product view whose nested size entity is captured on the product event.

Event

```
"<div data-elb=\"product\" data-elb-product=\"id:SKU-42;name:Sneakers\" data-elbaction=\"load:view\"><div data-elb=\"size\" data-elb-size=\"selected:large;inStock:true\"></div></div>"
```

Out

```
elb({
  "name": "page view",
  "data": {
    "domain": "example.com",
    "title": "",
    "referrer": "",
    "id": "/"
  },
  "context": {},
  "globals": {},
  "trigger": "load",
  "source": {
    "type": "browser",
    "platform": "web",
    "url": "https://example.com/",
    "referrer": ""
  }
});

elb({
  "name": "product view",
  "entity": "product",
  "action": "view",
  "data": {
    "id": "SKU-42",
    "name": "Sneakers"
  },
  "context": {},
  "globals": {},
  "nested": [
    {
      "entity": "size",
      "data": {
        "selected": "large",
        "inStock": true
      },
      "context": {},
      "nested": []
    }
  ],
  "source": {
    "type": "browser",
    "platform": "web",
    "url": "https://example.com/",
    "referrer": ""
  },
  "trigger": "load"
})
```

### Page view

A page load trigger captures the current URL, title, and referrer as a walker page view event.

Event

```
""
```

Out

```
elb({
  "name": "page view",
  "data": {
    "domain": "example.com",
    "title": "Documentation",
    "referrer": "https://example.com/",
    "id": "/docs"
  },
  "context": {},
  "globals": {},
  "trigger": "load",
  "source": {
    "type": "browser",
    "platform": "web",
    "url": "https://example.com/docs",
    "referrer": "https://example.com/"
  }
})
```

### Submit event

A form submit with walker data attributes produces a signup complete event carrying the selected plan.

Event

```
"<form data-elb=\"signup\" data-elb-signup=\"plan:premium\" data-elbaction=\"submit:complete\"></form>"
```

Out

```
elb({
  "name": "signup complete",
  "entity": "signup",
  "action": "complete",
  "data": {
    "plan": "premium"
  },
  "context": {},
  "globals": {},
  "nested": [],
  "source": {
    "type": "browser",
    "platform": "web",
    "url": "https://example.com/",
    "referrer": ""
  },
  "trigger": "submit"
})
```

## Enhanced elb[​](#enhanced-elb "Direct link to Enhanced elb")

The browser source returns an enhanced `elb` function with additional features:

```
// Collector elb: Basic event tracking
collector.push('product view', { id: 'P123' });

// Browser source elb: Enhanced with DOM commands
elb('walker init', document.querySelector('#app'));
elb('product view', { id: 'P123' }); // Also tracks events
```

**Browser source elb adds:**

* **DOM commands**: `walker init` for asynchronous DOM element loading
* **Flexible arguments**: support for multiple argument patterns
* **elbLayer integration**: automatic processing of queued commands
* **Element parameters**: support for element parameters in commands

See [Commands](https://www.walkeros.io/docs/sources/web/browser/commands.md) for complete API documentation.

## Multiple sources on one page[​](#multiple-sources-on-one-page "Direct link to Multiple sources on one page")

Any number of browser sources can run on one page. Each keeps its own triggers, its own element registry, and its own visibility state, so nothing one source wires, times, or tears down reaches another.

Two page-level resources are the exception, because each is a single slot:

| Resource                                | Setting    | Rule                                           |
| --------------------------------------- | ---------- | ---------------------------------------------- |
| The writer installed at `window[elb]`   | `elb`      | The first source to install the name keeps it. |
| The command queue at `window[elbLayer]` | `elbLayer` | The first source to adopt the array keeps it.  |

A source that asks for a name another source already holds is refused that one resource and logs an error. Everything else keeps working: its scope is still scanned, its triggers still fire, and its own `push` still resolves. A page's own bootstrap, the `elb` stub function and pre-filled `elbLayer` array defined before the source loads, belongs to no source and is adopted as usual.

The DOM is not owned. Any number of sources may scan the same node, `document` included. That is how two flows run on one page: give each its own `elb` name and its own `elbLayer` name (or `false`), and let them share the markup.

### A vendor flow beside the page's own[​](#a-vendor-flow-beside-the-pages-own "Direct link to A vendor flow beside the page's own")

Distinct names on both single-slot resources mean nothing is refused on either side. `pageview: false` leaves the `page view` event to the walker the page already runs, and the scope narrows the vendor's delegation to the subtree it cares about.

```
import { startFlow } from '@walkeros/collector';
import { sourceBrowser } from '@walkeros/web-source-browser';

const { collector, elb } = await startFlow({
  sources: {
    browser: {
      code: sourceBrowser,
      config: {
        settings: {
          elb: 'vendorElb',
          elbLayer: 'vendorLayer',
          scope: document.body,
          pageview: false,
        },
      },
    },
  },
});
```

### An embedded overlay with no window footprint[​](#an-embedded-overlay-with-no-window-footprint "Direct link to An embedded overlay with no window footprint")

`elb: false` installs no window property at all and `elbLayer: false` creates no queue, so the source adds nothing to the page window and reaches its collector through the `elb` that `startFlow` returns. Pair it with an explicit scope and the overlay captures only its own markup.

```
import { startFlow } from '@walkeros/collector';
import { sourceBrowser } from '@walkeros/web-source-browser';

const host = document.createElement('div');
document.body.appendChild(host);
const overlay = host.attachShadow({ mode: 'closed' });

const { collector, elb } = await startFlow({
  sources: {
    browser: {
      code: sourceBrowser,
      config: {
        settings: {
          elb: false,
          elbLayer: false,
          scope: overlay,
          pageview: false,
        },
      },
    },
  },
});
```

### One elbLayer belongs to one walker[​](#one-elblayer-belongs-to-one-walker "Direct link to One elbLayer belongs to one walker")

A queue is drained once. The source that adopts a named layer reads every entry, and a command like `walker run` has exactly one addressee, so a second source on the same array would starve the first rather than join it. Parallel flows use separate layer names or `elbLayer: false`, and share instrumentation through the `data-elb` attributes rather than through a queue.

### Overlapping scopes fire once per source[​](#overlapping-scopes-fire-once-per-source "Direct link to Overlapping scopes fire once per source")

Two sources scanning the same tagged element wire it twice. `click`, `hover`, `pulse`, `scroll`, `wait`, `impression`, and `visible` each fire once per source, into that source's own pipeline, and an observed element carries one dwell timer per source.

The `prefix` setting decides whether that is what you want:

* **Same prefix** is deliberate dual collect. One set of attributes feeds both pipelines, and each collector sees the interaction once.
* **Different prefix** isolates the two. Each source only sees elements tagged for it, so overlapping scopes cost nothing.

note

Give overlapping sources the same scope root. A source discovers [`data-elbobserve`](https://www.walkeros.io/docs/sources/web/browser/tagging/html-attributes.md#auto-init-with-data-elbobserve) containers inside its own scope only. When two sources share a prefix and the container sits outside one of their scopes, only the source that discovered it reaps elements the container removes. For the other one that element's teardown never runs: its `pulse` intervals keep firing, its `wait` timeouts stay armed, and its hover and visibility registrations stay in place against a node that has left the document, which also keeps that node referenced until the source is torn down. A shared scope root means both sources see the same containers.

### Known limits[​](#known-limits "Direct link to Known limits")

* **Version skew.** A page running an older release of the package leaves no ownership mark, so a source injected later reads that layer as free and adopts it. This only matters when both use the same layer name, which is what the contract above tells parallel flows not to do.
* **Integrations that assign `window[elb]` themselves.** The `walker.js` bundle assigns the name after the source has installed its writer, which leaves a value in the slot that belongs to no source, so a second source reads the name as free and installs over it.
* **The same script included twice.** Each copy scans the same markup into its own pipeline, so every event is collected twice. That is a page misconfiguration rather than something the source can resolve, and it is diagnosable: both refusals log at initialization.

## Next steps[​](#next-steps "Direct link to Next steps")

* [Session source](https://www.walkeros.io/docs/sources/web/session.md) - session and device tracking
* [Tagging guide](https://www.walkeros.io/docs/sources/web/browser/tagging/html-attributes.md) - learn about data attributes
* [Commands reference](https://www.walkeros.io/docs/sources/web/browser/commands.md) - full API documentation
* [Tagger tool](https://www.walkeros.io/docs/sources/web/browser/tagger.md) - visual tagging tool
