dom

package
v0.8.5 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 18, 2026 License: BSD-3-Clause Imports: 4 Imported by: 1

Documentation

Overview

Package dom is a small, owned DOM node tree built from golang.org/x/net/html.

The engine deliberately owns its node type rather than passing x/net/html nodes around: it keeps the rest of the engine (css, layout, paint) independent of the parser and gives a stable, minimal surface — element tag, attributes, text — that is trivial to construct in tests.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func AppendChild

func AppendChild(parent, child *Node)

AppendChild appends child to parent's child list, first detaching child from any current parent. Nil operands and self-appends are no-ops.

A DocumentFragment child is unwrapped per spec (see isFragment's own doc comment) rather than inserted as itself — confirmed live on caniuse.com's own "Browser scores" widget: its real, populated content ended up nested inside a literal, unstyled `<#fragment>` element instead of becoming a direct child of its real container, breaking the CSS relationship (`> `-combinator rules, `display:flex`'s own "only direct children are flex items" rule) the widget's own styling depended on to lay out its "Current version"/"Dev version" toggle correctly.

func InnerHTML

func InnerHTML(n *Node) string

InnerHTML serializes n's children back to HTML (the DOM innerHTML getter) — n's Content fragment's children for a <template>, n's own otherwise (see htmlContainer). Attribute order is normalised (sorted) so the output is deterministic.

func InsertBefore

func InsertBefore(parent, newChild, ref *Node)

InsertBefore inserts newChild immediately before ref among parent's children, detaching newChild from any current parent first. If ref is nil or not a child of parent, newChild is appended.

A DocumentFragment newChild is unwrapped per spec, same as AppendChild's own identical case (see isFragment's own doc comment): each of its children is inserted in turn immediately before ref, preserving their relative order.

func OuterHTML

func OuterHTML(n *Node) string

OuterHTML serializes n itself (element and its subtree) back to HTML.

func RemoveChild

func RemoveChild(parent, child *Node)

RemoveChild removes child from parent's child list, clearing its Parent. It is a no-op if child is not a direct child of parent.

func SetInnerHTML

func SetInnerHTML(n *Node, htmlSrc string) error

SetInnerHTML parses htmlSrc as an HTML fragment and replaces n's children with the result (the DOM innerHTML setter) — n's Content fragment for a <template>, n itself otherwise (see htmlContainer). On a parse error the children are left unchanged and the error is returned.

func SetTextContent

func SetTextContent(n *Node, text string)

SetTextContent replaces all of n's children with a single text node (or none, for empty text), matching the DOM textContent setter.

func TextContent

func TextContent(n *Node) string

TextContent returns the concatenation of every descendant text node's data, in document order (the DOM textContent getter).

func Title

func Title(root *Node) string

Title returns the document's <title> text, trimmed, or "".

Types

type Node

type Node struct {
	Type     NodeType
	Tag      string            // lowercased tag name (Element only)
	Attr     map[string]string // lowercased attribute names (Element only)
	Text     string            // character data (Text and Comment)
	Parent   *Node
	Children []*Node

	// Quirks is set on the Document (root) node when the source had no
	// `<!DOCTYPE ...>` at all — the single most common real-world trigger for
	// "quirks mode" (a page that never opted into standards mode). It is the
	// scoped case this engine detects; the HTML spec's fuller algorithm also
	// triggers quirks mode for specific legacy PUBLIC/SYSTEM doctype
	// identifiers (e.g. HTML 4.01 Transitional without a system ID), which
	// are vanishingly rare on real pages that reach this engine and are not
	// modelled — a page with ANY doctype is treated as standards mode here.
	// Confirmed load-bearing live: news.ycombinator.com ships no doctype at
	// all (`<html lang="en" op="news">` directly), and real browsers'
	// quirks-mode UA stylesheet resets `table{text-align:initial}` — without
	// which its `<center><table>...</table></center>` page shell (used only
	// to centre the table AS A BLOCK on the page) inherits centered text
	// into every cell, visibly wrong for cells the real page never intended
	// to centre (see css/ua.go's quirks-mode table rule).
	Quirks bool

	// Indeterminate is an <input type=checkbox>'s own "indeterminate" IDL
	// state (HTML Standard §4.10.5.1.19): unlike Checked, it has NO backing
	// content attribute at all — script is the only way to set it, so it
	// lives here as a plain runtime field, the same shape as Quirks/Shadow
	// above, rather than in Attr. Confirmed real usage: github.com's own
	// behaviors.js sets `checkbox.indeterminate = true` on page load for any
	// `[data-indeterminate]`-marked checkbox (its "select all" bulk-action
	// tri-state pattern). Backs the JS `.indeterminate` accessor and the
	// `:indeterminate` CSS pseudo-class (css/selector.go).
	Indeterminate bool

	// CustomValidity holds a form control's own "custom error message" set by
	// script via `.setCustomValidity(msg)` (HTML Standard's Constraint
	// Validation API) — empty means no custom error. This engine models NO
	// native constraints at all (no required/pattern/min/max/step checking
	// against a value), so this is the ONLY thing that can ever make
	// `.validity.valid`/`.checkValidity()` false — a deliberate, disclosed
	// scope boundary: this backs the real, evidenced usage (see js/dom.go's
	// own doc comment), not a full constraint-validation engine. Runtime-only
	// field, the same shape as Indeterminate above — there is no
	// "customvalidity" content attribute.
	CustomValidity string

	// Shadow is the shadow root attached to this element (a declarative
	// <template shadowrootmode> hoisted out at parse time — see
	// attachDeclarativeShadowRoots), or nil for a plain element. When set,
	// cascade and layout render Shadow.Children in place of this element's
	// own Children — the light-DOM Children above are NOT rendered directly;
	// they are visible only where a <slot> inside the shadow tree projects
	// them (see layout.renderedChildren / assignedSlotNodes).
	Shadow *ShadowRoot
	// ShadowHost is set on every node belonging to a shadow tree (recursively,
	// via markShadowHost) to that tree's host element; nil for an ordinary
	// light-DOM node. A <slot> element uses its own ShadowHost to find the
	// host whose light-DOM children it may project.
	ShadowHost *Node

	// Content is set on a <template> element (and only a <template> element)
	// to an inert "#fragment" node holding what would otherwise be its light-
	// DOM children — the HTMLTemplateElement.content DocumentFragment a real
	// browser exposes, per spec never rendered and never a template's own
	// Children. NewElement and convertChildren both set this up so every
	// <template>, however created, has one; SetInnerHTML/InnerHTML/serialize
	// all read and write through it instead of Children for this tag.
	// Real, confirmed live usage: lit-html's own template-cloning path reads
	// `templateEl.content` (see js.newTreeWalker's own doc comment, which
	// already documents `this.el.content` as the exact real expression
	// caniuse.com's lit-html-based web components read) — with no Content
	// field at all, `.content` was undefined, confirmed via a minimal
	// isolated repro to throw "Cannot read property 'cloneNode' of
	// undefined" the moment real cloning code touches it.
	Content *Node
	// contains filtered or unexported fields
}

Node is a single DOM node. Elements carry a lowercased Tag and an attribute map; text and comment nodes carry Text. Children are in document order.

func Find

func Find(n *Node, tag string) *Node

Find returns the first element in document order with the given tag, or nil.

func NewComment added in v0.4.0

func NewComment(data string) *Node

NewComment creates a detached comment node with the given data.

func NewElement

func NewElement(tag string) *Node

NewElement creates a detached element node with the given (lowercased) tag and an empty attribute map. It is used by the JS binding's document.createElement.

A "template" tag gets its own Content fragment set up immediately (see Node.Content's doc comment) — the other place a <template> can originate, convertChildren, does the same for one parsed from real HTML source.

func NewText

func NewText(text string) *Node

NewText creates a detached text node with the given character data.

func Parse

func Parse(htmlSrc string) (*Node, error)

Parse turns an HTML document into an owned Node tree. The returned node is a Document whose single element child is <html>.

func ParseFragment

func ParseFragment(htmlSrc string) ([]*Node, error)

ParseFragment parses an HTML fragment (as if it were the innerHTML of a <body>) into a list of detached owned nodes.

func (*Node) Attribute

func (n *Node) Attribute(name string) (string, bool)

Attribute returns the value of the named attribute (lowercased name) and whether it was present.

func (*Node) Classes

func (n *Node) Classes() []string

Classes returns the whitespace-split class list of an element.

func (*Node) ID

func (n *Node) ID() string

ID returns the element's id attribute (empty if absent).

type NodeType

type NodeType uint8

NodeType enumerates the kinds of node the engine cares about.

const (
	// Document is the synthetic root of a parsed tree.
	Document NodeType = iota
	// Element is a tag node (Tag/Attr populated).
	Element
	// Text is a run of character data (Text populated).
	Text
	// Comment is an HTML comment (`<!--…-->`), its data held in Text like a
	// Text node's. Previously dropped entirely during parsing (see
	// convertChildren's own doc comment) — a real gap, since React's
	// streaming-SSR hydration protocol marks Suspense boundaries with literal
	// `<!--$-->`/`<!--/$-->` comment nodes it expects to find in the DOM;
	// their absence made hydration see a structurally different tree than the
	// server sent, confirmed live as react.dev's own "Minified React error
	// #418" (hydration mismatch) cascading into a full client-side crash.
	Comment
)

type ShadowRoot added in v0.4.0

type ShadowRoot struct {
	// Host is the element this shadow root is attached to.
	Host *Node
	// Children are the shadow tree's top-level nodes (what a real
	// DocumentFragment's children would be) — the host's declarative
	// <template>'s .content, hoisted out of the light DOM. Their Parent
	// pointers are nil (they are the root of a separate tree); descendants
	// further down keep the ordinary Parent chain within the shadow tree.
	Children []*Node
}

ShadowRoot is a shadow tree attached to a host element: a separate node list, distinct from the host's own (light-DOM) Children, that cascade and layout render in the host's place. It models declarative Shadow DOM only (a <template shadowrootmode="open"|"closed"> consumed at parse time — see attachDeclarativeShadowRoots); there is no imperative attachShadow()/ customElements.define() path. "open" and "closed" are treated identically for rendering (the distinction only affects JS .shadowRoot introspection, out of scope here), so the mode itself is not recorded.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL