Managing the DOM
When writing JavaScript for the browser, you usually need to interact with HTML elements on the page. In traditional web development, you might use document.querySelector('.my-class') or document.getElementById('my-id').
In Coralite, direct DOM querying is typically replaced by the built-in refs plugin. This plugin provides a safe, runtime DOM element targeting system specifically designed for component encapsulation. The Coralite refs system generates unique IDs directly assigned to the ref attribute, allowing you to leave the standard id attribute intact for other uses (like linking a label's for attribute to an input's id).
Why Use Refs Instead of querySelector? #
- Encapsulation: When you use
document.querySelector, you risk selecting an element outside your component, or selecting an element from a different instance of the same component on the page. - Unreliable Selectors: Because Coralite components can have many instances on a single page, the framework dynamically generates unique, namespaced selectors for each element marked with a
ref. Relying on standard CSS classes or IDs from a global script is highly unreliable as these selectors are managed internally by the framework. - Performance: The refs plugin caches DOM lookups, so subsequent calls to retrieve the same element are instant.
How to Use Refs #
Using the refs plugin is a simple two-step process: defining the reference in your HTML template, and accessing it in your client-side client block. Note: The refs function is only available within a component's client block; elements inside a component cannot be reliably targeted from outside the component.
1. Template Setup #
Add a ref="yourName" attribute to any HTML element inside your component's <template>.
During the server-side build, Coralite intercepts this attribute, generates a unique, component-instance-specific ID for the element, and maps it behind the scenes.
<template id="my-form">
<form>
<!-- Assign a ref name to the input -->
<input type="text" ref="usernameInput" placeholder="Enter username"></input>
<!-- Assign a ref name to the button -->
<button type="submit" ref="submitBtn">Submit</button>
</form>
</template>
2. Client-Side Client Block #
Inside your defineComponent's client block, the refs plugin provides a helper function: refs('yourName').
Call this function with the name you defined in the template to retrieve the actual DOM element.
<script type="module">
import { defineComponent } from 'coralite'
export default defineComponent({
client: ({ refs }) => {
// Safe DOM element retrieval
const input = refs('usernameInput')
const button = refs('submitBtn')
// Now use standard DOM APIs
button.addEventListener('click', (e) => {
e.preventDefault()
console.log(`Submitted username: ${input.value}`)
})
}
})
</script>
Under the Hood: Scoping & Safety #
The refs plugin is highly context-aware. Depending on how your component is compiled, it behaves differently to ensure maximum safety:
- Global Queries: For declarative components, the plugin queries
document.querySelector('[ref="..."]')where the generated unique ref ID is assigned. - Caching: The first time you call
refs('myEl'), it performs the DOM query. If found, the DOM node is cached locally in the plugin. The next time you callrefs('myEl'), it returns immediately without querying the DOM again. - Null Safety: If the element isn't found (perhaps conditionally rendered out), the helper safely returns
nullwithout throwing an error.
E2E Testing & Hydration Readiness #
To ensure deterministic and flake-free End-to-End (E2E) testing, Coralite provides built-in testing integrations that bridge the gap between Server-Side Rendering and client-side interactivity.
1. Waiting for Hydration #
Because Coralite components execute their client blocks natively in the browser after the initial HTML is parsed, E2E frameworks (like Playwright) might attempt to interact with elements before their event listeners are bound. To prevent this, your tests must await the global readiness promise immediately after navigating to a page.
2. Testing Selectors (data-testid) #
In development mode, Coralite's testing plugin automatically duplicates all ref="xyz" attributes into standard data-testid attributes. It assigns a unique, namespaced string using the pattern: <component-id>__<refName>-<index>.
For example, if you defined <button ref="submitBtn"> in a component named my-form, you should locate it in a Playwright test using getByTestId:
// Wait for Coralite to finish executing all component scripts
await page.waitForFunction(() => window.__coralite__.lifecycle.hydrated);
// Safely interact using the auto-generated test ID
await page.getByTestId('my-form__submitBtn-0').click();