Complete reference for the Eleva Attr plugin.
The main plugin object to install on your Eleva application.
import { Attr } from "eleva/plugins";
app.use(Attr, options);
| Property | Type | Default | Description |
|---|---|---|---|
enableAria |
boolean |
true |
When enabled, automatically handles ARIA attributes (aria-*) |
enableData |
boolean |
true |
When enabled, automatically handles data attributes (data-*) |
enableBoolean |
boolean |
true |
When enabled, intelligently handles boolean attributes based on truthy/falsy values |
enableDynamic |
boolean |
true |
When enabled, detects and binds dynamic DOM properties |
Manually synchronize attributes from one element to another. This method is exposed on the app instance when the Attr plugin is installed.
/**
* Update element attributes
* @param {HTMLElement} oldElement - The source element
* @param {HTMLElement} newElement - The target element to update
* @returns {void}
*/
app.updateElementAttributes(oldElement, newElement);
const MyComponent = {
setup({ signal }) {
const app = this; // Reference to app instance
const updateAttributes = () => {
const oldEl = document.getElementById('source');
const newEl = document.getElementById('target');
app.updateElementAttributes(oldEl, newEl);
};
return { updateAttributes };
},
template() {
return `
<div id="source" data-value="123" aria-label="Source">Source</div>
<div id="target">Target</div>
<button @click="updateAttributes">Sync Attributes</button>
`;
}
};
Always include appropriate ARIA attributes for interactive elements:
// Good - Accessible button
`<button
aria-label="Close navigation menu"
aria-expanded="${ctx.isMenuOpen.value}"
@click="toggleMenu"
>
<span aria-hidden="true">×</span>
</button>`
// Bad - No accessibility information
`<button @click="toggleMenu">×</button>`
Let HTML do the work before reaching for ARIA:
// Good - Semantic HTML
`<nav aria-label="Main navigation">
<ul>
<li><a href="/home">Home</a></li>
<li><a href="/about">About</a></li>
</ul>
</nav>`
// Avoid - ARIA overuse
`<div role="navigation" aria-label="Main navigation">
<div role="list">
<div role="listitem"><span role="link">Home</span></div>
</div>
</div>`
Use consistent, descriptive data attribute names:
// Good - Clear, consistent naming
`<div
data-user-id="${ctx.user.id}"
data-user-role="${ctx.user.role}"
data-is-active="${ctx.user.isActive}"
>`
// Bad - Inconsistent, unclear naming
`<div
data-id="${ctx.user.id}"
data-r="${ctx.user.role}"
data-a="${ctx.user.isActive}"
>`
Be explicit about boolean attribute conditions:
// Good - Clear condition
`<button disabled="${ctx.isLoading.value || !ctx.isValid.value}">
Submit
</button>`
// Good - Computed property
const canSubmit = () => !isLoading.value && isValid.value;
`<button disabled="${!ctx.canSubmit()}">Submit</button>`
// Avoid - Complex inline logic
`<button disabled="${!(ctx.data.value && ctx.data.value.name && !ctx.errors.value.name)}">
Submit
</button>`
Minimize attribute updates for better performance:
// Good - Batch related state
const formState = signal({
isSubmitting: false,
isValid: true,
errorMessage: ""
});
// Avoid - Many separate signals for related state
const isSubmitting = signal(false);
const isValid = signal(true);
const errorMessage = signal("");
Problem: Boolean attribute stays present regardless of value.
// Wrong - String "false" is truthy
`<button disabled="false">` // Still disabled!
// Correct - Use template binding
`<button disabled="${ctx.isDisabled.value}">`
Solution: Always use template binding ${} for dynamic boolean attributes.
Problem: ARIA attributes don’t reflect state changes.
// Check that you're using .value for signals
`aria-expanded="${ctx.isOpen}"` // Wrong - missing .value
`aria-expanded="${ctx.isOpen.value}"` // Correct
Solution: Ensure you’re accessing the .value property of signals.
Problem: Data attribute values contain quotes or special characters.
// Problem
`data-message="${ctx.message.value}"` // message contains quotes
// Solution - Encode special characters
const safeMessage = () => encodeURIComponent(message.value);
`data-message="${ctx.safeMessage()}"`
Problem: Input value doesn’t update when signal changes.
// Ensure two-way binding
`<input
value="${ctx.inputValue.value}"
@input="inputValue.value = $event.target.value"
/>`
Solution: Implement both value binding and input event handler for two-way data flow.
const app = new Eleva("App");
app.use(Attr); // Must be before mount()
app.component("MyComponent", { /* ... */ });
app.mount(document.getElementById("app"), "MyComponent");
import { Attr } from "eleva/plugins";
// or
const { Attr } = window.ElevaPlugins;
// Log attribute updates
const DebugComponent = {
setup({ signal }) {
const value = signal("test");
// Watch for changes
value.watch((newVal, oldVal) => {
console.log(`Value changed: ${oldVal} → ${newVal}`);
});
return { value };
},
template({ value }) {
return `<div data-debug="${value.value}">${value.value}</div>`;
}
};
// Inspect element attributes
const el = document.querySelector('[data-debug]');
console.log('Attributes:', el.attributes);
console.log('Dataset:', el.dataset);
console.log('ARIA:', el.getAttribute('aria-label'));
Eleva uses render batching via queueMicrotask to optimize performance. This means attribute updates happen asynchronously after signal changes.
When signals change, attribute updates are batched with template re-renders:
// Both changes result in ONE DOM update
isDisabled.value = true;
ariaLabel.value = "Loading...";
// Attributes update together in next microtask
After changing a signal, attributes won’t reflect the change immediately:
isExpanded.value = true;
console.log(element.getAttribute('aria-expanded')); // Still "false"!
// Wait for batched update
isExpanded.value = true;
queueMicrotask(() => {
console.log(element.getAttribute('aria-expanded')); // Now "true"
});
When testing attribute bindings, allow time for batched updates:
test("button becomes disabled", async () => {
isLoading.value = true;
// Wait for batched update
await new Promise(resolve => queueMicrotask(resolve));
expect(button.disabled).toBe(true);
expect(button.getAttribute('aria-busy')).toBe('true');
});
Boolean attribute toggling follows the same batching rules:
// These are batched together
disabled.value = true;
hidden.value = false;
checked.value = true;
// All three attributes update in one DOM operation
| Feature | Description |
|---|---|
| ARIA Handling | Automatic accessibility attribute management |
| Data Attributes | Custom data storage on elements |
| Boolean Attributes | Intelligent truthy/falsy handling |
| Dynamic Properties | DOM property synchronization |
| Zero Config | Works out of the box with sensible defaults |
| Selective Enable | Configure which features to enable |
| Manual API | updateElementAttributes() for advanced use cases |
For questions or issues, visit the GitHub repository.
| ← Back to Usage Patterns | Back to Attr Overview | Router Plugin → |