Skip to content
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

add docs for the Navigation API #21960

Merged
merged 56 commits into from
Nov 20, 2022
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
56 commits
Select commit Hold shift + click to select a range
f74c58c
add docs for the Navigation API
chrisdavidmills Oct 31, 2022
91e37e2
Update files/en-us/web/api/navigation_api/index.md
chrisdavidmills Nov 1, 2022
388f304
Update files/en-us/web/api/navigation_api/index.md
chrisdavidmills Nov 1, 2022
a9ca266
Update files/en-us/web/api/navigation_api/index.md
chrisdavidmills Nov 1, 2022
0c7dd0d
Merge branch 'main' into add-navigation-api-ref-docs
chrisdavidmills Nov 1, 2022
03a56c7
updating landing page according to domenics comments
chrisdavidmills Nov 1, 2022
6b7a904
remaining interface pages plus assorted fixes
chrisdavidmills Nov 2, 2022
9a2b3ba
add all member pages for Navigate
chrisdavidmills Nov 4, 2022
fad99a3
making fixes for domenic review comments
chrisdavidmills Nov 4, 2022
fc0e240
fix macro error
chrisdavidmills Nov 4, 2022
c8aeb47
Merge branch 'main' into add-navigation-api-ref-docs
Nov 4, 2022
c0ed753
add member pages for NavigationDestination and NavigationHistoryEntry
chrisdavidmills Nov 4, 2022
44c0b18
Merge branch 'add-navigation-api-ref-docs' of github.com:chrisdavidmi…
chrisdavidmills Nov 4, 2022
72f8ce2
attempt to fix folder name casing
chrisdavidmills Nov 4, 2022
b2e1e36
fix casing issue in directory name
chrisdavidmills Nov 7, 2022
4554e97
add all remaining pages, fix flaws
chrisdavidmills Nov 7, 2022
d264c4f
Merge branch 'main' into add-navigation-api-ref-docs
chrisdavidmills Nov 7, 2022
1e96c03
adding description of when disposal occurs
chrisdavidmills Nov 8, 2022
0e18d96
Merge branch 'add-navigation-api-ref-docs' of github.com:chrisdavidmi…
chrisdavidmills Nov 8, 2022
de7f600
Update files/en-us/web/api/navigation_api/index.md
chrisdavidmills Nov 9, 2022
43f9c1f
Update files/en-us/web/api/navigation_api/index.md
chrisdavidmills Nov 9, 2022
f3e94db
Update files/en-us/web/api/navigation_api/index.md
chrisdavidmills Nov 9, 2022
5a47f9e
Update files/en-us/web/api/navigation_api/index.md
chrisdavidmills Nov 9, 2022
b4a42f3
Update files/en-us/web/api/navigation_api/index.md
chrisdavidmills Nov 9, 2022
40f93b0
Update files/en-us/web/api/navigation_api/index.md
chrisdavidmills Nov 9, 2022
dc435da
Update files/en-us/web/api/navigation_api/index.md
chrisdavidmills Nov 9, 2022
12a51d2
Update files/en-us/web/api/navigation_api/index.md
chrisdavidmills Nov 9, 2022
33a7e8e
Update files/en-us/web/api/navigation_api/index.md
chrisdavidmills Nov 9, 2022
5938b37
adding fixes for wbamberg comments
chrisdavidmills Nov 9, 2022
ecee4c7
Merge branch 'add-navigation-api-ref-docs' of github.com:chrisdavidmi…
chrisdavidmills Nov 9, 2022
6333b97
adding fixes for wbamberg comments
chrisdavidmills Nov 9, 2022
8725f8e
making fixes for wbamberg comments
chrisdavidmills Nov 10, 2022
03efe1e
Update files/en-us/web/api/navigation/index.md
chrisdavidmills Nov 10, 2022
fa2f482
Update files/en-us/web/api/navigation/updatecurrententry/index.md
chrisdavidmills Nov 10, 2022
3bb1bed
Update files/en-us/web/api/navigation/updatecurrententry/index.md
chrisdavidmills Nov 10, 2022
251848f
Update files/en-us/web/api/navigation/updatecurrententry/index.md
chrisdavidmills Nov 10, 2022
b16a267
Update files/en-us/web/api/navigation/updatecurrententry/index.md
chrisdavidmills Nov 10, 2022
127c770
Update files/en-us/web/api/navigation/traverseto/index.md
chrisdavidmills Nov 10, 2022
3761a1d
Update files/en-us/web/api/navigation/back/index.md
chrisdavidmills Nov 10, 2022
991c81a
Update files/en-us/web/api/navigation/cangoback/index.md
chrisdavidmills Nov 10, 2022
e816cb1
Update files/en-us/web/api/navigation/cangoforward/index.md
chrisdavidmills Nov 10, 2022
9696dbc
Update files/en-us/web/api/navigation/currententry/index.md
chrisdavidmills Nov 10, 2022
504f04f
Update files/en-us/web/api/navigation/forward/index.md
chrisdavidmills Nov 10, 2022
ed352c5
Update files/en-us/web/api/navigation/navigate/index.md
chrisdavidmills Nov 10, 2022
b0a10a5
Update files/en-us/web/api/navigation/navigate/index.md
chrisdavidmills Nov 10, 2022
5e6be2a
Update files/en-us/web/api/navigation/navigate/index.md
chrisdavidmills Nov 10, 2022
4ad513f
Update files/en-us/web/api/navigation/reload/index.md
chrisdavidmills Nov 10, 2022
af10131
Update files/en-us/web/api/navigation/reload/index.md
chrisdavidmills Nov 10, 2022
3b887e3
Update files/en-us/web/api/navigation/transition/index.md
chrisdavidmills Nov 10, 2022
9c9e8da
Update files/en-us/web/api/navigation/traverseto/index.md
chrisdavidmills Nov 10, 2022
d1b7e04
Update files/en-us/web/api/navigation/traverseto/index.md
chrisdavidmills Nov 10, 2022
6448d5b
fixing linter trailing space errors
chrisdavidmills Nov 10, 2022
f4b5752
add structured-clonable information to navigate()
chrisdavidmills Nov 14, 2022
7f81b8b
add structured-clonable information to reload() and updateCurrentEntry()
chrisdavidmills Nov 15, 2022
1476651
making fixes in response to wbamberg navigationhistoryentry comments
chrisdavidmills Nov 16, 2022
1805606
moooooar fixes for wbamberg comments
chrisdavidmills Nov 20, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
194 changes: 194 additions & 0 deletions files/en-us/web/api/navigation_api/index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,194 @@
---
title: Navigation API
slug: Web/API/Navigation_API
page-type: web-api-overview
tags:
- API
- History
- Landing
- Navigate
- Navigation
- Navigation API
- Overview
- Scroll
- Traversal
browser-compat:
- api.Navigation
- api.NavigateEvent
- api.Window.navigation
---

