The DOM API allows you to manipulate DOM elements and handle events directly in C++, offering higher performance and lower memory-usage than doing so via JavaScript.

Its syntax closely matches JavaScript for quick porting of existing code and general ease-of-use.

> 🚧 Preview API
>
> The DOM API ships as a preview in 2.0. Names and behavior may still change after 2.0.

> 📘 Works Without JavaScript
>
> The DOM API works even with JavaScript disabled (`ViewConfig::enable_javascript = false`). That makes it well suited for games and embedded devices (see [Embedded and Device UI](/docs/2.0/embedded-and-device-ui)).

## Same Syntax as JavaScript

Updating an element in JavaScript looks like this:

```js
const status = document.querySelector("#status");
status.textContent = "Connected";
status.classList.add("online");
status.style.backgroundColor = "purple";
```

The same four lines in C++ look nearly identical:

```cpp
auto status = document.querySelector("#status");
status.textContent = "Connected";
status.classList.add("online");
status.style.backgroundColor = "purple";
```

## Quick Start

Consider an HTML page with a status line and a button:

```html
<p id="status">Offline</p>
<button id="save">Save</button>
```

You can wire up actions to be performed on the DOM via `dom::Triggers` callbacks:

```cpp
#include <Ultralight/DOM.h>

// Define our DOM triggers:
dom::Triggers page;

page.OnDOMReady([](dom::Document document) {
  auto status = document.getElementById("status");
  status.textContent = "Connected";
  status.classList.add("online");
});

page.On("#save", "click", [](dom::Element button) {
  button.textContent = "Saved";
  button.disabled = true;
});

// Attach to a View (persists across navigations)
if (page.AttachTo(view.get()))
  view->LoadURL("file:///app.html");
```

## Rules to Know

- **Call the API on the Renderer's thread.** Copying, moving, and destroying a handle is safe from any thread.
- **A handle never keeps its page alive.** Once the page navigates away, the handle's page is gone and anything you do with it fails safely (no null checks needed).
- **The API never throws C++ exceptions.** A call that fails returns an empty value (pass `dom::Checked` to get a `dom::Result` holding the reason).

## Choosing Between the APIs

Native code can interact with a page through data bindings, the DOM API, or the JavaScript API. Each fits a different task.

| Task | What to Use |
| :--- | :--- |
| Show state on the page and keep it updated | Data bindings (`ul-*` attributes and `dd::Context::Sync()`). See [About Data Bindings](/docs/2.0/about-data-bindings). |
| Make one-off DOM changes, like focusing an element, measuring layout, or creating nodes | The DOM API |
| Respond to clicks in native code with no page script | A `ul-on` action in data bindings, or a `dom::Triggers` listener when native code needs the element or the event |
| Call native code from page script and return a result | A `js::API` function. See [About JavaScript Interop](/docs/2.0/about-javascript-interop). |
| Send an event to page script | `js::API::Emit()` |
| Work with the DOM from another thread | Call `Renderer::PostTask()` to run a task on the Renderer's thread, then use the DOM API inside that task |

Both data bindings and the DOM API work with JavaScript disabled (`ViewConfig::enable_javascript = false`)— the JavaScript API requires it enabled.

### Using Multiple APIs on One View

You can attach `js::API`, `dom::Triggers`, and `dd::Context` to the same View using `AttachTo()`— each stays attached until you call `DetachFrom()` or destroy the owning object.

| Behavior | js::API | dom::Triggers | dd::Context |
| :--- | :--- | :--- | :--- |
| Thread to attach from | Renderer's thread | Renderer's thread | Any thread |
| When current page gets it | Right away | Right away (DOM-ready hooks run inside `AttachTo()`) | During a later `Renderer::Update()` |
| Subframes | Supported with `js::AllFrames` | Supported with `dom::AllFrames` | Never (main frame only) |
| After `DetachFrom()` | Events stop right away, and the page keeps bindings until it navigates | Listeners stop right away | Page drops bindings during a later `Renderer::Update()` |

> 🚧 Match Origin Rules Across APIs
>
> All three APIs accept origin rules and default to local content (`file://` pages and `View::LoadHTML()`). If you use custom origin rules, pass the same rules to every API attached to the View so pages do not receive one set of bindings while missing another.

For details on attaching and configuring each system, see [Extending JavaScript with Native API](/docs/2.0/extending-javascript-with-native-api), [DOM Triggers and Navigation](/docs/2.0/page-wiring-and-navigation), and [Binding Threads and Lifetime](/docs/2.0/binding-threads-and-lifetime).

## Where to Go Next

- [DOM Handles and Errors](/docs/2.0/dom-handles-and-errors) — Check handle validity, handle errors, and enable runtime diagnostics.
- [Finding and Modifying Elements](/docs/2.0/finding-and-modifying-elements) — Query elements with CSS selectors, update content and attributes, and build nodes.
- [Walking the Node Tree](/docs/2.0/walking-the-node-tree) — Traverse node hierarchies, inspect text and comment nodes, and navigate frames.
- [Reading and Writing Styles](/docs/2.0/reading-and-writing-styles) — Read computed CSS styles and set inline style properties.
- [Element Geometry and Scrolling](/docs/2.0/element-geometry-and-scrolling) — Measure element bounds, control scrolling, inspect the viewport, and hit-test coordinates.
- [Forms and Inputs](/docs/2.0/forms-and-inputs) — Read and set form control values, work with select elements, and handle form submission.
- [Handling DOM Events](/docs/2.0/handling-dom-events) — Register event listeners, read event objects, and dispatch custom events.
- [DOM Triggers and Navigation](/docs/2.0/page-wiring-and-navigation) — Keep event listeners active across navigations and manage back-forward cache restores.
- [Ranges and Selection](/docs/2.0/ranges-and-selection) — Create DOM ranges, inspect user selections, and measure text dimensions.

For C applications, see [DOM Access in C](/docs/2.0/c-dom-access).
