Skip to content

Instantly share code, notes, and snippets.

@travishorn
Last active July 20, 2023 20:05
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save travishorn/6182fa25d9cc9319149c4b405268295e to your computer and use it in GitHub Desktop.
Save travishorn/6182fa25d9cc9319149c4b405268295e to your computer and use it in GitHub Desktop.
Submit form on control change without pushing new state to history
// A pattern I often find myself using consists of a report-type page that
// displays any amount of data. At the top of that page is a `<form>` containing
// one or more `<select>` elements whose purpose is to filter the data on the
// report.
// The form uses `method="GET"`, which is great to make sure that the URL stays
// in line with what the page shows. A user can bookmark/favorite the URL and
// come back to it later. A user can share the URL so other users can see
// exactly the same page.
// Each `<select>` element has the attribute `onchange="this.form.submit()"` so
// the report changes every time a filter is changed. This way the data on the
// page stays in line with the filters at all times.
// On the server-side, the query parameters are read while generating the page.
// However, this approach has a couple downsides:
// 1. The history has quickly get out of hand. Each time the user changes a
// filter, a new state is pushed to the history. This means that when the
// user wants to go back, away from the report, they must click the **back**
// button multiple times (once for each time they changed a filter).
// 2. When a user changes a filter, looks at the updated report, then clicks the
// **back** button, the old version of the report is displayed (as expected),
// but the `<select>` they most recently changed still shows their new
// selection. So the filter is not in line with what's on the page.
// Instead, you can use JavaScript to set behavior on the page so that the
// report updates with filter changes as usual, and the **back** button takes
// the user back away from the report, ignoring any of the states where a filter
// was changed.
// To do so...
// 1. Remove `onchange="this.form.submit()"` from all `<select>` elements
// 2. Give the `<form>` element the attribute `id="filters"`
// 3. Include the following JavaScript after all DOM elements have been loaded
const filtersForm = document.querySelector("#filters");
const filterControls = filtersForm.querySelectorAll("select");
const handleFilterChange = () => {
const formData = new FormData(filtersForm);
const searchParams = new URLSearchParams(formData).toString();
history.replaceState(null, '', `?${searchParams}`);
window.location.reload();
}
filterControls.forEach((fc) => {
fc.addEventListener("change", handleFilterChange);
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment