-
Notifications
You must be signed in to change notification settings - Fork 26
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
Behavior of navigate(currentURL, { replace }) #111
Comments
Is that true for
There's more precedent when it comes to enums, with one being
|
No. The more precise statement is, we want to encourage people doing SPA navs to go through the
I meant precedent in regards to letting people perform same-URL push navigations. I'm not sure we want to make that functionality easy. |
Somewhat related: whatwg/html#7386. What does |
Scroll to |
If that is correct and Chrome only implements the early return for |
What's the advantage of 4 over 2? "A bit weird" doesn't strike me as enough of a negative to introduce novel behavior. |
Yeah, 2 is probably the path of least resistance. Maybe with a console warning, if not a rejected promise, if you pass |
I guess I could see 5 being okay. Perhaps some input from web developers would help sway this one way or another. |
From #72 (comment):
This approach doesn't work with upfront decisions, right? (I.e., the idea that you decide before navigating whether you end up replacing or pushing.) Because the initial URL could be same-origin, but then redirect to the URL you want to attack. |
The decision is made based on the initiator document's origin vs. the target iframe's current document's origin. That works, I think. Cases:
|
@annevk, @smaug----, @natechapin and I discussed this offline. Our main concerns were:
These constraints combine into something based on @jakearchibald's ideas upthread in #111 (comment) . Specifically, we change the
TLDR for web developers wondering how the API will change: instead of I will work on spec/implementation/test updates for this soon! |
In the course of writing the spec (#219) it became clear that current URL is not the only case we care about; initial about:blank, javascript: URLs, and not-completely-loaded also have similar implicitly-convert-push-to-replace behavior. I've specced those to return rejected promises for "push" as well. |
We can then introduce { history: "push" } which explicitly errors if we're in a convert-to-replace situation. Closes #111. This also fixes a preexisting omission where we were not properly doing replace navigations before the document is loaded.
We've received reports from an app adopting the navigation API that same-URL pushes are actually desired. The issue is that they are migrating code from IMO we should just allow same-URL push navigations. As mentioned above we basically just omitted this from the MVP because we thought it was not necessary. It's still not necessary cross-document, but it is necessary same-document, and part of the point of the navigation API is to let you decouple the same-document vs. cross-documentness of your navigation from the initiator. |
Unfortunately, our experience shows the negative effect of pushing the current url to the navigation history. In practice, especially on the mobile version of the web, the client double clicks on a link or button, for various reasons why clients do this, especially a high percentage of such clicks from clients with disabilities. Because of pushing the same urls into the history, when you click on the back button, nothing happens, customers then complain that the native back button does not work, the reason is that a lot of the same urls we are pushed in the history. We are trying to process this programmatically, but I don’t know by chance who needs to push the same urls into the story, it looks like bad design and your original suggestion of the same urls always doing a post replacement instead of a push was correct. If you don't use navigation handlers. |
I have a use case for same-URL pushes. It's a page that's dynamically rendered according to the params that are passed by history state. So the URL is the same, but the state is different. BTW, React Navigation (popular React Native lib) addresses this the following way:
|
I understand your case, but you don’t need clients to stay on the current page when they click on the back button? I want this behavior to be fixed, just bring it in line with the history behavior when there are no history handlers, so that the back button works the same way it works when there are no history handlers. P.S. |
Required behavior for my use case described above: History after entering the first page: History after entering the second page: History after clicking the back button: The URL is the same, but they need to be treated as 2 different pages and I need to re-render whenever the history changes. |
I understand that when you click back, you need the user to remain on the same url, but with a new non-explicit state, you probably know that this will not work if the user passes the link, the non-explicit state is worse than the explicit one, but this is your choice. In my case, there are no hidden states, a compromise solution, if no states are passed to the navigation method and the specified url is equal to the current url, in this case always replace the history instead of pushing, so everyone is happy? |
If I'm navigating to the same URL and passing different values in the state, then I have the intention of pushing a new navigation entry. If the values in the state are the same, then it was a double click. Not sure if there would be some use case where someone would want to push a new entry with the same URL and same state. |
I propose extends navigation.addEventListener('navigate', e => {
//...
e.intercept({
history: navigation.currentEntry.url === e.destination.url ? 'replace' : e.navigationType,
//...
});
}); What do you think? |
The boat has long sailed on whether or not we can allow same-URL pushes - this is something that history.pushState() allows, it's something location allows, I do not think we should change this behavior. But, I think the API makes this pretty easy to configure for users who do NOT want to allow same-URL navigations, for instance for debouncing link clicks. There are lots of ways you can prevent navigations that are the "same" as the previous navigation, but here's one simple way: navigation.addEventListener('navigate', e => {
if (navigation.currentEntry.url === e.destination.url) {
e.preventDefault();
}
}); That is, if the url is the same, just preventDefault the navigation. There are lots of other complicated ways you might do this as well, but that would be app specific about what is and is not a "same" navigation. |
No, try this 3 times after each reload: location.href = location.href; and then 1 click on the back button and you will get a page that, according to your logic, is at step -3. |
Yes, true, I'm just saying you could have the same URL multiple times in history by |
You suggested me another compromise solution ) Now for all browsers, clicking on a link means push transition mode, except if the url is equal to the current one, I suggest if the event is caused by a click on the link and the url is equal to the current one, then perform the transition in replacement mode, this will not break those cases where implicit state is passed, because clicking on a link has no implicit state and this is consistent with the actual behavior of all browsers |
If you want to maintain current browser behavior of doing nothing or doing a reload when the URL is the same, I think that use case is satisfied with the existing API. But if you want to re-do some part of your application logic on same-URL navigations and they should be replace rather than push, I agree that seems trickier. If you want some way |
We have a SPA application, we cannot do nothing when the client clicks on the link that leads to the current url. If we do not call the intercept, our application will restart and all client states will be lost, we must not allow the document to be reloaded. We must execute the intercept, because that, judging by the behavior of users, they click on links (for example, on the site logo) to load new content, so we need to call the intercept (fetch by RestAPI new content), but in the history replacement mode, so that the back button works correctly. |
Isn't this handled pretty simply with code such |
Yes, we use something similar, since the use of the old API history
But in practice, you suggest using the same hacks, instead of solving the problem at the level of a new interface. All I want is that the behavior of browsers without navigation event handlers matches the behavior with the handler, if the browsers click on the link with url equal to the current url, make the transition in replace mode, the same thing should happen in the handler without the need to write extra hacks. But it’s even better to give the ability to specify the type of history in the interceptor, the interface that I proposed will give imperative flexibility to custom implementations of such cases. navigation.addEventListener('navigate', e => {
//...
e.intercept({
history: navigation.currentEntry.url === e.destination.url ? 'replace' : e.navigationType,
//...
});
}); |
I don't think using two methods in a row counts as a hack? I know you really like adding new options to intercept, but that's not generally how intercept() options work; they control the interception, they don't start a new navigation with new properties. |
Yes, this is a hack because these methods are used to get around the current interface limitations.
These two actions do not do what we need, we do not need to cancel events and we do not need to create a new event.
In my opinion this is a hijacking of the default behavior i.e. we intercept the type of history change and replace it with the one we want, it seems logical to me, but if you have a better interface, I will agree. |
Fair point about the negative consequences of combining those two. Perhaps this is something to consider when designing the redirect #124. But it's pretty off-topic for this issue which is about the behavior of |
It discusses #124 the replacement of url, here it discusses the changing of the type of the history, these are different problems, but solutions to both in the new optional properties for the intercept method ) |
I think they are more closely related, since they are both about changing an in-progress navigation. And as I stated above, that's not really what the intercept() options are for... |
But do you basically agree that there should be an interface to change the current history type in the navigation handler or not? |
Yep! I just wish it wasn't in this issue, but instead was in a dedicated issue :) |
This was fixed in whatwg/html@0e2536f. |
@domenic Thanks, do I need to create a bug report in Chrome, on the implementation of new spec changes? |
Nope, it's implemented in Chromium 114+. |
@jakearchibald, @annevk, @smaug---- and I discussed the behavior of
navigation.navigate(appHistory.current.url)
today.The current spec says that this always does a replace navigation, similar to
location.href = location.href
. This is a bit weird because it means that thereplace
parameter is ignored, e.g.We discussed a few options for what to do:
Do a same-document navigation, either push or replace according to the
replace
option.pushState(null, "")
/replaceState(null, "")
navigate()
to be about new-document navigations; to make them same-document, you intercept with thenavigate
event.Do a new-document replace navigation, ignoring the
replace
optionlocation.href = location.href
, orlocation.assign(location.href)
.Do a new-document navigation, either push or replace according to the
replace
option.(3), but have the default be a somewhat-magic "replace for same-URL, push for different-URL". I.e.
Return a rejected promise whenever you pass
replace: false
and try to navigate to the current URL.Return a rejected promise whenever you pass anything besides
replace: true
and try to navigate to the current URL. I.e. you need to be super-explicit.We were leaning toward (4), although the lack of good precedent is a bit tricky.
The text was updated successfully, but these errors were encountered: