Kite

dom

! The document. Web only. ! ! Ordinary Kite over std/js — there is not one extern ! declaration in this file, and that is the point. Everything here could have ! been written by a user, which is what makes the primitives underneath a real ! answer to "what if the standard library never wrapped the thing I need" ! rather than a promise. ! ! A Kite program lives inside a page and owns parts of it. The page is ! served by whatever serves it, styled by whatever stylesheet it already has, ! and Kite takes the parts that need real logic. Attaching to <body> is ! available and is not the point. ! ! ``kite ignore ! use std/dom ! ! fn main() { ! let button = dom.find("#save") ! if button == nil { ! return ! } ! let (sub, err) = dom.on(button, "click", |e: dom.Event| { save() }) ! if err != nil { ! io.print("could not attach: \(err.message())") ! } ! } ! `` ! ! This module finds elements, makes them, changes them, and listens to them. ! The browser lays out, CSS styles, and a design system is a stylesheet.

Element

struct Element

One element.

Opaque: outside this module an Element can be held and passed but not read or built, because its field is unmarked ([spec §4.2]). That is the whole of how the untyped layer underneath is stopped from spreading into application code — and it is ordinary visibility, not a special rule.

Event

struct Event

Something that happened.

Subscription

struct Subscription

A listener that has been attached, and can be taken off again.

Returned rather than discarded because nothing else can cancel it. The closure is alive as long as the listener is, the listener is alive as long as the element is, and an element a program keeps a handle on is alive as long as the program. Explicit teardown is squarely inside this language's tolerance for verbosity.

raw

pub fn raw(e: Element) -> JsValue

The host object inside an element.

The escape hatch, and it is meant to be used as one. Sealing Element completely sounds safer and is not: the first person who needs a method this module never wrapped would be unable to reach their own element, and what they would do instead is rebuild a parallel untyped world beside this one. One marked door is a boundary that holds; a wall is a boundary that gets climbed.

It is greppable on purpose. dom.raw( in a diff is a thing to look at.

wrap

pub fn wrap(v: JsValue) -> Element

The other direction, for a host object a program obtained itself.

raw_event

pub fn raw_event(e: Event) -> JsValue

The host object inside an event, for the same reason.

find

pub fn find(selector: str) -> Option<Element>

The first element matching a CSS selector, or nothing.

An Option<Element>, so a selector that matched nothing is a value the caller has to open before using. Testing it narrows the binding to an Element for the rest of the branch.

find_in

pub fn find_in(parent: Element, selector: str) -> Option<Element>

The first element inside parent matching a selector.

find_all

pub fn find_all(selector: str) -> [Element]

Every element matching a selector, in document order.

The empty slice when nothing matches or the selector is malformed. A malformed selector throws in the browser; here it is the same answer as no matches, because a caller looping over the result cannot act on the difference. Use [find] when the distinction matters.

create

pub fn create(tag: str) -> (Element, error)

A new element, not yet in the document.

text

pub fn text(e: Element) -> str

The element's text content.

set_text

pub fn set_text(e: Element, body: str) -> error

Replace the element's text content.

Text, never markup: whatever is passed becomes the element's text, and a string that looks like a tag stays a string. Building markup out of strings is the commonest way to grow an injection bug, so the typed layer sets text and nothing else. [raw] is where a program goes when it means otherwise.

attribute

pub fn attribute(e: Element, name: str) -> Option<str>

One attribute, or nothing when it is absent.

set_attribute

pub fn set_attribute(e: Element, name: str, value: str) -> error

remove_attribute

pub fn remove_attribute(e: Element, name: str) -> error

value

pub fn value(e: Element) -> str

The value of an input, a textarea or a select.

A property, not an attribute, and the difference is not pedantry. getAttribute("value") answers with the default the markup gave, which stops being the truth the moment somebody types. Reading the attribute is how a form ends up submitting what it was born with. The same holds for checked, selected and indeterminate, which is why each of those has a call here rather than being left to [attribute].

set_value

pub fn set_value(e: Element, body: str) -> error

checked

pub fn checked(e: Element) -> bool

set_checked

pub fn set_checked(e: Element, on: bool) -> error

set_style

pub fn set_style(e: Element, property: str, value: str) -> error

One CSS property, by its CSS name — background-color, not backgroundColor.

For values a stylesheet cannot know: a computed position, a progress width, a custom property the CSS then reads. Everything static belongs in the stylesheet, where the cascade can reach it.

add_class

pub fn add_class(e: Element, name: str) -> error

remove_class

pub fn remove_class(e: Element, name: str) -> error

has_class

pub fn has_class(e: Element, name: str) -> bool

set_class

pub fn set_class(e: Element, name: str, on: bool) -> error

Add the class when on, remove it otherwise.

The shape state usually wants. A program that keeps its state in the model and its appearance in CSS spends most of its time here — and :hover, :focus-visible and :checked it does not touch at all, because those never reach Kite.

append

pub fn append(parent: Element, child: Element) -> error

insert_before

pub fn insert_before(parent: Element, child: Element, before: Element) -> error

remove

pub fn remove(e: Element) -> error

Take an element out of the document.

same

pub fn same(a: Element, b: Element) -> bool

Whether two handles are the same element.

The question every listener asks about event.target, and the reason a host object is a reference rather than a number: the old handle table pushed a new entry on every lookup, so finding one element twice gave two handles that compared unequal. == on an Element is a compile error that points here.

on

pub fn on(target: Element, name: str, handler: fn(Event)) -> (Subscription, error)

Listen, and get back something that can stop listening.

The handler is given an [Event], never a raw host value — which is what keeps std/js out of an application's vocabulary even though everything here is built on it.

target

pub fn target(e: Event) -> Option<Element>

What the event happened to.

Optional because a target is not always an element: it may be the document, the window, or a text node. A call that assumed otherwise would be wrong on exactly the events that are hardest to debug.

event_value

pub fn event_value(e: Event) -> str

The value of whatever the event happened to, for an input event.

key

pub fn key(e: Event) -> str

Which key, for a keyboard event: "Enter", "a", "ArrowUp".

prevent_default

pub fn prevent_default(e: Event) -> error

Do not do the default thing — submit the form, follow the link.

stop_propagation

pub fn stop_propagation(e: Event) -> error

Do not let the event carry on up the tree.

title

pub fn title() -> str

set_title

pub fn set_title(body: str) -> error

body

pub fn body() -> Option<Element>

The <body>, for a program that owns the whole page after all.