HTMLForge.jl — LLM Reference

This page is a condensed, single-file reference for HTMLForge.jl (v0.3.13) optimized for LLM code-generation systems. It covers the full public API. Julia ≥ 1.8.

Install

using Pkg; Pkg.add("HTMLForge")
using HTMLForge

Type Hierarchy

HTMLNode (abstract)
├── HTMLElement{T}   # T is a Symbol tag, e.g. HTMLElement{:div}
├── HTMLText         # text node
└── NullNode         # sentinel for missing parents

HTMLDocument

Returned by parsehtml. Fields:

  • doctype::AbstractString — doctype string (empty if none)
  • root::HTMLElement — root of the document tree

HTMLElement{T}

mutable struct HTMLElement{T} <: HTMLNode
    children::Vector{HTMLNode}
    parent::HTMLNode
    attributes::Dict{AbstractString, AbstractString}
end

Constructors:

HTMLElement(:div)                                          # empty
HTMLElement(:div, HTMLText("hello"))                        # single child
HTMLElement(:p, [HTMLText("text")], Dict("class"=>"intro")) # children + attrs
HTMLElement(:div, HTMLNode[]; data_id="42", class="box")   # keyword attrs (underscores → hyphens)

Indexing:

el["class"] = "wide"   # set attribute
el[:class]             # get attribute → "wide"
el[1]                  # first child

HTMLText

HTMLText("some text")  # parent defaults to NullNode()

Parsing

parsehtml

parsehtml(html::String;
    strict=false,              # throw InvalidHTMLException on errors
    preserve_whitespace=false, # keep whitespace text nodes
    preserve_template=false,   # preserve <template> elements
    include_parent=true        # set parent references
) → HTMLDocument

parsehtml_snippet

parsehtml_snippet(html::String;
    preserve_whitespace=false,
    preserve_template=false
) → HTMLElement

Returns an HTMLElement. Multiple top-level tags are wrapped in a <div>.


Manipulation

Element properties

tag(el)                    # → Symbol (:div, :p, etc.)
attrs(el)                  # → Dict of attributes
getattr(el, name)          # → value or nothing
getattr(el, name, default) # → value or default
setattr!(el, name, value)  # set attribute (validated)
hasattr(el, name)          # → Bool
children(el)               # → Vector{HTMLNode}
text(el)                   # recursively extract all text

Finding elements

findfirst(f, el)           # first descendant matching predicate f (pre-order DFS)
findfirst(f, doc)          # same, starting from doc
getbyid(el_or_doc, id)    # find element by id attribute
applyif!(cond, f!, el_or_doc) # apply mutating f! to all elements matching cond

CSS class helpers

hasclass(el, cls)          # → Bool
addclass!(el, cls)         # add class (no-op if present)
removeclass!(el, cls)      # remove class
replaceclass!(el, old, new) # replace; pass nothing to just remove

Validation

@validate :attr "data-value"          # validate attribute name
@validate :class "my-class"           # validate class name
@validate :attr ["a", "b"]           # validate multiple attribute names
@validate :class ["c1", "c2"]        # validate multiple class names

Invalid names raise InvalidAttributeException. Invalid class values raise ArgumentError or InvalidAttributeException.

Pretty printing

prettyprint(el)            # print to stdout
prettyprint(io, el)        # print to IO
prettyprint(doc)           # works on documents too

Tree Traversal

Re-exported from AbstractTrees.jl:

preorder(node)             # pre-order DFS
postorder(node)            # post-order DFS
breadthfirst(node)         # breadth-first (level-order)

Use with for elem in preorder(doc.root) ... end.


Equality

HTMLDocument, HTMLElement, and HTMLText support ==, isequal, hash. Elements are equal when they have the same tag, attributes, and children (parents are ignored).


HTMX Support

All HTMX helpers mutate in place and return the element (enabling chaining). Attributes are stored with the data-hx- prefix.

Generic setter

hxattr!(el, attr, value)   # data-hx-<attr>="value"

AJAX request helpers

hxget!(el, url)            # data-hx-get
hxpost!(el, url)           # data-hx-post
hxput!(el, url)            # data-hx-put
hxpatch!(el, url)          # data-hx-patch
hxdelete!(el, url)         # data-hx-delete
hxrequest!(el, method, url) # data-hx-<method>

Trigger

