bind

package
v0.3.3 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: May 5, 2026 License: MIT Imports: 3 Imported by: 0

Documentation

Overview

Package bind provides element annotation helpers for tether. Each helper attaches a data-tether-* attribute to a Fluent element, telling the client JS runtime how to handle that element - which events to forward, what client-side behaviour to apply, or which reactive signals to bind.

All bindings are applied via Apply with composable Option values:

bind.Apply(button.Text("Delete"),
    bind.OnClick("delete"),
    bind.Confirm("Are you sure?"),
    bind.Disable("Deleting..."),
)

This top-to-bottom style scales cleanly as behaviours are stacked and provides a single, consistent way to annotate elements.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func Apply

func Apply[E Settable[E]](el E, opts ...Option) E

Apply attaches all options to an element in order. Stack multiple behaviours top-to-bottom for readable composition:

bind.Apply(btn, bind.OnClick("delete"), bind.Confirm("Sure?"), bind.Disable("Deleting..."))

Types

type Option

type Option struct {
	// contains filtered or unexported fields
}

Option describes a single data attribute to apply to an element. Use with Apply for a top-to-bottom composition style when stacking multiple behaviours on one element:

bind.Apply(button.Text("Delete"),
    bind.OnClick("delete"),
    bind.Confirm("Are you sure?"),
    bind.Disable("Deleting..."),
)

func AutoFocus

func AutoFocus() Option

AutoFocus moves keyboard focus to this element after each server update morphs the DOM. Use this on the primary input in a form so the cursor returns there after each interaction.

func AutoScroll added in v0.2.3

func AutoScroll() Option

AutoScroll marks a scrollable container that should automatically scroll to the bottom after each morph. Use this on log viewers, streaming output, and chat feeds where new content appears at the bottom and the user should follow along. Unlike PreserveScroll which maintains the current position, AutoScroll always moves to the latest content.

func BindAttr

func BindAttr(attr, signal string) Option

BindAttr sets an HTML attribute to the signal's value. When the signal is falsy the attribute is removed entirely. Use this for dynamic attributes like "disabled", "aria-expanded", or "href".

func BindClass

func BindClass(class, signal string) Option

BindClass adds the CSS class when the named signal is truthy and removes it when falsy. Use this for conditional styling that reacts to server-pushed state without a full render cycle.

func BindHide

func BindHide(signal string) Option

BindHide is the inverse of BindShow: the element is hidden when the signal is truthy and visible when falsy.

func BindShow

func BindShow(signal string) Option

BindShow makes the element visible when the named signal is truthy and hidden when falsy. Visibility is toggled via CSS display - the element remains in the DOM either way.

func BindText

func BindText(signal string) Option

BindText replaces the element's text content with the signal's current value whenever the server pushes an update via [tether.Session.Signal]. The signal name must match the key passed to Signal().

func BindValue

func BindValue(signal string) Option

BindValue binds a form element's value property (input, select, textarea) to a named signal. When the server pushes a new signal value, the form element's displayed value updates instantly.

func Cloak

func Cloak() Option

Cloak hides the element until the tether runtime initialises. The client removes the attribute on startup, making the element visible. Use this to prevent a flash of unbound content - e.g. a signal-bound element that would briefly show its raw template text before the signal value is applied.

func Collect

func Collect(selector string) Option

Collect adds a CSS selector that the client resolves at event time. Matched elements contribute their current value to Event.Data, keyed by the element's name or id attribute. Use this to send input values with a click or keydown event without wrapping in a form:

bind.Apply(button.New().Text("Send"),
    bind.OnClick("chat.send"),
    bind.Collect("#message-input"),
)

func CollectSelected added in v0.2.0

func CollectSelected(selector string) Option

CollectSelected gathers the IDs of all selected items (those with the "tether-selected" class) within the container matched by selector. The IDs are included in the event data as a comma-separated "selected" key. Pair with OnClick on an action button:

bind.Apply(button.Text("Delete Selected"),
    bind.OnClick("items.delete"),
    bind.CollectSelected("#item-list"),
)

// In Handle:
ids := strings.Split(ev.Data["selected"], ",")

func Confirm

func Confirm(message string) Option

