Skip to content

Conversation

@trueadm
Copy link
Contributor

@trueadm trueadm commented May 25, 2023

Fixes #8725.

This PR fixes an outstanding issue with routing hash links in Firefox. Specifically, clicking a link that contains as hash and where the link is the same as the current page URL, Firefox clears the history state, causing browser navigation to fail thereafter. In order to tackle this, we need to detect where the URL for a hash link is the same and manually prevent any client-side routing to take place. Instead we just apply the same scrolling heuristics – fixing the issue in FF.

Please don't delete this checklist! Before submitting the PR, please make sure you do the following:

  • It's really useful if your PR references an issue where it is discussed ahead of time. In many cases, features are absent for a reason. For large changes, please create an RFC: https://github.com/sveltejs/rfcs
  • This message body should clearly illustrate what problems it solves.
  • Ideally, include a test that fails without this PR but passes with it.

Tests

  • Run the tests with pnpm test and lint the project with pnpm lint and pnpm check

Changesets

  • If your PR makes a change that should be noted in one or more packages' changelogs, generate a changeset by running pnpm changeset and following the prompts. Changesets that add features should be minor and those that fix bugs should be patch. Please prefix changeset messages with feat:, fix:, or chore:.

@changeset-bot
Copy link

changeset-bot bot commented May 25, 2023

🦋 Changeset detected

Latest commit: ba4354d

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
@sveltejs/kit Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@trueadm trueadm force-pushed the fix-ff-hash-bug branch from afe7316 to bd8cafc Compare May 25, 2023 09:55
@trueadm trueadm changed the title Fix hash link navigation issues with FF fix: hash link navigation issues with FF May 25, 2023
@trueadm trueadm changed the title fix: hash link navigation issues with FF fix: like prevent history change when clicking same hash link May 25, 2023
@trueadm trueadm changed the title fix: like prevent history change when clicking same hash link fix: prevent history change when clicking same hash link May 25, 2023
@Rich-Harris Rich-Harris merged commit 4aa976e into sveltejs:master May 25, 2023
@github-actions github-actions bot mentioned this pull request May 25, 2023
@flayks
Copy link

flayks commented May 26, 2023

@trueadm @dummdidumm I'm using #contact and #cart hashes to open a popin over my site (a contact form and a shopping cart), but since this fix I can only open and close them once. In order to open one, I now have to close one to be able to open the other. Also, I now cannot open the cart/contact popin again after closing it, for instance.

Using hashes allows me to be able to share an URL and still open the popin needed from the hash present in the URL.

Ideally I'd need the new Shallow routing feature but it's still in WIP. Is there any workaround for this issue until then?

Here is how I implement it:

// +layout.svelte - detect hashchange to change my stores that open each popin
const handleHashChange = () => {
    $showContact = window.location.hash === '#contact'
    $cartOpen = window.location.hash === '#cart'
}


// Header.svelte - close function when clicking on a button
const handleButtonClick = (event: MouseEvent, name: string) => {
    // Contact/Cart: Close popin
    if (
        (name === 'contact' && $showContact) ||
        (name === 'cart' && $cartOpen)
    ) {
        closePopin()
        event.preventDefault()
    }
}

<Button
    size="small"
    href="#cart"
    label={$cartAmount > 0 ? String($cartAmount) : null}
    on:click={event => handleButtonClick(event, 'cart')}
/>


// utils/functions.ts - closing the popin
export const closePopin = () => {
    // Remove hash from url
    history.pushState({}, '', window.location.href.split('#')[0])

    // Trigger
    window.dispatchEvent(new HashChangeEvent('hashchange'))
}

@trueadm
Copy link
Contributor Author

trueadm commented May 26, 2023

@flayks What happens if you remove the event.preventDefault() from your code above?

@flayks
Copy link

flayks commented May 26, 2023

@trueadm unfortunately nothing… 😅 I've tried that before thinking that it would block the click situation

@trueadm
Copy link
Contributor Author

trueadm commented May 26, 2023

You shouldn't need to do window.dispatchEvent(new HashChangeEvent('hashchange')) if you don't prevent default.

@flayks
Copy link

flayks commented May 26, 2023

I think history.pushState({}, '', window.location.href.split('#')[0]) is not triggering a hashchange event if I'm correct, and I'd like to avoid an empty hash (/page#) which would jump to the top of the page

@trueadm
Copy link
Contributor Author

trueadm commented May 26, 2023

If I'm not mistaken, your logic was working before because of a bug in the routing logic. I think the best approach you'd want to take, is to capture the scroll position and then re-instate it after the route change. I'm very new to SvelteKit, but maybe there's some API exposed that you can use that allows for a route change that keeps scroll position the same without needing to pushState manually. If not, then you can try doing that yourself?

@trueadm trueadm deleted the fix-ff-hash-bug branch May 26, 2023 09:30
@flayks
Copy link

flayks commented May 26, 2023

I feel that just using window.location.hash = '' in the closePopin function and no event.preventDefault() anywhere, this fix really prevents to open the same hash after having it triggered once.

@flayks
Copy link

flayks commented May 26, 2023

I did a little test here for the contact hash:

if (name === 'contact') {
    if ($showContact) {
        closePopin()
    } else {
        window.location.hash = 'contact'
    }
}

When triggered manually like so, it allows to "force" the hash to change. It looks a bit counterproductive regarding the hash link of my button but that's the only workaround I found so far 🤷

This is fine on my Header, although I have these buttons in other places and need to implement the same workaround everywhere which I really would like to avoid.

@boczpeter
Copy link

This patch introduces a killer bug in my app.
I use #hash navigation to open dialog content, which can be closed by a button (executing history.back()) or the browser's back button. Now the user cannot open the same dialog, because you disabled client navigation and the link becomes dead. This shouldn't be standard procedure.
What is the recommended workaround for this?
I need to pin the app version to 1.18.0 until it is resolved.

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.

Clicking hash link twice on Firefox breaks back navigation

7 participants