docs

Ranges and Selection

Work with ranges of page content and manage the active selection from C++.

On this page

You can inspect, measure, and modify spans of page content directly from native code, using similar syntax to JavaScript.

Native code can also read the user's active selection (a caret or highlighted text), change what is selected, and react to selection changes.

Ranges

A dom::Range spans a slice of the page, matching JavaScript's Range. Call createRange() to create one, point it at your content, and read its text:

C++
auto range = document.createRange();
range.selectNodeContents(document.getElementById("quote"));
std::string text = range.toString();

Measuring Text on Screen

Call getBoundingClientRect() to get a single box around everything the range covers, or getClientRects() for a std::vector of boxes covering each line of wrapped text. Both return viewport CSS pixels (updating layout first if the page has pending changes):

C++
for (dom::DOMRect line : range.getClientRects())
  DrawUnderline(line);

Setting the Boundaries

Use selectNode() to span an entire node, or selectNodeContents() to span its children. To set exact endpoints, call setStart() and setEnd() with an offset (which counts UTF-16 units inside text nodes and child nodes elsewhere):

C++
dom::Node text = document.getElementById("quote").firstChild();
range.setStart(text, 0);
range.setEnd(text, 5);

Changing the Content

You can remove, extract, or duplicate the DOM nodes covered by a range:

Call What It Does
deleteContents() Removes the covered content from the page
extractContents() Moves the covered content into a new dom::DocumentFragment (a node only partly covered stays, minus the covered part)
cloneContents() Copies the covered content into a new fragment and leaves the page alone

Call extractContents() to pull the covered nodes into a reusable fragment:

C++
dom::DocumentFragment removed = range.extractContents();

Keeping a Range

A range is live— its ends move automatically as the page changes to keep covering the same content. Use cloneRange() to make an independent copy:

C++
dom::Range saved = range.cloneRange();

👍 Let Ranges Go When You're Done

Each live range adds a little work to every change to its page, so destroy it once you're done.

The Selection

Calling document.getSelection() returns the page's active selection, representing either the caret or highlighted text. Call toString() to read the selected text, and check isCollapsed() to see whether anything is highlighted:

C++
dom::Selection selection = document.getSelection();
if (!selection.isCollapsed())
  ShowQuoteButton(selection.toString());

Reacting to Changes

Each change to the selection fires a selectionchange event on the document. The library dispatches these events during a later Renderer::Update() (one event per change):

C++
document.addEventListener("selectionchange", [document] {
  UpdateQuoteButton(document.getSelection().toString());
});

Selecting a Range

Call removeAllRanges() followed by addRange() to select a range (on its own, addRange() only grows an overlapping selection). You can also call modify() to move or extend the selection by words or lines, like using the arrow keys:

C++
selection.removeAllRanges();
selection.addRange(range);

📘 Selections in Text Fields

The selection sees a text field from outside— its anchor and focus sit just outside the <input> or <textarea>, and it reads as collapsed. Use the field's selectionStart and selectionEnd instead (see Forms and Inputs).

Differences from JavaScript

While the C++ API mirrors browser DOM behavior closely, a few calls return snapshots instead of live references or handle cross-document nodes differently:

JavaScript C++
getRangeAt() returns the selection's live range (changing it changes the selection) Returns a copy
Selection calls with a node from another document throw Ignored
getClientRects() returns a list Returns a std::vector snapshot