{{securecontext_header}}{{seecompattable}}{{DefaultAPISidebar("Navigation API")}}
chrisdavidmills marked this conversation as resolved.
Show resolved Hide resolved

The **Navigation API** provides the ability to initiate, intercept, and manage browser navigation actions. It can also introspect an application's history entries. This is a successor to previous web platform features such as [The History API](/en-US/docs/Web/API/History_API) and {{domxref("window.location")}}, which solves their shortcomings and is specifically aimed at the needs of {{glossary("SPA", "single-page applications (SPAs)")}}.
chrisdavidmills marked this conversation as resolved.
Show resolved Hide resolved

## Concepts and Usage
chrisdavidmills marked this conversation as resolved.
Show resolved Hide resolved

In SPAs, the page template tends to stay the same during usage, and the content is dynamically rewritten as the user visits different pages or features. As a result, only one distinct page is loaded in the browser, which breaks the expected user experience of navigating back and forth between different locations in the viewing history. This problem can be solved to a degree via [The History API](/en-US/docs/Web/API/History_API), but it is not designed for the needs of SPAs. The Navigation API aims to bridge that gap.
chrisdavidmills marked this conversation as resolved.
Show resolved Hide resolved

The API is accessed via the {{domxref("Window.navigation")}} property, which returns a reference to a global {{domxref("Navigation")}} object. Each `window` object has its own corresponding `navigation` instance.

### Handling navigations