Confirm shows a browser confirmation dialog before the event is sent. If the user cancels, the event is silently dropped.

func CopyToClipboard added in v0.2.0

func CopyToClipboard(selector string) Option

CopyToClipboard copies the text content of the element matched by selector to the clipboard on click. No server round-trip - the copy happens entirely in the browser. Use this for "copy" buttons next to API keys, code snippets, or share URLs.

func Countdown added in v0.3.3

func Countdown(d time.Duration) Option

Countdown configures a timer to count down from the given duration instead of counting up from zero. When the timer reaches zero it stops automatically. Combine with TimerOnComplete to fire a server event when the countdown finishes.

func Data

func Data(key, value string) Option

Data sets a custom data-tether-* attribute on the element. This is the escape hatch for attributes not covered by the built-in options.

func Debounce

func Debounce(d time.Duration) Option

Debounce overrides the default input debounce delay for this element. The default (300ms, configurable via [tether.App].Client.DefaultDebounce) groups rapid keystrokes into a single event. Set a shorter duration for search-as-you-type, or a longer one for expensive operations. Panics if d is negative.

func Disable

func Disable(text string) Option

Disable replaces the element's text and disables it while the server processes the event. The original text and state are restored when the server responds. Prevents double-clicks and gives users visual feedback that something is happening.

func Draggable added in v0.2.0

func Draggable() Option

Draggable marks an element as draggable. Pair with EventData to attach identifying data (e.g. an item ID) that travels with the drag:

bind.Apply(card,
    bind.Draggable(),
    bind.EventData("id", card.ID),
)

func DropTarget added in v0.2.0

func DropTarget(action string) Option

DropTarget marks an element as a valid drop zone. When a draggable element is dropped onto this target, the action fires with event data merged from both the dragged element and the target. Pair with EventData to identify the target:

bind.Apply(column,
    bind.DropTarget("card.move"),
    bind.EventData("column", "1"),
)

func Editable added in v0.2.0

func Editable(action string) Option

Editable marks a contenteditable element and forwards its text content to the server when the element loses focus (blur). The action receives the edited text in ev.Value().

bind.Apply(span.Text("Click to edit").Attr("contenteditable", "true"),
    bind.Editable("item.rename"),
)

func Event

func Event(eventType, action string) Option

Event forwards an arbitrary DOM event to the server. Use this for event types not covered by the built-in options (OnClick, OnSubmit, etc.). The eventType is the standard DOM event name.

bind.Apply(el, bind.Event("dblclick", "open-editor"))

func EventData

func EventData(key, value string) Option

EventData attaches a static key-value pair to every event from this element. The pair appears in [tether.Event].Data alongside any values the client collects automatically (input value, form fields). Use this to carry context - like an item ID - with each click so the handler knows which item was acted on without maintaining server-side selection state.

func FilterKey

func FilterKey(key string) Option

FilterKey restricts an OnKeyDown binding to fire only for a specific key (e.g. "Enter", "Escape"). Other keys are silently ignored by the client and never reach the server.

func FlashClass added in v0.2.1

func FlashClass(class string) Option

FlashClass temporarily adds a CSS class to the element after a client-side action succeeds. The class is removed after the configured flash duration. Use this for richer feedback like colour changes, icon swaps, or animations:

bind.Apply(btn,
    bind.CopyToClipboard("#key"),
    bind.FlashClass("copied"),
)

func FlashText added in v0.2.1

func FlashText(text string) Option

FlashText temporarily replaces the element's text content after a client-side action succeeds (e.g. clipboard copy). The original text is restored after the configured flash duration (Client.FlashDuration, default 2s). No server round-trip. Pair with CopyToClipboard or other client-side actions:

bind.Apply(btn,
    bind.CopyToClipboard("#key"),
    bind.FlashText("Copied!"),
)

func FocusTrap

func FocusTrap() Option

FocusTrap constrains keyboard focus (Tab/Shift+Tab) to elements within this container. Use this for modals and drawers to prevent focus from escaping to elements behind the overlay - required for accessibility.

func Hook

func Hook(name string) Option

Hook attaches a named JS lifecycle hook to the element. Hooks are defined in application JS (e.g. hooks.js) and receive callbacks for mounted, updated, and destroyed events. Use this to integrate third-party JS libraries (charts, maps, editors) that need to initialise when the element appears, update when its data attributes change, and clean up when it is removed from the DOM.

