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 ¶
- func AppendChild(parent, child *Node)
- func InnerHTML(n *Node) string
- func InsertBefore(parent, newChild, ref *Node)
- func OuterHTML(n *Node) string
- func RemoveChild(parent, child *Node)
- func SetInnerHTML(n *Node, htmlSrc string) error
- func SetTextContent(n *Node, text string)
- func TextContent(n *Node) string
- func Title(root *Node) string
- type Node
- type NodeType
- type ShadowRoot
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 ¶
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 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 ¶
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 ¶
SetTextContent replaces all of n's children with a single text node (or none, for empty text), matching the DOM textContent setter.
func TextContent ¶
TextContent returns the concatenation of every descendant text node's data, in document order (the DOM textContent getter).
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 NewComment ¶ added in v0.4.0
NewComment creates a detached comment node with the given data.
func NewElement ¶
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 Parse ¶
Parse turns an HTML document into an owned Node tree. The returned node is a Document whose single element child is <html>.
func ParseFragment ¶
ParseFragment parses an HTML fragment (as if it were the innerHTML of a <body>) into a list of detached owned nodes.
func (*Node) Attribute ¶
Attribute returns the value of the named attribute (lowercased name) and whether it was present.
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.