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:
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):
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):
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:
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:
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:
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):
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:
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'sselectionStartandselectionEndinstead (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 |