Vertex has one rendering system — Vertex.template (Mustache). There's no
virtual DOM and no reconciler to reconcile against, so the only real interop question left
is how a template's full re-render interacts with DOM you (or a third-party widget) manage
by hand. Three patterns — which ones work and why.
Plain HTML controls sit outside the template's mount element. A
V$(...).on(event, selector, fn) delegated handler reads the control and calls
tmpl.set() or tmpl.update() — it never reaches into the
template's own rendered markup. Because the controls' DOM and the template's mount element
are disjoint, the template is free to blow away and rebuild its own
innerHTML on every change without touching anything the surrounding page owns.
Data flows controls → template via .set()/.update().
Data flows template → controls via the template's on('change', …)
event (fired whenever a data-bind input changes). Fully bidirectional, zero shared DOM.
The imperative controls and the template live in completely separate DOM regions.
A plain JS object acts as the shared store. When either side mutates it, the other is
notified via a tiny event emitter — no framework coupling at all. The controls call
store.set(patch) directly via VQuery event handlers; the template's
store.subscribe(...) callback calls tmpl.update(store).
Template._render() always does this._el.innerHTML = html — a full
replace, every .set()/.update() call. If a template renders a
<div id="sub"> and something else — a hand-built widget, a third-party
script, or even a second new Vertex.template({ el: '#sub', … }) —
attaches DOM inside that div, the next re-render of the parent destroys it
all. Any setInterval/addEventListener the child set up keeps
running against a now-detached node — nothing ever tears it down. That's a silent leak, not
just a visual glitch.
The template protects its own data-bind inputs from this — a focused text input
keeps its live DOM node and cursor position across a re-render, and a range input skips
re-rendering entirely while it's being dragged (see the source) — but that carve-out is scoped
to its own
bound inputs. It has no idea foreign DOM is sitting inside the element it's about to overwrite.
Foreign widgets mounted so far: 0