hxtrigger!(el, event;
    once=false, changed=false, delay=nothing, throttle=nothing,
    from=nothing, target=nothing, consume=false, queue=nothing,
    filter=nothing
)
# Example: hxtrigger!(el, "click"; once=true, delay="500ms")
# → data-hx-trigger="click once delay:500ms"
# Filter: hxtrigger!(el, "click"; filter="ctrlKey")
# → data-hx-trigger="click[ctrlKey]"

Target & swap

hxtarget!(el, selector)    # CSS selector, "this", "closest ...", etc.

hxswap!(el, style;
    transition=nothing, swap=nothing, settle=nothing,
    ignoreTitle=nothing, scroll=nothing, show=nothing,
    focusScroll=nothing
)
# Valid styles: "innerHTML", "outerHTML", "afterbegin", "beforebegin",
#               "beforeend", "afterend", "delete", "none"
# Example: hxswap!(el, "innerHTML"; transition=true, settle="100ms")

hxswapoob!(el, value)     # out-of-band swap
hxselect!(el, selector)   # select subset of response
hxselectoob!(el, sels)    # out-of-band select

Values & URLs

hxvals!(el, json)          # additional values (JSON string)
hxpushurl!(el, value)      # push URL ("true", "false", or URL)
hxreplaceurl!(el, value)   # replace URL without history entry

User interaction

hxconfirm!(el, message)    # confirm() dialog
hxprompt!(el, message)     # prompt() dialog
hxindicator!(el, selector) # element to show during request

Progressive enhancement & parameters

hxboost!(el, value)        # "true"/"false"
hxinclude!(el, selector)   # include other elements' values
hxparams!(el, value)       # "*", "none", specific list, "not ..."
hxheaders!(el, json)       # additional headers (JSON)
hxsync!(el, value)         # e.g. "closest form:abort"
hxencoding!(el, encoding)  # e.g. "multipart/form-data"
hxext!(el, extensions)     # comma-separated extension list

Event handling

hxon!(el, event, script)   # data-hx-on:<event>="script"

Disable & inheritance

hxdisable!(el)             # disable htmx on element+children
hxdisabledelt!(el, sel)    # disable elements during request
hxdisinherit!(el, attrs)   # disable inheritance ("*" or space-separated)
hxinherit!(el, attrs)      # enable inheritance

History & preservation

hxhistory!(el, value)      # "false" to prevent caching
hxhistoryelt!(el)          # mark as history snapshot source
hxpreserve!(el)            # keep unchanged between requests (needs stable id)

Request config & validation

hxrequestconfig!(el, val)  # e.g. "timeout:3000, credentials:true"
hxvalidate!(el)            # enable HTML5 validation before request

Chaining

el = HTMLElement(:button)
el |> x -> hxpost!(x, "/submit") |>
     x -> hxtarget!(x, "#result") |>
     x -> hxswap!(x, "outerHTML") |>
     x -> hxconfirm!(x, "Sure?")

Experimental HTMX Module

Three additional interfaces. Load with:

using HTMLForge
include(joinpath(pkgdir(HTMLForge), "src", "Experimental.jl"))

Interface 1: Classic functions (auto-generated)

Same as standard API above — all hx*! functions are generated via @eval from an internal registry.

Interface 2: @hx macro

Declarative multi-attribute assignment:

# Create + assign
btn = @hx :button get="/api" trigger="click" target="#result"

# Modify existing
el = HTMLElement(:form)
@hx el post="/submit" swap="outerHTML" confirm="Sure?"

# Underscores → hyphens
@hx el push_url="true" replace_url="/new"

# Expression interpolation
url = "/api/v2"
btn = @hx :button get=url

Note: @hx sets raw string values. Trigger modifiers must be in the string: trigger="click once delay:500ms".

Interface 3: hx.* pipe DSL

Most ergonomic — uses |> with curried closures:

el = HTMLElement(:button) |>
    hx.post("/api/submit") |>
    hx.trigger("click"; once=true) |>
    hx.target("#response") |>
    hx.swap("outerHTML"; transition=true, settle="200ms") |>
    hx.confirm("Proceed?") |>
    hx.indicator("#spinner")

Flag attributes: hx.disable(), hx.preserve(), hx.validate(), hx.historyelt().

Side-by-side comparison

# Classic
el = HTMLElement(:button)
hxpost!(el, "/submit")
hxtarget!(el, "#result")
hxswap!(el, "outerHTML")
hxtrigger!(el, "click"; once=true)
hxconfirm!(el, "Sure?")

