Skip to content

Conversation

absurdprofit
Copy link
Contributor

@absurdprofit absurdprofit commented Jan 24, 2025

This PR adds a proposal for extensions to the existing anchor (<a>) element. You can see the proposal in action in this demo written in Lit.

Copy link
Collaborator

@domenic domenic left a comment

Choose a reason for hiding this comment

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

Thanks for showing us what this would look like!

I think what is most missing here is some evidence that this kind of functionality is something developers want. Especially for the more complex pieces. Is anyone creating experiences like these today using JavaScript? Can you find live examples, ideally on popular sites? Or even just StackOverflow questions of people asking how to do this sort of thing?

The `reload=""` attribute will reload the current entry. Note: the `href=""` attribute is ignored.
```html
<a reload>Refresh results</a>
```
Copy link
Collaborator

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...

Copy link
Contributor Author

@absurdprofit absurdprofit Jan 31, 2025

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.

Copy link
Contributor Author

@absurdprofit absurdprofit Jan 31, 2025

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.

```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.
Copy link
Collaborator

Choose a reason for hiding this comment

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

Similar questions for the href-less versions here.

Additionally, to get key=""s you need to use JavaScript. So I can't really understand when you would use this.

I can see a use case for <a href="/" rel="prev" traverse>, however.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The key="..." attribute is merely a convenience instead of a Super Declarative™ API. This exists as a method for when you want to be super precise about which entry you navigate to unlike the href="..." pattern matching feature. Additionally having an <a> tag that navigates to a specific existing entry without clickjacking is essentially impossible without something like the NavigateEvent.sourceElement feature.

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 <a> which can lead to a messy history stack as a "previous" page now exists at the head of the history stack.

```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>
Copy link
Collaborator

Choose a reason for hiding this comment

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

What happens if there are no links in history with these hrefs?

Copy link
Contributor Author

Choose a reason for hiding this comment

The 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 <a>s as they exist now is "push", meaning if a link doesn't exist it simply becomes a push navigation. The proposal also adds the ability to modify this "base navigate behaviour" so for example a replace="" boolean attribute would make the fallback instead be a replace navigation.

};
a.navigateinfo = {
type: "push",
};
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 this gets pretty messy.

The issues mostly come about with the interaction between the JS properties and the attributes.

  • What does the attribute return, when the JS property is set to an object?
  • What does the JS property return, when the attribute is set to a string?
  • How does el.setAttribute("navigatestate", el.getAttribute("navigatestate")) behave? Does it change what state is sent? Does it change the value of the JS property?
  • How does el.navigatestate = el.navigatestate behave? Does it change what state is sent? Does it change the value of the attribute?

I would suggest leaving these as strings only and saying that if you want more complex objects, you need to use JavaScript.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

  • The getAttribute() would return the attribute as seen in HTML.
  • The JS property at first would be the same as the HTML attribute. In other words the attribute would serve as a "default value".
  • No, the attribute wouldn't change the value of the JS property. The JS property takes precedent in the state that is sent.
  • No, the JS property wouldn't change the value of the attribute.

TLDR; the idea here is the navigatestate and navigateinfo properties/attributes would mirror the behaviour of the value attribute/property from the <input> element. The attribute has no effect on the property beyond its use as a "default value".

This wasn't very clear in the demo and I apologise.

```

#### 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.
Copy link
Collaborator

Choose a reason for hiding this comment

The 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?

Copy link
Contributor Author

Choose a reason for hiding this comment

The 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.
image

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.
Screenshot 2025-01-30 212946

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 <a> being push further encourages developers to use JavaScript based solutions that aren't synched to the browser history stack to avoid duplicate history entries polluting the stack (among other issues).

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 window.location.search, I could restore the current tab when the page loads. CodeSandbox implements file tabs like this.

@absurdprofit
Copy link
Contributor Author

Can you find live examples, ideally on popular sites? Or even just StackOverflow questions of people asking how to do this sort of thing?

Here's a SO post I came across once about using only an <a> to implement a "back link".

@absurdprofit
Copy link
Contributor Author

absurdprofit commented Jan 31, 2025

I think what is most missing here is some evidence that this kind of functionality is something developers want.

By any chance are there any channels you recommend I use to solicit this kind of feedback?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants