Skip to content

JavaScript hooks and debugging

Greg Bowler edited this page Sep 7, 2026 · 3 revisions

Flux is deliberately small, but it still needs to cooperate with any JavaScript we add ourselves.

Event listeners are reattached after swaps

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.

New Flux elements are initialised automatically

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

Listening to Flux lifecycle events

Flux dispatches a few document-level events so application JavaScript can cooperate with the update flow without taking over rendering.

Before a request

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 to fetch
  • 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.

Before and after rendering

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 as outer, inner, or live-outer
  • mode - the broad update mode, such as outer, inner, or attributes
  • 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 in flux: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.

Enable debug logging

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.

Failure behaviour

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.

Testing the behaviour

This repository includes two useful test layers:

  • vitest unit tests for the JavaScript classes in src/
  • behat browser tests for the website in examples/ and regression fixtures in test/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.

Clone this wiki locally