Refs
While declarative programming is preferred, there are times when you need to access DOM elements directly to perform imperative operations (e.g., focusing an input, playing a video, initializing third-party libraries, or measuring element dimensions). WebComponent integrates Markup's ref system to let you reference elements easily.
Binding Refs in Templates
To bind a DOM element, add a ref attribute with a unique name to any element in your template.
1import { WebComponent, html } from '@beforesemicolon/web-component'2 3class FocusInput extends WebComponent {4 render() {5 return html`6 <div>7 <input type="text" ref="textInput" placeholder="Type here..." />8 <button onclick="${this.focusInput}">Focus Input</button>9 </div>10 `11 }12 13 focusInput = () => {14 // We will access the element here15 }16}Accessing Refs via this.refs
You can access all bound elements using the this.refs getter.
Since multiple elements can share the same ref name, each key in this.refs returns an array of elements matching that ref name. If there is only one element with that ref, it will still be returned inside an array (e.g., as the first element: this.refs.textInput[0]).
1focusInput = () => {2 const inputElement = this.refs.textInput?.[0]3 if (inputElement) {4 inputElement.focus()5 }6}Refs and Lifecycle
References are only populated after the component is rendered and mounted. They are not available inside the constructor.
If you need to perform imperative operations immediately when the element appears, use the onMount lifecycle hook.
1import { WebComponent, html } from '@beforesemicolon/web-component'2 3class AutoFocusInput extends WebComponent {4 render() {5 return html` <input type="text" ref="username" /> `6 }7 8 onMount() {9 // Element is now in the DOM, safe to access refs10 const input = this.refs.username?.[0]11 if (input) {12 input.focus()13 }14 }15}Multiple Elements / Dynamic Refs
You can use the same ref name on multiple elements to group them. WebComponent will return all rendered elements sharing that ref name in the order they appear.
1import { WebComponent, html } from '@beforesemicolon/web-component'2 3class ListSelector extends WebComponent {4 render() {5 return html`6 <ul>7 <li ref="item">Item A</li>8 <li ref="item">Item B</li>9 <li ref="item">Item C</li>10 </ul>11 <button onclick="${this.logItems}">Log Items</button>12 `13 }14 15 logItems = () => {16 const items = this.refs.item ?? []17 items.forEach((li, index) => {18 console.log(`Element ${index}:`, li.textContent)19 })20 }21}Refs are dynamic. If elements are conditionally rendered (e.g., using helper functions like when), they will be added to or removed from the this.refs arrays accordingly. Always check if the ref array and its elements exist before accessing them.