Skip to content
Domphy

ElementNode

Core node representing a single HTML element in the Domphy tree.

import { ElementNode } from "@domphy/core"

const node = new ElementNode({ div: "Hello World" })
node.render(document.body)

Constructor

new ElementNode(domphyElement: DomphyElement, parent?: ElementNode | null)

Properties

PropertyTypeDescription
typestringAlways "ElementNode"
parentElementNode | nullParent node. null if root
tagNameTagNameHTML tag name e.g. "div"
childrenElementListChild nodes
stylesStyleListScoped CSS styles
attributesAttributeListHTML attributes
domElementHTMLElement | nullMounted DOM element
keystring | number | nullIdentity key for diffing
nodeIdstringHash used for scoped CSS class generation
_portal((root) => Element) | undefinedRedirects DOM mount target when present

The scoped CSS class is attached through node.attributes using the pattern ${tagName}_${nodeId}.

Methods

render(domElement)

Creates a DOM node and appends it to the target.

node.render(document.body)
node.render(document.getElementById("app")!)

mount(domElement, domStyle?)

Hydrates onto an existing DOM element. Used for SSR.

const html = node.generateHTML()
const css = node.generateCSS()
// ... send to client ...
const domStyle = document.getElementById("domphy-style") as HTMLStyleElement
node.mount(document.getElementById("app")!, domStyle)

When doing SSR, render CSS into <style id="domphy-style">...</style> on the server, then pass that same style element to mount() on the client.

remove()

Removes this node from its parent.

node.remove()

patch(rawElement)

Replaces the node's element descriptor in-place. Triggers a reconciliation pass to apply the new props/children/style to the DOM without unmounting.

node.patch({ div: "updated content", class: "active" })
ParameterTypeDescription
rawElementDomphyElementNew element descriptor to apply

Unlike merge() which deep-merges, patch() replaces the full descriptor. Used internally by list reconciliation (a reactive list/content function reusing a node by key or position) and explicit update flows.

patch() reconciles the node's own flat style properties (e.g. color, padding) — properties present before but absent from the new descriptor are removed, matching how attributes are reconciled. Nested selector blocks are not reconciled (&:hover, @media, @keyframes, @font-face): they are set once at construction and assumed stable across reuse. A value that must change after construction under a nested selector needs its own reactive function (color: (l) => …) rather than a plain value recomputed by the caller.

merge(partial)

Updates this node from a partial element descriptor.

node.merge({ style: { color: "red" }, class: "active" })

addEvent(name, callback)

Registers a DOM event listener. Multiple callbacks are chained.

node.addEvent("click", (e, node) => console.log(node.tagName))

addHook(name, callback)

Registers a lifecycle hook. Multiple callbacks are chained.

node.addHook("Mount", (node) => console.log("mounted"))
node.addHook("BeforeRemove", (node, done) => {
  animate(node.domElement).then(done)
})
HookTrigger
Schedule(node, rawElement) => void — fired before parsing; use to apply context-aware patches via merge(rawElement, ...)
Init(node) => void — fired after parsing, before insertion into the tree
InsertNode added to children list
MountDOM element created
BeforeUpdateBefore children diff
UpdateAfter children diff
BeforeRemoveBefore DOM removal — call done() to proceed
RemoveAfter DOM removal
ErrorCaught error from a reactive child ((node, error, reset) => void) — call reset() to clear children and render fallback

getRoot()

Returns the root node of the tree.

const root = node.getRoot()

getContext(name) / setContext(name, value)

Inherited context — walks up the tree to find the nearest value.

// Parent
node.setContext("theme", "dark")

// Any descendant
const theme = node.getContext("theme") // "dark"

getMetadata(name) / setMetadata(key, value)

Local metadata — not inherited by children.

node.setMetadata("id", "user-123")
node.getMetadata("id") // "user-123"

getBehavior(key)

Looks up a per-node behavior instance attached via behavior(), walking up through ancestors (same pattern as getContext/getMetadata) — a behavior is declared on the element that owns the concern, but the event that needs it often fires on a descendant.

import { behavior } from "@domphy/core"

const anchorPartial = behavior("floating", attachFloating, { open: openState })

// later, from a live-rebound trigger event handler:
onClick: (e, node) => node.getBehavior("floating")?.show()

Returns undefined if the key was never declared on this node or an ancestor, or was declared but hasn't attached yet (construction-time, before Mount).

generateHTML()

Generates HTML string. Used for SSR.

const html = node.generateHTML()
// "<div class="div_abc123">Hello</div>"

generateCSS()

Generates CSS string for this node and all descendants. Used for SSR.

const css = node.generateCSS()