@mypolis.eu/fixi
v0.2.0
Published
A lightweight, modern fork of [fixi](https://github.com/bigskysoftware/fixi) - progressive enhancement for HTML with declarative AJAX.
Readme
@mypolis.eu/fixi
A lightweight, modern fork of fixi - progressive enhancement for HTML with declarative AJAX.
Installation
npm install @mypolis.eu/fixiBasic Usage
Import the library and it automatically enhances your HTML:
import "@mypolis.eu/fixi/core";Then add fx-* attributes to your HTML:
<button
fx-action="/api/like"
fx-target="#likes"
fx-swap="innerHTML"
>
Like
</button>
<div id="likes">0 likes</div>Core Attributes
fx-action (required)
The URL to fetch when triggered.
<button fx-action="/api/data">Load Data</button>fx-target
CSS selector for the element to update. Defaults to the element itself.
<button
fx-action="/api/update"
fx-target="#result"
>
Update
</button>
<div id="result">Old content</div>fx-swap
How to swap the content. Options: innerHTML, outerHTML, beforebegin, afterbegin, beforeend, afterend, none, or a custom function.
<!-- Replace inner content -->
<div
fx-action="/api/content"
fx-swap="innerHTML"
>
Loading...
</div>
<!-- Replace entire element -->
<div
fx-action="/api/card"
fx-swap="outerHTML"
>
Loading...
</div>
<!-- Insert adjacent HTML -->
<div
fx-action="/api/item"
fx-swap="beforeend"
>
Items:
</div>fx-trigger
Event that triggers the action. Defaults: click (button/div), submit (form), change (input/select/textarea).
<!-- Trigger on hover -->
<div
fx-action="/api/preview"
fx-trigger="mouseenter"
>
Hover me
</div>
<!-- Trigger on intersection (lazy loading) -->
<div
fx-action="/api/lazy"
fx-trigger="intersect"
>
Load when visible
</div>
<!-- Custom threshold and root margin for intersection -->
<div
fx-action="/api/lazy"
fx-trigger="intersect"
fx-intersect-threshold="0.5"
fx-intersect-root-margin="100px"
>
Load when 50% visible with 100px margin
</div>fx-method
HTTP method. Default: GET
<form
fx-action="/api/create"
fx-method="POST"
>
<input name="title" />
<button type="submit">Create</button>
</form>fx-indicator
Show loading state during requests.
<!-- Toggle fx-requesting class on the button -->
<button
fx-action="/api/save"
fx-indicator
>
Save
</button>
<!-- Toggle custom class -->
<button
fx-action="/api/save"
fx-indicator
fx-indicator-class="is-loading"
>
Save
</button>
<!-- Toggle multiple attributes for accessibility -->
<button
fx-action="/api/save"
fx-indicator
fx-indicator-attr="disabled,aria-busy,aria-disabled"
>
Save
</button>
<!-- Use external indicator element -->
<button
fx-action="/api/save"
fx-indicator="#spinner"
>
Save
</button>
<span
id="spinner"
class="hidden"
>Loading...</span
>Events
The library dispatches events throughout the request lifecycle:
fx:init / fx:inited
Fired when an element is first processed.
document.addEventListener("fx:init", (e) => {
console.log("Element initialized:", e.target);
});fx:config
Intercept and modify configuration before the request.
element.addEventListener("fx:config", (e) => {
// Add custom headers
e.cfg.headers["X-Custom"] = "value";
// Use custom fetch
e.cfg.fetch = myCustomFetch;
});fx:before
Fired before the request starts. Cancelable.
element.addEventListener("fx:before", (e) => {
if (!confirm("Are you sure?")) {
e.preventDefault(); // Cancel the request
}
});fx:after
Fired after successful response. Cancelable.
element.addEventListener("fx:after", (e) => {
console.log("Response received:", e.cfg.text);
});fx:swapped
Fired after DOM has been updated.
element.addEventListener("fx:swapped", (e) => {
// Re-initialize any JS that depends on the new DOM
initTooltips();
});fx:error
Fired when request fails.
element.addEventListener("fx:error", (e) => {
console.error("Request failed:", e.cfg.error);
});fx:finally
Always fired after request completes (success or error).
Advanced Usage
Manual Processing
If you add elements dynamically, manually trigger processing:
import {fixi} from "@mypolis.eu/fixi/core";
// After adding new element to DOM
const newElement = document.createElement("div");
newElement.setAttribute("fx-action", "/api/data");
document.body.appendChild(newElement);
fixi.process(newElement);Custom Swap Function
<script>
element.addEventListener("fx:config", (e) => {
e.cfg.swap = async (cfg) => {
// Custom logic to handle the response
const data = JSON.parse(cfg.text);
cfg.target.innerHTML = renderTemplate(data);
};
});
</script>View Transitions
The library automatically uses document.startViewTransition when available:
// Check if browser supports view transitions
if (document.startViewTransition) {
console.log("Smooth transitions enabled!");
}Ignoring Elements
Use fx-ignore to prevent processing of child elements:
<div fx-ignore>
<button fx-action="/api/ignored">This won't be enhanced</button>
</div>TypeScript Support
Global event types are automatically available when importing:
import type {FxConfigEvent, FxBeforeEvent, FxAfterEvent} from "@mypolis.eu/fixi/core";
element.addEventListener("fx:config", (e: FxConfigEvent) => {
e.cfg.fetch = customFetch;
});Configuration Interface
interface FixiConfig {
trigger: Event; // The triggering event
action: string; // URL to fetch
method: string; // HTTP method
target: Element; // Element to update
swap: string | Function; // Swap strategy
body: FormData | null; // Request body
headers: Record<string, string>;
fetch: typeof fetch; // Fetch function to use
confirm?: () => Promise<boolean>;
// ... more options
}Browser Support
| Browser | Version | Year | | ------- | ------- | ---- | | Chrome | 74+ | 2019 | | Firefox | 90+ | 2021 | | Safari | 14.1+ | 2021 | | Edge | 79+ | 2020 |
All modern browsers supporting ES2022.
Note: View Transitions (for smooth animations) require Chrome 111+, but the library works perfectly without them in older browsers.
License
MIT - Based on fixi by Big Sky Software