func Hotkey added in v0.2.0

func Hotkey(combo, action string) Option

Hotkey registers a global keyboard shortcut. When the key combo is pressed anywhere on the page, the action fires as a normal tether event. The combo format is modifier keys joined with + followed by the key name: "ctrl+k", "escape", "shift+?", "ctrl+shift+p".

One hotkey per element. For multiple hotkeys, apply each to its own element:

bind.Apply(div.New(), bind.Hotkey("ctrl+k", "search.open"))
bind.Apply(div.New(), bind.Hotkey("escape", "modal.close"))

The client runtime builds a registry from [data-tether-hotkey] elements on init and after each morph. Lookups are O(1) per keypress with no CSS selector queries.

func Indicator

func Indicator(selector string) Option

Indicator shows a loading spinner or skeleton at the given CSS selector while the server processes the event. The indicator is removed when the server responds. Use this for actions where the user needs visual feedback in a different part of the page from the triggering element.

func Link() Option

Link enables client-side navigation for anchor elements. Instead of a full page reload, the client intercepts the click, updates the browser URL via pushState, and sends a navigate event to the server. The server re-renders the active page and pushes a diff - only the changed content is updated, preserving scroll position and input state. Use this for navigation within a single tether handler. For links to a different handler (e.g. from /ws/ to /sse/), use a regular <a> tag so the browser performs a full page load.

func MaxLength added in v0.2.0

func MaxLength(n int, message string) Option

MaxLength prevents form submission if the field value exceeds n characters. The message is shown as a validation tooltip. Panics if n is negative.

func MinLength added in v0.2.0

func MinLength(n int, message string) Option

MinLength prevents form submission if the field value is shorter than n characters. The message is shown as a validation tooltip. Panics if n is negative.

func OnBlur

func OnBlur(action string) Option

OnBlur forwards blur events to the server. Fires when the element loses keyboard focus. Useful for validating input on exit.

func OnChange

func OnChange(action string) Option

OnChange forwards change events to the server. Unlike OnInput, this fires once when the user commits a value (leaving a text field, selecting a dropdown option, toggling a checkbox). The element's value is included in [tether.Event].Data["value"].

func OnClick

func OnClick(action string) Option

OnClick forwards click events to the server. The action identifies which click this is (e.g. "delete", "save") so Handle can switch on it.

func OnFocus

func OnFocus(action string) Option

OnFocus forwards focus events to the server. Fires when the element receives keyboard focus (click, tab, or programmatic focus).

func OnInput

func OnInput(action string) Option

OnInput forwards input events to the server with debouncing. The element's current value is included automatically in [tether.Event].Data["value"]. Debounce delay defaults to 300ms (configurable via [tether.App].Client.DefaultDebounce or per-element via Debounce).

func OnKeyDown

func OnKeyDown(action string) Option

OnKeyDown forwards keydown events to the server. The pressed key name (e.g. "Enter", "Escape", "ArrowUp") is available via [tether.Event].Key(). Combine with FilterKey to restrict which keys trigger the server event.

func OnLongPress added in v0.2.0

func OnLongPress(action string) Option

OnLongPress fires after a sustained touch (~500ms) on the element. Cancelled if the finger moves before the timeout. Common mobile alternative to right-click. Only fires on touch devices.

func OnPaste added in v0.2.0

func OnPaste(action string) Option

OnPaste forwards paste events to the server. The pasted text is included in [tether.Event].Data["value"]. Use this for paste-to-search, paste-to-import, or paste-from-clipboard features.

func OnSubmit

func OnSubmit(action string) Option

OnSubmit forwards form submissions to the server. All named fields inside the form are automatically collected into [tether.Event].Data so the handler can read them by name (ev.Data["email"], ev.Int("age")).

func OnSwipe added in v0.2.0

func OnSwipe(action string) Option

OnSwipe fires when the user swipes on the element. The swipe direction is included in ev.Data["direction"] as "left", "right", "up", or "down". Only fires on touch devices.

func OnViewport

func OnViewport(action string) Option

