Every item in your page is a `dom::Node`, including the text and comments between tags. Element queries skip those items— `dom::Node` reaches every child in the tree.

The examples use this HTML markup with the View's `dom::Document` named `document`:

```html
<p id="greeting">Howdy <b>world</b>!</p>
```

## Nodes and Elements

Every element is a `dom::Node`, but a node can also represent plain text or a comment. To inspect a node or convert it to an element, use its member methods:

```cpp
auto greeting = document.getElementById("greeting");

dom::Node first = greeting.firstChild();
Log(first.nodeName());  // "#text" (the "Howdy " text)

dom::Element bold = first.nextSibling().AsElement();
Log(bold.tagName());    // "B"
```

`nodeType()` returns the kind of node (such as `dom::NodeType::Element`, `dom::NodeType::Text`, or `dom::NodeType::Comment`). `nodeName()` returns `#text` for text nodes, `#comment` for comments, and the uppercase tag name for elements.

`AsElement()` narrows a node to a `dom::Element`— it returns an empty handle for text and comments.

> 👍 Whitespace Creates Text Nodes
>
> Whitespace between HTML tags becomes a text node. Calling `firstChild()` on an indented element usually returns spaces and a newline— use `firstElementChild()` to skip straight to the first child element.

## Walking the Tree

### Traversal Methods

You can traverse the tree using calls that return every node kind or element calls that skip text and comments.

| Relationship | Node Calls (Every Kind) | Element Calls (Elements Only) |
| :--- | :--- | :--- |
| First child | `firstChild()` | `firstElementChild()` |
| Last child | `lastChild()` | `lastElementChild()` |
| Siblings | `previousSibling()`, `nextSibling()` | `previousElementSibling()`, `nextElementSibling()` |
| Parent | `parentNode()` | `parentElement()` |
| All children | `childNodes()` | `children()`, `childElementCount()` |

To check whether a node has children of any kind, call `hasChildNodes()`.

### Iterating Child Nodes

To step through every child node in order, iterate over `childNodes()`:

```cpp
for (dom::Node child : greeting.childNodes()) {
  if (child.nodeType() == dom::NodeType::Text) {
    std::string text = child.nodeValue;  // "Howdy ", then "!"
    Log(text);
  }
}
```

Both `childNodes()` and `children()` return snapshots rather than live collections— call them again to inspect changes made after the query.

## Text and Comment Nodes

### Creating Text and Comments

You can create standalone text and comment nodes on the document, then insert them using standard tree insertion methods:

```cpp
auto label = document.getElementById("score");
dom::Node score = document.createTextNode("0");
label.appendChild(score);
label.before(document.createComment("updated by the HUD"));
```

Strings passed to methods like `append()`, `prepend()`, `before()`, `after()`, or `replaceWith()` become text nodes automatically (the string is never parsed as markup). For insertion calls, see [Finding and Modifying Elements](/docs/2.0/finding-and-modifying-elements).

### Changing Text in Place

To update a text node in place, assign to `nodeValue`:

```cpp
score.nodeValue = std::to_string(points);
```

Holding a handle to a text node lets you update its value without modifying other children (assigning to `textContent` on the parent element replaces all children).

On elements, `nodeValue` reads as an empty string and ignores writes.

## Checking If a Node Is in the Page

To check if a node is currently connected, call `isConnected()`:

```cpp
auto toast = document.createElement("div");
toast.isConnected();  // false

document.body().appendChild(toast);
toast.isConnected();  // true

toast.remove();
toast.isConnected();  // false
```

A newly created node isn't connected until you insert it into the page.

Calling `remove()` disconnects a node from its parent, but its C++ handle remains valid so you can insert it again later.

## Documents and Frames

### The Document

The `dom::Document` provides direct access to top-level elements like `documentElement()` (the root `<html>`), `body()`, `head()`, and `activeElement()` (the focused element).

It also supports tree traversal calls like `firstElementChild()`, `children()`, `hasChildNodes()`, and `childNodes()` (which includes the doctype).

To navigate from the root element back to the document, call `ownerDocument()`:

```cpp
dom::Element root = document.documentElement();

root.parentNode();     // empty
root.ownerDocument();  // the document
```

> 📘 The Document Is Not a Node
>
> Unlike the web DOM, `dom::Document` is its own type and doesn't inherit from `dom::Node`. Calling `parentNode()` on the root `<html>` element returns an empty handle— use `ownerDocument()` to reach the document from any node.

### Frame Documents

Each `<iframe>` or `<frame>` element contains its own document.

To access an iframe's document, convert the element with `AsIFrame()` and call `contentDocument()`:

```cpp
auto frame = document.querySelector("iframe#shop").AsIFrame();
dom::Document shop = frame.contentDocument();
auto cart = shop.getElementById("cart");
```

The top-level page's document comes from `dom::Document(view)`.

> 🚧 Frame Handles Expire on Navigation
>
> A frame document's handles stop working when the frame navigates or is removed from the page.

## Differences from JavaScript

While the C++ DOM API mirrors JavaScript, a few types and behaviors differ.

| JavaScript | C++ |
| :--- | :--- |
| `node.childNodes` and `el.children` are live collections | `childNodes()` and `children()` return snapshots (call again after changes) |
| Read-only properties (`node.parentNode`, `node.isConnected`) | Method calls (`parentNode()`, `isConnected()`) |
| The document is a `Node` (`nodeType` 9, where `html.parentNode === document`) | `dom::Document` is its own type (`parentNode()` on `<html>` is empty) |
| `Text` and `Comment` objects have `data` | Single `dom::Node` type with `nodeValue` |
| Missing nodes return `null` | Returns an empty handle |
