docs

About the DOM API

Manipulate the DOM and handle events directly from native code.

On this page

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

Same Syntax as JavaScript

Updating an element in JavaScript looks like this:

JavaScript
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:

C++
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:

C++
#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

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.
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.
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, DOM Triggers and Navigation, and Binding Threads and Lifetime.

Where to Go Next

For C applications, see DOM Access in C.