Elements
Reach a real page element with element() handles, link elements without ids, and run setup code with attach.
A handler can change count. Sometimes it also needs the real <input>, to focus it or measure it.
An element is not state. Your component sets up the page before the element exists. So Markless gives you a handle: a name that points at one element once that element is on the page.
Click +1 and focus in the figure.
What does a handle point at?
Press +1 and focus. Watch the input get the number and the focus.
The page's HTML. You never wrote an id.
<label for="a1">Count</label>
<input id="a1" value="0">
<button>+1 and focus</button>import { element, state } from '@markless/core'; export default function Counter() @{ let count = state(0); const field = element<HTMLInputElement>(); (a handle) <section> <label for={field}>Count</label> (no id written) <input el={field} value={count} /> (links it here) <button onClick={() => { count++; field?.focus(); }}>+1 and focus</button> </section>}- noteYour component ran and set up the page. The input did not exist yet, so
fieldpointed at nothing. - noteThe page is on screen. Now
fieldpoints at the real input.
Simplified. Markless makes the id for you. Its real value looks different from "a1".
Notice that the handler reaches the real input. You never wrote an id, but the label still points at the input.
import { element, state } from '@markless/core';
export default function Counter() @{
let count = state(0);
const field = element<HTMLInputElement>();
<section>
<label for={field}>Count</label>
<input el={field} value={count} />
<button onClick={() => { count++; field?.focus(); }}>+1 and focus</button>
</section>
}
el={field} links the handle to the <input>. In a handler, field is the element itself, or undefined if the element is not on the page. That is why the code writes field?.focus().
Links without ids
Look at for={field}. You never write an id. The compiler makes one and puts it on both elements. This works in for, aria-labelledby, aria-controls, aria-describedby, and popovertarget.
Setup code with attach
attach gives an element to your function. Use it for observers and outside libraries. If your function returns a cleanup function, Markless calls it when the element goes away.
<button
attach={(button) => {
const observer = new ResizeObserver(() => {});
observer.observe(button);
return () => observer.disconnect();
}}
onClick={() => count++}
>{count}</button>
attach works on page elements like <button>, not on components. On a component, it gives MARKLESS_ATTACH_HOST_ELEMENT_REQUIRED.
Next: What if a value comes from a slow network call? Async values →
