-
Notifications
You must be signed in to change notification settings - Fork 0
JavaScript hooks and debugging
Flux is deliberately small, but it still needs to cooperate with any JavaScript we add ourselves.
Internally, Flux wraps addEventListener so it can keep a record of listeners attached to elements. When a Flux update replaces an element, Flux reattaches those listeners to the matching new element.
That means code like this continues to work after a refresh:
const button = document.querySelector("button.tracker");
button.addEventListener("click", () => {
console.log("tracked");
});As long as the new document contains the corresponding replacement element, Flux carries the listener over.
If an update inserts new elements containing data-flux, Flux scans and initialises them as part of the replacement process. This is particularly important for:
- nested submit buttons
- new Flux links
- live regions introduced by an update
Flux dispatches a few document-level events so application JavaScript can cooperate with the update flow without taking over rendering.
flux:before-request fires just before Flux calls fetch.
document.addEventListener("flux:before-request", event => {
event.detail.requestOptions.headers = new Headers(event.detail.requestOptions.headers);
event.detail.requestOptions.headers.set("X-Requested-With", "Flux");
});The event detail contains:
-
url- the URL Flux is about to fetch -
requestOptions- the options that will be passed tofetch -
method- the request method, normalised to lower case -
historyState- Flux's internal navigation state for this request
Both url and requestOptions are mutable. That makes this event useful for adding headers, appending query parameters, or passing lightweight client state back to the server.
flux:before-render fires after Flux has worked out which elements will update, but before it changes the DOM.
flux:after-render fires after those updates have been applied.
document.addEventListener("flux:before-render", event => {
for(const update of event.detail.updates) {
console.log("Will update", update.existingElement, "with", update.newElement);
}
});
document.addEventListener("flux:after-render", event => {
for(const update of event.detail.updates) {
console.log("Updated element", update.element);
}
});Each update record contains:
-
type- the Flux update type, such asouter,inner, orlive-outer -
mode- the broad update mode, such asouter,inner, orattributes -
existingElement- the element currently in the document -
newElement- the matching element from the returned document -
element- the element that remains in the document after rendering, available influx:after-render
These hooks are a good fit for small behaviours such as preserving scroll position, recording which item was last seen, or adding request metadata. They are not intended to replace Flux's server-rendered update model.
When we are working on Flux itself, or trying to understand a tricky page, we can enable debug mode:
import { FluxDebug } from "@phpgt/flux";
import "@phpgt/flux";With debug mode enabled, Flux logs extra detail about event registration, target storage, and focus restoration.
If Flux cannot process a returned document properly, it logs an error and reloads the page. The most common reason would be a response that is not a complete HTML document.
Important
Flux is designed around full HTML responses. If we return a fragment with no <head>, Flux treats that as an error and falls back to a normal reload.
This repository includes two useful test layers:
-
vitestunit tests for the JavaScript classes insrc/ -
behatbrowser tests for the website inexamples/and regression fixtures intest/fixtures/example/
The browser suite is the best source of truth for end-to-end behaviour because it exercises the real DOM update flow.
The walkthrough is complete. See the list of flux attributes as the reference sheet, or browse the runnable examples.
PHP.GT/Flux is a separately maintained component of PHP.GT/WebEngine.