# @hx macro
el = @hx :button post="/submit" target="#result" swap="outerHTML" trigger="click once" confirm="Sure?"

# Pipe DSL
el = HTMLElement(:button) |>
    hx.post("/submit") |>
    hx.target("#result") |>
    hx.swap("outerHTML") |>
    hx.trigger("click"; once=true) |>
    hx.confirm("Sure?")

Complete HTMX Attribute Table

Value attributes

FunctionAttributePipe
hxget!(el, v)data-hx-gethx.get(v)
hxpost!(el, v)data-hx-posthx.post(v)
hxput!(el, v)data-hx-puthx.put(v)
hxpatch!(el, v)data-hx-patchhx.patch(v)
hxdelete!(el, v)data-hx-deletehx.delete(v)
hxtarget!(el, v)data-hx-targethx.target(v)
hxselect!(el, v)data-hx-selecthx.select(v)
hxswapoob!(el, v)data-hx-swap-oobhx.swapoob(v)
hxselectoob!(el, v)data-hx-select-oobhx.selectoob(v)
hxvals!(el, v)data-hx-valshx.vals(v)
hxpushurl!(el, v)data-hx-push-urlhx.pushurl(v)
hxreplaceurl!(el, v)data-hx-replace-urlhx.replaceurl(v)
hxconfirm!(el, v)data-hx-confirmhx.confirm(v)
hxprompt!(el, v)data-hx-prompthx.prompt(v)
hxindicator!(el, v)data-hx-indicatorhx.indicator(v)
hxboost!(el, v)data-hx-boosthx.boost(v)
hxinclude!(el, v)data-hx-includehx.include(v)
hxparams!(el, v)data-hx-paramshx.params(v)
hxheaders!(el, v)data-hx-headershx.headers(v)
hxsync!(el, v)data-hx-synchx.sync(v)
hxencoding!(el, v)data-hx-encodinghx.encoding(v)
hxext!(el, v)data-hx-exthx.ext(v)
hxdisinherit!(el, v)data-hx-disinherithx.disinherit(v)
hxinherit!(el, v)data-hx-inherithx.inherit(v)
hxhistory!(el, v)data-hx-historyhx.history(v)
hxrequestconfig!(el, v)data-hx-requesthx.request(v)
hxdisabledelt!(el, v)data-hx-disabled-elthx.disabledelt(v)

Flag attributes (no argument)

FunctionAttributePipe
hxdisable!(el)data-hx-disablehx.disable()
hxpreserve!(el)data-hx-preservehx.preserve()
hxvalidate!(el)data-hx-validatehx.validate()
hxhistoryelt!(el)data-hx-history-elthx.historyelt()

Complex attributes (keyword arguments)

FunctionPipekwargs
hxtrigger!(el, event; ...)hx.trigger(event; ...)once, changed, delay, throttle, from, target, consume, queue, filter
hxswap!(el, style; ...)hx.swap(style; ...)transition, swap, settle, ignoreTitle, scroll, show, focusScroll
hxon!(el, event, script)hx.on(event, script)

Common Patterns

Parse → find → modify → print

doc = parsehtml(read("page.html", String))
el = getbyid(doc, "main-content")
addclass!(el, "active")
hxget!(el, "/api/refresh")
hxtarget!(el, "#content")
prettyprint(doc)

Build an element tree from scratch

div = HTMLElement(:div)
div["class"] = "container"

header = HTMLElement(:h1, HTMLText("Title"))
push!(div, header)

btn = HTMLElement(:button, HTMLText("Click me"))
hxpost!(btn, "/api/action")
hxtarget!(btn, "#result")
hxswap!(btn, "innerHTML")
push!(div, btn)

result = HTMLElement(:div)
result["id"] = "result"
push!(div, result)

prettyprint(div)

Traverse and transform

doc = parsehtml(html_string)
for el in preorder(doc.root)
    if isa(el, HTMLElement) && tag(el) == :a
        hxboost!(el, "true")
    end
end

Apply a change to all matching elements

applyif!(el -> tag(el) == :form, el -> hxboost!(el, "true"), doc)

Experimental pipe DSL full example

using HTMLForge
include(joinpath(pkgdir(HTMLForge), "src", "Experimental.jl"))

form = HTMLElement(:form) |>
    hx.post("/api/submit") |>
    hx.target("#result") |>
    hx.swap("outerHTML"; transition=true) |>
    hx.trigger("submit"; once=true) |>
    hx.confirm("Submit form?") |>
    hx.indicator("#spinner")