OnViewport fires when the element enters the visible viewport, using an IntersectionObserver internally. Place this on a sentinel element at the bottom of a list to implement infinite scroll - when the sentinel scrolls into view, the server loads the next page of data.

func Optimistic

func Optimistic(signal, value string) Option

Optimistic sets a signal immediately on click AND sends the event to the server. The signal provides instant visual feedback while the server processes the event. If the server sends a different signal value in its response, the client updates to match - the server is always authoritative.

func OptimisticToggle

func OptimisticToggle(signal string) Option

OptimisticToggle flips a boolean signal immediately on click AND sends the event to the server. Like Optimistic but for boolean toggles - the signal flips instantly while the server processes the real state change.

func Pattern added in v0.2.0

func Pattern(regex, message string) Option

Pattern prevents form submission if the field value does not match the regular expression. The message is shown as a validation tooltip.

func Permanent

func Permanent() Option

Permanent excludes the element from DOM morphing. When idiomorph processes a server update, it skips elements marked permanent - their content, attributes, and children are preserved exactly as-is. Use this for elements with client-side state that must survive server updates (e.g. a video player, an interactive map, or a third-party widget that manages its own DOM).

func Prefix

func Prefix(name string) Option

Prefix sets the event namespace for a component container. When the client JS sends an event from inside a prefixed container, it automatically prepends the prefix to the action. This allows components to use bare action names (e.g. "send") while the framework routes them via the full prefixed name (e.g. "shoutbox.send").

Apply Prefix to the element that wraps a mounted component's Render output:

bind.Apply(div.New(s.Chat.Render()), bind.Prefix("chat")).Dynamic("chat-section")

func PreserveScroll added in v0.2.0

func PreserveScroll() Option

PreserveScroll marks a scrollable container whose scroll position should survive DOM morphing. Without this, idiomorph may reset the scroll position when the container's content is updated. Use this on columns, chat feeds, or any scrollable region.

func PreventDefault added in v0.2.0

func PreventDefault() Option

PreventDefault suppresses the browser's default behaviour for the event. Use this with Event to handle events like contextmenu without the browser's native menu appearing:

bind.Apply(el, bind.Event("contextmenu", "menu.open"), bind.PreventDefault())

func PushSubscribe

func PushSubscribe() Option

PushSubscribe marks a button for Web Push subscription. On click, the browser requests notification permission from the user and, if granted, subscribes via the service worker's PushManager. The subscription is sent to [tether.PushConfig].OnSubscribe so it can be stored for later use with [tether.Session.Push].

func Required added in v0.2.0

func Required(message string) Option

Required prevents form submission if the field is empty. The message is shown as a browser validation tooltip.

func Reset

func Reset() Option

Reset clears all form fields after a successful submit. Useful for chat-style inputs where the form should be ready for the next message immediately after sending.

func ScrollTo added in v0.2.0

func ScrollTo(selector string) Option

ScrollTo scrolls the matched element into view on click without a server round-trip. Uses smooth scrolling behaviour.

func Selectable added in v0.2.0

func Selectable() Option

Selectable marks a container for multi-select. Children with EventData "id" attributes become selectable items. Click selects one (deselects others), Ctrl/Cmd+click toggles, Shift+click selects a range. Selected items receive the "tether-selected" CSS class. Selection is purely client-side - no server round-trip per click. Use CollectSelected on an action button to gather the selected IDs when needed.

func SetSignal

func SetSignal(signal, value string) Option

SetSignal sets a signal to a specific value on click. No server round-trip - the signal updates instantly on the client. Use this for tab selection, radio-style patterns, or any case where clicking an element should set a known value.

func Sortable added in v0.2.0

func Sortable(action string) Option

Sortable marks a container for within-container reordering. When a draggable element is dropped inside a sortable container, the action fires with the drop index in event data ("index"). This enables priority reordering within a list or column. Sortable containers are also valid drop targets - items can be dragged in from outside.

bind.Apply(todoColumn,
    bind.Sortable("card.reorder"),
    bind.EventData("column", "0"),
)

func Throttle

func Throttle(d time.Duration) Option

Throttle sets a minimum interval between events from this element. Unlike Debounce which waits for a pause, Throttle fires the first event immediately and drops subsequent events until the interval elapses. Use this for scroll or resize handlers where you want regular updates without flooding the server. Panics if d is negative.

