HTML bindings

An HTML binding is a data-bind* attribute on a view element that pagelove.mjs uses to populate that element from a schema instance — and, when the element is editable, to write user edits back into the schema.

When to reach for it

Reach for a binding whenever a view element needs to show a value from a schema article, or whenever a user edit on the view needs to flow back into the record. Bindings are how the rendered light-DOM view of an instance stays in sync with the underlying <article> without any imperative wiring.

The three binding attributes

Attribute Direction What it does
data-bind="prop" two-way (when the element is editable) The element's text content (or value, on form controls) reflects the named property. User edits are written back to the schema article.
data-bind-attr="prop1 prop2" read-only Forwards each named property to a data-* attribute on the view root itself. color becomes data-color, x becomes data-x. Useful for CSS hooks.
data-bind-<htmlattr>="prop" read-only Binds one specific HTML attribute to a property value. data-bind-href="url" sets href, data-bind-src="photo" sets src, data-bind-datetime="publishedAt" sets datetime.

Property lookup is always scoped: pagelove.mjs resolves each prop against the direct [itemprop="<prop>"] children of the current schema scope, not against descendants.

Two-way updates

A data-bind element participates in writeback when user input reaches it through one of the delegated DOM events handled by pagelove.mjs:

Event Target selector Purpose
change any [data-bind] Commit on form-control change (checkbox, select, native date picker).
focusout [contenteditable][data-bind], input[data-bind], select[data-bind], textarea[data-bind] Commit when the element loses focus after editing.
input the same editable selectors Live commits, debounced so each keystroke does not hit the schema.

[contenteditable] elements participate automatically because they fire focusout and input the same way <input> and <textarea> do. No extra markup is required to make a contenteditable heading writeable — the data-bind attribute is enough.

Form controls (input, select, textarea) read and write through the element's value. Every other element reads and writes through textContent. Writes are idempotent: the binder compares before writing, so an unchanged value produces no DOM mutation.

Repetition

A single data-bind element can stamp multiple view elements when the matching schema property repeats. Two conditions trigger repetition:

#repeatElement() performs the stamp. The original element is removed from the DOM and replaced with a comment marker of the form <!--data-bind:<prop>-->. For each matching schema child, a clone of the stamp is inserted after the marker. If the schema child has no itemscope, the clone receives the scalar value written into its text content. If the schema child is itself an itemscope, the clone is linked to it via data-for="<schemaId>" and #populateBindings() recurses into the clone against the nested schema scope.

Nested scopes

A data-bind that sits inside a deeper [data-for] element is owned by that inner scope, not by the outer pass. During the outer viewRoot.querySelectorAll('[data-bind]') iteration, any element whose closest [data-for] ancestor is not the current viewRoot is skipped. The same rule applies to the data-bind-<htmlattr> sweep. This is what allows nested components to manage their own bindings without the parent rewriting them on every pass.

Examples

Two-way text binding on a heading

<template itemtype="https://schema.host/Note">
  <article class="note">
    <h1 data-bind="title" contenteditable></h1>
    <p data-bind="body" contenteditable></p>
  </article>
</template>

The heading and paragraph reflect the instance's title and body. Editing either in place commits back to the schema article on focusout and during input (debounced).

Forwarding properties as data-* attributes

<template itemtype="https://schema.host/StickyNote">
  <article class="sticky" data-bind-attr="color x y">
    <h1 data-bind="title" contenteditable></h1>
  </article>
</template>

The root <article> receives data-color, data-x, and data-y reflecting the matching properties on the instance. CSS can then read them:

.sticky { background: attr(data-color); transform: translate(attr(data-x px), attr(data-y px)); }

Binding an HTML attribute

<template itemtype="https://schema.host/Link">
  <article>
    <a data-bind-href="url" data-bind="label"></a>
    <time data-bind-datetime="publishedAt" data-bind="publishedAt"></time>
  </article>
</template>

The <a> gets its href from url and its visible text from label. The <time> gets its datetime attribute from publishedAt and its visible text from the same property.

See also