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.
ElementEventSubscriptionrawwrapraw_eventfindfind_infind_allcreatetextset_textattributeset_attributeremove_attributevalueset_valuecheckedset_checkedset_styleadd_classremove_classhas_classset_classappendinsert_beforeremovesameontargetevent_valuekeyprevent_defaultstop_propagationtitleset_titlebody
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.
raw: JsValue
Event
struct Event
Something that happened.
raw: JsValue
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.
target: JsValueevent: strhandler: JsValue
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.