-
Notifications
You must be signed in to change notification settings - Fork 22.9k
Technical review: Add precommitHandler() and other info to Navigation API docs #41524
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Technical review: Add precommitHandler() and other info to Navigation API docs #41524
Conversation
Preview URLs
(comment last updated: 2025-10-14 15:40:51) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for working on this, see comments
- : A callback function that defines what the navigation handling behavior should be. This generally handles resource fetching, and returns a promise. | ||
- : A callback function that defines what the navigation handling behavior should be; it returns a promise. This function will run after the {{domxref("Navigation.navigate_event", "navigate")}} event has fired and the {{domxref("Navigation.currentEntry", "currentEntry")}} property has been updated. | ||
- `precommitHandler` {{optional_inline}} | ||
- : A callback function that defines any behavior that should occur just before the navigation has finished; it accepts a controller object as an argument and returns a promise. This function will run after the {{domxref("Navigation.navigate_event", "navigate")}} event has fired but before the {{domxref("Navigation.currentEntry", "currentEntry")}} property has been updated. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's a bit strange to say "after navigate event has fired" because you can only intercept once the event is fired.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm pretty sure I stole that wording from the explainer ;-)
But I see what you mean — the whole point is that it is run after the event is fired, so it isn't worth saying. I've updated this, and similar wording, to remove that bit, so for example:
This function will run before the {{domxref("Navigation.currentEntry", "currentEntry")}} property has been updated
- : Thrown if: | ||
- The event was dispatched by a {{domxref("EventTarget.dispatchEvent", "dispatchEvent()")}} call, rather than the user agent. | ||
- The navigation cannot be intercepted ({{domxref("NavigateEvent.canIntercept")}} is `false`). | ||
- A `precommitHandler()` callback was called on a non-cancelable event ({{domxref("Event.cancelable")}} is `false`). |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
s/called/provided
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Updated; thanks.
|
||
## Description | ||
|
||
The `intercept()` method is used to implement custom navigation behavior when a link is clicked in an application. It does this via a couple of different callbacks. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not only links. Also form submissions and any type of programmatic navigation like calling pushState
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good point. I've updated this bit to
The
intercept()
method is used to implement custom SPA navigation behavior when a navigation occurs, for example when a link is clicked, a form is submitted, or a programmatic navigation is initiated (using {{domxref("History.pushState()")}}, {{domxref("Window.location")}}, etc.).It does this via a couple of different callbacks,
handler()
andprecommitHandler()
.
|
||
### Handling immediate navigations with `handler()` | ||
|
||
The `handler()` callback is generally used to handle resource fetching and updating the UI to show the content for the new page, which will typically look like this, at a basic level: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Don't know about "at a basic level"...
Technically, a way to describe this is that handler
occurs as a response to a committed navigation, and precommitHandler
occurs as a way to modify or cancel an in-flight navigation, or to perform operations while the navigation is ongoing and before it is committed.
This also works with the browser UI - e.g. the precommit handlers work before the current URL is updated in the URL bar / omnibox.
I think this distinction was a bit missing for me throughout.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This makes sense, yeah. I've attempted to update the descriptive text around the two handlers to follow this pattern a bit more. Let me know what you think.
|
||
### Handling precommit actions with `precommitHandler()` | ||
|
||
However, depending on the URL, you might not always want to commit the navigation immediately. For example, if the user navigates to a restricted page and the user is not signed in, you may want to redirect the browser to a sign-in page. This kind of scenario can be dealt with using the `precommitHandler()` callback, which will run after the {{domxref("Navigation.navigate_event", "navigate")}} event has fired but before the {{domxref("Navigation.currentEntry", "currentEntry")}} property has been updated. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not only depending on the URL. It can depend on all kinds of conditions, e.g. the source element.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I changed this bit anyway, as per the previous comment.
The `precommitHandler()` callback takes a `controller` object as an argument, which contains a `redirect()` method. The `redirect()` method takes two parameters — a string representing the URL to redirect to, and an options object containing two parameters: | ||
|
||
- `state` {{optional_inline}} | ||
- : Contains any state information you want to pass along with the navigation, for example for logging or tracking purposes. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This state is the same as the state in https://developer.mozilla.org/en-US/docs/Web/API/NavigationHistoryEntry/getState
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good point; I've added a mention and a link to the description:
Contains any state information you want to pass along with the navigation, for example, for logging or tracking purposes. The state for the navigation can subsequently be retrieved via {{domxref("NavigationHistoryEntry.getState()")}}.
|
||
Both `precommitHandler()` and `handler()` callbacks can be included inside the same `intercept()` call. | ||
|
||
- When the `precommitHandler()` promise fulfills, the browser moves on to run any `handler()` callback present. If `precommitHandler()` rejects, `navigateerror` fires, the `committed` and `finished` promises reject, and the navigation is cancelled. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Note that multiple intercept calls are possible, which means something like "all precommit handlers are called first, and when all of them resolved, the navigation commits and the handler callbacks are called".
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OK, this is good to know. I've rewritten the section like so:
Both precommitHandler()
and handler()
callbacks can be included inside the same intercept()
call.
-
First, the
precommitHandler()
handler runs.- When the
precommitHandler()
promise fulfills, the navigation commits. - If the
precommitHandler()
rejects,navigateerror
fires, thecommitted
andfinished
promises reject, and the navigation is cancelled.
- When the
-
When the navigation commits, a new {{domxref("NavigationHistoryEntry")}} is created for the navigation, and its
committed
promise fulfills. -
Next, the
handler()
promise runs.- When the
handler()
promise fulfills and thenavigatesuccess
event fires, the navigationfinished
promise fulfills as well, to indicate the navigation is finished. - If
handler()
rejects,navigateerror
fires, thefinished
promise rejects, and the navigation is canceled.
- When the
Note that the above process is upheld even across multiple intercept()
calls on the same NavigateEvent
. All precommitHandler()
callbacks are called first, and when all of them resolve, the navigation commits, and all the handler()
callbacks are called.
|
||
- {{domxref("NavigateEvent.intercept", "intercept()")}} takes as an argument a callback handler function returning a promise. It allows you to control what happens when the navigation is initiated. For example, in the case of an SPA, it can be used to load relevant new content into the UI based on the path of the URL navigated to. | ||
- {{domxref("NavigateEvent.intercept", "intercept()")}} allows you to specify custom behavior for navigations, and can take the following as arguments: | ||
- Callback handler functions allowing you to specify what happens both _when_ the navigation is initiated and _just before_ the navigation is initiated. For example, it could be used to load relevant new content into the UI based on the path of the URL navigated to, or redirect the browser to a sign-in page if the URL points to a restricted page and the user is not signed in. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The word "initiated" a bit inaccurate. It's before/when the navigation is committed.
The navigate event itself correspond to when the navigation was "initiated".
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Got it. I've changed these instances to "committed".
Description
Chromium 141 adds support for the
precommitHandler
option of theNavigateEvent.intercept()
method. See https://chromestatus.com/feature/5134734612496384.This PR adds documentation for it.
It also overhauls the
intercept()
method page in general, adding a bunch of details in a large "Description" section that was missing before. I think more work needs to be done here, but it is out of the scope of this PR for me to try to fix everything. For now, I've added a bit more information about how theintercept()
features work so that the page is a bit more informative.Motivation
Additional details
Related issues and pull requests