fix: self link does not trigger page reload (#8182)
This commit is contained in:
parent
f224c539c8
commit
cfc465ddeb
4 changed files with 68 additions and 22 deletions
5
.changeset/gold-carpets-film.md
Normal file
5
.changeset/gold-carpets-film.md
Normal file
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
'astro': patch
|
||||
---
|
||||
|
||||
View Transitions: self link (`href=""`) does not trigger page reload
|
|
@ -274,30 +274,54 @@ const { fallback = 'animate' } = Astro.props as Props;
|
|||
// that is going to another page within the same origin. Basically it determines
|
||||
// same-origin navigation, but omits special key combos for new tabs, etc.
|
||||
if (
|
||||
link &&
|
||||
link instanceof HTMLAnchorElement &&
|
||||
link.href &&
|
||||
(!link.target || link.target === '_self') &&
|
||||
link.origin === location.origin &&
|
||||
!(
|
||||
// Same page means same path and same query params
|
||||
(location.pathname === link.pathname && location.search === link.search)
|
||||
) &&
|
||||
ev.button === 0 && // left clicks only
|
||||
!ev.metaKey && // new tab (mac)
|
||||
!ev.ctrlKey && // new tab (windows)
|
||||
!ev.altKey && // download
|
||||
!ev.shiftKey &&
|
||||
!ev.defaultPrevented &&
|
||||
transitionEnabledOnThisPage()
|
||||
) {
|
||||
ev.preventDefault();
|
||||
navigate('forward', link.href, { index: ++currentHistoryIndex, scrollY: 0 });
|
||||
const newState: State = { index: currentHistoryIndex, scrollY };
|
||||
persistState({ index: currentHistoryIndex - 1, scrollY });
|
||||
history.pushState(newState, '', link.href);
|
||||
!link ||
|
||||
!(link instanceof HTMLAnchorElement) ||
|
||||
!link.href ||
|
||||
(link.target && link.target !== '_self') ||
|
||||
link.origin !== location.origin ||
|
||||
ev.button !== 0 || // left clicks only
|
||||
ev.metaKey || // new tab (mac)
|
||||
ev.ctrlKey || // new tab (windows)
|
||||
ev.altKey || // download
|
||||
ev.shiftKey || // new window
|
||||
ev.defaultPrevented ||
|
||||
!transitionEnabledOnThisPage()
|
||||
)
|
||||
// No page transitions in these cases,
|
||||
// Let the browser standard action handle this
|
||||
return;
|
||||
|
||||
// We do not need to handle same page links because there are no page transitions
|
||||
// Same page means same path and same query params (but different hash)
|
||||
if (location.pathname === link.pathname && location.search === link.search) {
|
||||
if (link.hash) {
|
||||
// The browser default action will handle navigations with hash fragments
|
||||
return;
|
||||
} else {
|
||||
// Special case: self link without hash
|
||||
// If handed to the browser it will reload the page
|
||||
// But we want to handle it like any other same page navigation
|
||||
// So we scroll to the top of the page but do not start page transitions
|
||||
ev.preventDefault();
|
||||
persistState({ ...history.state, scrollY });
|
||||
scrollTo({ left: 0, top: 0, behavior: 'instant' });
|
||||
if (location.hash) {
|
||||
// last target was different
|
||||
const newState: State = { index: ++currentHistoryIndex, scrollY: 0 };
|
||||
history.pushState(newState, '', link.href);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// these are the cases we will handle: same origin, different page
|
||||
ev.preventDefault();
|
||||
navigate('forward', link.href, { index: ++currentHistoryIndex, scrollY: 0 });
|
||||
const newState: State = { index: currentHistoryIndex, scrollY };
|
||||
persistState({ index: currentHistoryIndex - 1, scrollY });
|
||||
history.pushState(newState, '', link.href);
|
||||
});
|
||||
|
||||
addEventListener('popstate', (ev) => {
|
||||
if (!transitionEnabledOnThisPage()) {
|
||||
// The current page doesn't haven't View Transitions,
|
||||
|
|
|
@ -7,6 +7,7 @@ import Layout from '../components/Layout.astro';
|
|||
<a id="click-two" href="/two">go to 2</a>
|
||||
<a id="click-three" href="/three">go to 3</a>
|
||||
<a id="click-longpage" href="/long-page">go to long page</a>
|
||||
<a id="click-self" href="">go to top</a>
|
||||
|
||||
<div id="test">test content</div>
|
||||
</Layout>
|
||||
|
|
|
@ -190,6 +190,22 @@ test.describe('View Transitions', () => {
|
|||
await expect(p, 'should have content').toHaveText('Page 1');
|
||||
});
|
||||
|
||||
test('click self link (w/o hash) does not do navigation', async ({ page, astro }) => {
|
||||
const loads = [];
|
||||
page.addListener('load', (p) => {
|
||||
loads.push(p.title());
|
||||
});
|
||||
// Go to page 1
|
||||
await page.goto(astro.resolveUrl('/one'));
|
||||
const p = page.locator('#one');
|
||||
await expect(p, 'should have content').toHaveText('Page 1');
|
||||
|
||||
// Clicking href="" stays on page
|
||||
await page.click('#click-self');
|
||||
await expect(p, 'should have content').toHaveText('Page 1');
|
||||
expect(loads.length, 'There should only be 1 page load').toEqual(1);
|
||||
});
|
||||
|
||||
test('Scroll position restored on back button', async ({ page, astro }) => {
|
||||
// Go to page 1
|
||||
await page.goto(astro.resolveUrl('/long-page'));
|
||||
|
|
Loading…
Reference in a new issue