`navigation` has several associated events, the most notable being the {{domxref("Navigation/navigate_event", "navigate")}} event. This is fired when [any type of navigation](https://github.com/WICG/navigation-api#appendix-types-of-navigations) is initiated, meaning that you can control all page navigations from one central place. The `navigate` event has an event object of type {{domxref("NavigateEvent")}}, which contains detailed information including the navigation's destination URL, type, whether it contains `POST` form data or a download request, and more.

It also contains two methods:
chrisdavidmills marked this conversation as resolved.
Show resolved Hide resolved

- {{domxref("NavigateEvent.intercept", "intercept()")}} allows you to control what happens when the navigation is initiated using a callback handler function, which should return a promise. 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.
chrisdavidmills marked this conversation as resolved.
Show resolved Hide resolved
- {{domxref("NavigateEvent.scroll", "scroll()")}} allows you to manually initiate the browser's scroll behavior (e.g. to a fragment identifier in the URL), if it makes sense for your code, rather than waiting for the browser to handle it automatically.

Once a navigation is initiated, and your `intercept()` handler is called, a {{domxref("NavigationTransition")}} object instance is created (accessible via {{domxref("Navigation.transition")}}), which can be used used to track the process of the ongoing navigation.

> **Note:** In this context "transition" refers to the transition between one history entry and another. It isn't related to CSS transitions.

> **Note:** You can also call {{domxref("Event.preventDefault", "preventDefault()")}} to stop the navigation entirely.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note that canceling traversals is not yet implemented anywhere, although we've updated the explainer and spec since we have an implementation in progress. I'm not sure whether that's necessary to mention here, or elsewhere, or not at all.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've updated it to

Note: In future implementations you will be able to call {{domxref("Event.preventDefault", "preventDefault()")}} to stop the navigation entirely. This is specced out, but not currently implemented anywhere.

We can always update it when the implementation lands. Let me know.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, sorry, it's a bit more subtle than that. Canceling (most) push/reload/replace navigations is possible today. It's the traverse navigations which are still a future feature.

You can see the diff, which represents what's not possible yet but will be soon, in WICG/navigation-api@a430943

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah! I see. I've updated it again, to

Note: You can also call {{domxref("Event.preventDefault", "preventDefault()")}} to stop the navigation entirely in most cases. This works today for most push, reload, and replace navigations; cancellation of traverse navigations is not yet implemented.

I'll keep it simple for now here, and then link to your explainer text when it is published.


When the `initiate()` handler function's promise fulfills, the `Navigate` object's {{domxref("Navigation/navigatesuccess_event", "navigatesuccess")}} event fires, allowing you to run cleanup code after a successful navigation has completed. If it rejects, meaning the navigation has failed, {{domxref("Navigation/navigateerror_event", "navigateerror")}} fires instead, allowing you to gracefully handle the failure case. There is also a {{domxref("NavigationTransition.finished", "finished")}} property on the `NavigationTransition` object, which fullfills or rejects at the same time as the aforementioned events are fired, providing another path for handling the success and failure cases if it is needed.
chrisdavidmills marked this conversation as resolved.
Show resolved Hide resolved

### Programmatically traversing the navigation history

As the user navigates through your application, each new location navigated to results in the creation of a navigation history entry. Each history entry is represented by a distinct {{domxref("NavigationHistoryEntry")}} object instance. These contain several useful properties such as the entry's key, URL, and state information. You can return the entry that the user is currently navigated to right now using {{domxref("Navigation.currentEntry","currentEntry")}}, and an array of all existing history entries using {{domxref("Navigation.entries", "entries()")}}. Each `NavigationHistoryEntry` object has a {{domxref("NavigationHistoryEntry/dispose_event", "dispose")}} event, which fires when the entry is no longer part of the browser history (e.g. navigate back three times, then navigate forwards to somewhere else. Those three history entries will be disposed).
chrisdavidmills marked this conversation as resolved.
Show resolved Hide resolved

The `Navigation` object contains all the methods you'll need to traverse through the navigation history:

- {{domxref("Navigation.navigate", "navigate()")}} navigates to a new URL, creating a new navigation history entry.
- {{domxref("Navigation.reload", "reload()")}} reloads the current navigation history entry.
- {{domxref("Navigation.back", "back()")}} navigates to the previous navigation history entry, if that is possible.
- {{domxref("Navigation.forward", "forward()")}} navigates to the next navigation history entry, if that is possible.
- {{domxref("Navigation.traverseTo", "traverseTo()")}} navigates to a specific navigation history entry identified by its key value, which is obtained via the relevant entry's {{domxref("NavigationHistoryEntry.key")}} property.

Each one of the above methods returns an object containing two promises — `{ committed, finished }`. This allows the invoking function to wait on taking further action until:

- `committed` fulfills, meaning that the visible URL has changed and a new NavigationHistoryEntry has been created.
- `finished` fulfills, meaning that all promises returned by your `intercept()` handler are fulfilled. This is equivalent to the {{domxref("NavigationTransition.finished")}} promise fulfilling, when the {{domxref("Navigation/navigatesuccess_event", "navigatesuccess")}} event fires, as mentioned earlier.
- either one of the above promises rejects, meaning that the navigation transition has failed for some reason.

### State

The Navigation API allows you to store state on each history entry. This is developer-defined information — it can be whatever you like. For example, you might want to store a `visitCount` property that records the number of times a view has been visited, or an object containing multiple properties.

To get a {{domxref("NavigationHistoryEntry")}}'s state, you call its {{domxref("NavigationHistoryEntry.getState", "getState()")}} method. It is initially `undefined`, but when state information is set on the entry, it will return the previously-set state information.

Setting state is a bit more nuanced. You can't retrieve the state value and then update it directly — the copy stored on the entry will not change. Instead, you need to update it while performing a {{domxref("Navigation.navigate", "navigate()")}} or {{domxref("Navigation.reload", "reload()")}} — each one of these optionally takes an options object parameter, which includes a `state` property containing the new state to set on the history entry. When these navigations complete, the state change will be automatically applied.
chrisdavidmills marked this conversation as resolved.
Show resolved Hide resolved

In other cases a state change is not automatically applied, and you will need to do it manually once the navigation has completed. This is handled using {{domxref("Navigation.updateCurrentEntry", "updateCurrentEntry()")}}. The {{domxref("Navigation/currententrychange_event", "currententrychange")}} will fire when the current entry change is complete.

(I'M REALLY NOT SURE IF THIS SECTION ON STATE IS ACCURATE. I AM KINDA GUESSING AT THE "AUTOMATIC BEHAVIOR" MENTIONED ABOVE, AND WHEN UPDATECURRENTENTRY() SHOULD BE USED. NONE OF THE MATERIAL I'VE FOUND SEEMS PARTICULARLY CLEAR ON THIS POINT.)
chrisdavidmills marked this conversation as resolved.
Show resolved Hide resolved

### Limitations

There are a few perceived limitations with the Navigation API, which may or may not be limitating depending on how your app is set up. Find out about these by reading [Modern client-side routing: the Navigation API > What's missing?](https://developer.chrome.com/docs/web-platform/navigation-api/#whats-missing)

## Interfaces

- {{domxref("Navigation")}}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think these should be listed in alphabetical order.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I kind of did, but decided to put the event objects at the end in a separate alphabetical list.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's a minor point, but I do think consistent alpha order is the best option.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK, updated in next commit

- : Allows control over all navigation actions for the current `window` in one central place, including initiating navigations programmatically, introspecting navigation history entries, and managing navigations as they happen.
chrisdavidmills marked this conversation as resolved.
Show resolved Hide resolved
- {{domxref("NavigationDestination")}}
- : Represents the destination being navigated to in the current navigation.
- {{domxref("NavigationHistoryEntry")}}
- : Represents a single navigation history entry.
- {{domxref("NavigationTransition")}}
- : Represents an ongoing navigation.
- {{domxref("NavigationCurrentEntryChangeEvent")}}
- : Event object for the {{domxref("Navigation/currententrychange_event", "currententrychange")}} event, which fires when the currently navigated to history entry changes. Provides access to the navigation type, and the previous history entry that was navigated from.
- {{domxref("NavigateEvent")}}
- : Event object for the {{domxref("Navigation/navigate_event", "navigate")}} event, which fires when [any type of navigation](https://github.com/WICG/navigation-api#appendix-types-of-navigations) is initiated. Provides access to information about that navigation, and most notably the {{domxref("NavigateEvent.intercept", "intercept()")}}, which allows you to control what happens when the navigation is initiated.

## Extensions to the `Window` interface
chrisdavidmills marked this conversation as resolved.
Show resolved Hide resolved

- {{domxref("Window.navigation")}}
- : Returns the current `window`'s associated {{domxref("Navigation")}} object. Entry point for the API.

## Examples

> **Note:** Check out Domenic Denicola's [Navigation API live demo](https://gigantic-honored-octagon.glitch.me/).

### Handling a navigation using `intercept()`

```js
navigation.addEventListener('navigate', navigateEvent => {
// Exit early if this navigation shouldn't be intercepted,
chrisdavidmills marked this conversation as resolved.
Show resolved Hide resolved
// e.g. if the navigation is cross-origin, or a download request
if (shouldNotIntercept(navigateEvent)) return;

const url = new URL(navigateEvent.destination.url);

if (url.pathname.startsWith('/articles/')) {
navigateEvent.intercept({
async handler() {
// The URL has already changed, so show a placeholder while
//fetching the new content, such as a spinner or loading page
renderArticlePagePlaceholder();

// Fetch the new content and display when ready
const articleContent = await getArticleContent(url.pathname);
renderArticlePage(articleContent);
},
});
}
});
```

### Handling scrolling using `scroll()`

```js
navigation.addEventListener('navigate', navigateEvent => {
if (shouldNotIntercept(navigateEvent)) return;
const url = new URL(navigateEvent.destination.url);

if (url.pathname.startsWith('/articles/')) {
navigateEvent.intercept({
async handler() {
const articleContent = await getArticleContent(url.pathname);
renderArticlePage(articleContent);
navigateEvent.scroll();

const secondaryContent = await getSecondaryContent(url.pathname);
addSecondaryContent(secondaryContent);
},
});
}
});
```

### Traversing to a specific history entry

```js
// On JS startup, get the key of the first loaded page
// so the user can always go back there.
const {key} = navigation.currentEntry;
backToHomeButton.onclick = () => navigation.traverseTo(key);

// Navigate away, but the button will always work.
await navigation.navigate('/another_url').finished;
```

### Updating state

```js
navigation.navigate(url, {state: newState});
```

Or

```js
navigation.reload({state: newState});
```

Or if the state could not be updated automatically during a navigation transition:

```js
navigation.updateCurrentEntry({state: newState});
```

## Specifications

{{Specifications}}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's only one spec so it's unfortunate that we have two spec entries here. I guess that's because we have 2 BCD queries. We are in a bit of a mess wrt BCD and spec URLs, especially regarding API overview pages. But maybe it would be better to use a spec-url front matter key? Although tbh I'm not sure if that will work to override the BCD ones, or if you will just end up with three of them...

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't know what you mean by "spec-url front matter key".

I could just include a single browser compact entry, as it would still provide an idea.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can include an item in the front matter YAML for the page like:

spec-urls: https://drafts.csswg.org/css-display/#typedef-display-inside

...I think that will then be used for the specifications section instead of BCD (?). I hope so anyway.

I could just include a single browser compact entry, as it would still provide an idea.

If the spec-urls thing works as expected it would be better, because the BCD entry will presumably link to a fragment inside the spec, while in an overview page we really want to just link to the spec AFAICT.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've tried it, and the result is that we end up with three spec URLs listed rather than two. I could see this totally making sense if the spec-urls override the URLs taken from the browser-compat entries, but it is additive.

I'll leave my content as-is, for now.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fair enough. I wish this stuff worked better.


## Browser compatibility

{{Compat}}

## See also

- [Modern client-side routing: the Navigation API](https://developer.chrome.com/docs/web-platform/navigation-api/)
15 changes: 15 additions & 0 deletions files/jsondata/GroupData.json
Original file line number Diff line number Diff line change
Expand Up @@ -873,6 +873,21 @@
"properties": [],
"events": []
},
"Navigation API": {
"overview": ["Navigation API"],
"guides": [],
"interfaces": [
"NavigateEvent",
"Navigation",
"NavigationCurrentEntryChangeEvent",
"NavigationDestination",
"NavigationHistoryEntry",
"NavigationTransition"
],
"methods": [],
"properties": ["Window.navigation"],
"events": []
},
"Navigation Timing": {
"overview": ["Navigation timing API"],
"guides": ["/docs/Web/API/Navigation_timing_API/Using_Navigation_Timing"],
Expand Down