func Timer added in v0.3.3

func Timer(name string) Option

Timer attaches a client-side timer to the element. The timer ticks entirely in the browser - no server goroutine, no WebSocket messages per tick. The server controls the timer by pushing signals:

  • sess.Signal("name.running", true) starts the timer
  • sess.Signal("name.running", false) pauses it
  • sess.Signal("name", 0) resets the value

The element's text content is automatically bound to the formatted timer value - no separate BindText call is needed.

By default the timer counts up from zero at one-second precision with an auto-detected display format (ss, mm:ss, or hh:mm:ss). Combine with Countdown, TimerPrecision, TimerFormat, and TimerOnComplete to configure behaviour:

bind.Apply(el,
    bind.Timer("quiz"),
    bind.Countdown(30*time.Second),
    bind.TimerOnComplete("quiz.expired"),
)

func TimerFormat added in v0.3.3

func TimerFormat(pattern string) Option

TimerFormat sets an explicit display format for the timer value. The default is "auto", which picks the shortest readable representation based on the current value (ss under a minute, mm:ss under an hour, hh:mm:ss beyond that).

Supported format tokens:

  • hh:mm:ss hours, minutes, seconds
  • mm:ss minutes, seconds
  • ss seconds only
  • mm:ss.S minutes, seconds, tenths
  • mm:ss.SS minutes, seconds, hundredths

func TimerOnComplete added in v0.3.3

func TimerOnComplete(action string) Option

TimerOnComplete sets the event action fired back to the server when a countdown timer reaches zero. The event is sent as a standard tether event and appears in Handle with the given action name. Has no effect on count-up timers.

func TimerPrecision added in v0.3.3

func TimerPrecision(d time.Duration) Option

TimerPrecision sets the tick interval for the timer. The default is one second. Use shorter intervals for stopwatch-style displays:

bind.TimerPrecision(100 * time.Millisecond) // tenths of a second

func ToggleAttr

func ToggleAttr(attr string) Option

ToggleAttr toggles a boolean HTML attribute (e.g. "hidden", "disabled", "open") on click without a server round-trip.

func ToggleClass

func ToggleClass(class string) Option

ToggleClass adds or removes a CSS class on click without a server round-trip. Useful for toggling visibility, active states, or themes entirely on the client. Combine with ToggleTarget to toggle a class on a different element.

func ToggleSignal

func ToggleSignal(signal string) Option

ToggleSignal flips a boolean signal between true and false on click. No server round-trip - the signal updates instantly on the client. Combine with BindShow or BindClass for UI that toggles without network latency (dropdowns, accordions, dark mode).

func ToggleTarget

func ToggleTarget(selector string) Option

ToggleTarget directs a ToggleClass or ToggleAttr at a different element identified by a CSS selector, rather than the clicked element.

func Transition

func Transition(name string) Option

Transition enables CSS enter/leave transitions on the element. The name maps to CSS class prefixes: {name}-enter-from, {name}-enter-to, {name}-leave-from, {name}-leave-to. When the element is added to the DOM, the enter classes are applied; when removed, the leave classes animate the element out before it is actually deleted.

func Upload

func Upload(action string) Option

Upload marks the element as a file upload trigger. Clicking it opens the browser's file picker. When files are selected, they are uploaded as a multipart POST to the server and delivered to [tether.UploadConfig].Handle. The action string identifies which upload this is (e.g. "avatar", "document") and appears in [tether.Upload].Action.

func UploadInput

func UploadInput(selector string) Option

UploadInput provides a CSS selector for the file <input> element when it is not adjacent to the upload trigger in the DOM. By default the client looks for the nearest file input; use this when the input is elsewhere (e.g. hidden, in a different container).

func UploadProgress

func UploadProgress(action string) Option

UploadProgress binds a <progress> element's value attribute to the upload progress for the named action. The client updates the value (0–100) as bytes are sent, giving users a visual progress bar.

type Settable

type Settable[E any] interface {
	SetData(string, string) E
}

Settable is the structural type constraint for element annotation helpers. Any Fluent element with a chainable SetData method satisfies it.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL