-
Notifications
You must be signed in to change notification settings - Fork 28
Anchor extensions #278
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?
Anchor extensions #278
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -84,6 +84,10 @@ backButtonEl.addEventListener("click", () => { | |
| - [Notifications on entry disposal](#notifications-on-entry-disposal) | ||
| - [Current entry change monitoring](#current-entry-change-monitoring) | ||
| - [Complete event sequence](#complete-event-sequence) | ||
| - [Extensions to the Anchor element](#extensions-to-the-anchor-element) | ||
| - [Default navigation type](#default-navigation-type) | ||
| - [The navigateinfo="..." and navigatestate="..." attributes](#the-navigateinfo-and-navigatestate-attributes) | ||
| - [Additional example](#additional-example) | ||
| - [Guide for migrating from the existing history API](#guide-for-migrating-from-the-existing-history-api) | ||
| - [Performing navigations](#performing-navigations) | ||
| - [Warning: back/forward are not always opposites](#warning-backforward-are-not-always-opposites) | ||
|
|
@@ -1033,6 +1037,92 @@ The commit steps are: | |
| 1. Any now-unreachable `NavigationHistoryEntry` instances fire `dispose`. | ||
| 1. The URL bar updates. | ||
|
|
||
| ### Extensions to the Anchor element | ||
| Currently an `<a>` only supports push navigations. This proposal aims to extend its existing behaviour with a few new and existing attributes. | ||
| Namely: | ||
| - `reload=""`, `replace=""` and `traverse=""` as boolean attributes. | ||
| - `navigateinfo=""` and `navigatestate=""` as string attributes. | ||
| - `href="..."` and `rel="next/prev"` with the addition of the string attribute `key="..."` as traversal hints. | ||
|
|
||
| #### Default navigation type | ||
| The default navigation type of a `<a>` is always a push navigation. The new boolean attributes `replace=""`, `reload=""` and `traverse=""` serve as the default navigation type modifier. | ||
|
|
||
| The default navigation type will still be a push navigation. | ||
| ```html | ||
| <a href="posts">Posts</a> | ||
| ``` | ||
|
|
||
| The `reload=""` attribute will reload the current entry. Note: the `href=""` attribute is ignored. | ||
| ```html | ||
| <a reload>Refresh results</a> | ||
| ``` | ||
|
|
||
| The `replace=""` attribute in addition to the `href="..."` attribute will replace the current entry with a new history entry. | ||
| ```html | ||
| <a href="/login" replace>Logout</a> | ||
| ``` | ||
| The `traverse=""` attibute allows `<a>`s to link to existing entries. When using the `traverse=""` navigation type modifier the `rel="next/prev"`, `key="..."` and `href="..."` attributes, become optional hints. | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Similar questions for the href-less versions here. Additionally, to get I can see a use case for There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The A solid use case for something like this that I thought of is implementing breadcrumbs, most sites currently implement such a feature using the basic push navigation behaviour of the |
||
| The `rel="..."` is an existing attribute that hints at the relationship of the linked URL to the current document. When used with `traverse=""` it serves as a directional hint. | ||
| ```html | ||
| <a rel="prev" traverse>Go back</a> | ||
| <a rel="next" traverse>Go forward</a> | ||
| ``` | ||
| The `href="..."` is an existing attribute that specifies the URL the `<a>` points to. When used with the `traverse=""` attribute the anchor will point to the closest entry with a matching URL. The `rel="next/prev"` attribute can be used to specify the search direction. | ||
| ```html | ||
| <a href=".." traverse>Tab 1</a> <!-- Given the current URL pathname is /parent/child this anchor navigates to an entry with /parent as the pathname. --> | ||
| <a href="/" rel="prev" traverse>Go home</a> | ||
| <a href="/posts" rel="next" traverse>Posts</a> | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What happens if there are no links in history with these hrefs? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If there are no links in the history then it should fallback to the base navigate behaviour. The default base navigate behaviour for |
||
| ``` | ||
| The `key="..."` is a new attribute that allows you to specify a history entry key the `<a>` points to. This attribute only works with the `traverse=""` attribute and is ignored otherwise. If no matching entry is found the behaviour is the same as `<a>Dead link</a>`. | ||
| ```html | ||
| <a key="7c6a6481-c4bc-40f5-9617-0287441a3418" traverse>Go to 7c6a6481-c4bc-40f5-9617-0287441a3418</a> | ||
| ``` | ||
| The `key="..."` attribute takes precedence over `href=""` meaning if an entry with a matching key isn't found, the anchor will fallback to traversal with the `href="..."` hint. | ||
| ```html | ||
| <a href="/" key="7c6a6481-c4bc-40f5-9617-0287441a3418" traverse>Go home</a> | ||
| ``` | ||
|
|
||
| The `traverse=""` boolean attribute will always take precedence over push, replace and reload navigation types. Meaning if no matching entry is found the `<a>` will fallback to the next navigation type. | ||
| ```html | ||
| <a href="/" rel="prev" traverse replace>Go home</a> <!-- / Will replace the current entry if no matching entry is found --> | ||
| ``` | ||
|
|
||
| #### The `navigateinfo="..."` and `navigatestate="..."` attributes | ||
| Finally two string attributes have been added for passing state and info via an `<a>`. | ||
| ```html | ||
| <a id="home" href="/" navigateinfo="push" navigatestate="newuser">Home</a> | ||
| ``` | ||
| The new attributes will have corresponding DOM properties for passing non-string data types to entries. | ||
| ```js | ||
| const a = document.getElementById("home"); | ||
| a.navigatestate = { | ||
| user: { | ||
| name: "John Doe", | ||
| }, | ||
| }; | ||
| a.navigateinfo = { | ||
| type: "push", | ||
| }; | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think this gets pretty messy. The issues mostly come about with the interaction between the JS properties and the attributes.
I would suggest leaving these as strings only and saying that if you want more complex objects, you need to use JavaScript. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
TLDR; the idea here is the navigatestate and navigateinfo properties/attributes would mirror the behaviour of the value attribute/property from the This wasn't very clear in the demo and I apologise. |
||
| ``` | ||
|
|
||
| Note: `navigatestate=""` is ignored for traverse navigations. | ||
| ```html | ||
| <a href="/posts" navigateinfo="push" navigatestate="newstate">Home</a> | ||
| <a navigateinfo="reload" navigatestate="newstate" reload>Refresh</a> | ||
| <a href="/login" navigateinfo="replace" navigatestate="newstate" replace>Logout</a> | ||
| <a rel="prev" navigateinfo="traverse" navigatestate="newstate" traverse>Back</a> <!-- navigatestate="newstate" is ignored --> | ||
| ``` | ||
|
|
||
| #### Additional Example | ||
| Tabbed navigation could be implemented without JavaScript. By using `traverse=""` anchors will point to existing entries if they exist but fallback to a push navigation if a matching entry doesn't already exist. | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is this actually a user experience that people want? Are there any sites today that are implementing this kind of user experience using JavaScript? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The main tabs in Gmail are implemented using JavaScript, however like many implementations online these navigations aren't added to the history stack and it's not trivial to implement synchronisation with the history stack especially with the old history API. This is also the case for Azure which is an SPA that uses hash based navigation but they still don't persist these navigations to the stack. I couldn't find any example of tab navigation by history stack traversal via JS but from my experience a lot of the anxiety around doing things like that on the web comes from the fact that it's hard. Also the default navigation behaviour of an One problem with omitting tab navigation from the browser history is that users lose their position upon reloading the page, forcing them to manually restore their previous state. Additionally, tabs are not directly linkable, which can be frustrating if you wish to share or revisit a specific tab. To address this in the past, I have implemented a solution that synchronizes the tab state with the query parameters in the URL. This approach ensures that users can reload the page without losing their current tab selection while also making tabs linkable. At the same time, it avoids polluting the history stack with duplicate entries by updating the URL without pushing new history entries unnecessarily. By reading the initial tab state from |
||
| ```html | ||
| <nav class="tabs"> | ||
| <a class="tab" href="tab-1" traverse>Tab 1</a> | ||
| <a class="tab" href="tab-2" traverse>Tab 2</a> | ||
| <a class="tab" href="tab-3" traverse>Tab 3</a> | ||
| </nav> | ||
| ``` | ||
|
|
||
| ## Guide for migrating from the existing history API | ||
|
|
||
| For web developers using the API, here's a guide to explain how you would replace usage of `window.history` with `window.navigation`. | ||
|
|
||


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.
Can you explain the benefits of this over
<button onclick="location.reload()">? It's unclear if reloading really fits the hyperlink paradigm well...Uh oh!
There was an error while loading. Please reload this page.
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.
Yea I'm not very sure either, I added it more for completeness sake. I did think it would be nice to have a "open in next tab" functionality for a refresh UI element which a
<button>would lack, which is why I still added it. Beyond content security policies blocking inline JS I don't see any problem with just using JS like in your example to achieve the same behaviour. If there isn't much interest in something like this I would drop it.Uh oh!
There was an error while loading. Please reload this page.
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 do have a question though, what kind of navigation does
<a href='.'>trigger i.e. what should NavigateEvent.navigationType yield? I tried this in chrome a few times, sometimes it would be a push navigation and sometimes it would be a replace navigation. I ask because my initial intuition was this would have been a "reload" navigation but since that wasn't the case I felt motivated to add a keyword for it.