Help us shape the future of Go! Take the 2026 Go Developer Survey

dom

package
v0.9.1 Latest Latest
Warning

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

Go to latest
Published: Oct 19, 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