Prevent script re-execution on page evaluation (#8033)
This commit is contained in:
parent
87d4b18437
commit
405913cdf2
4 changed files with 53 additions and 0 deletions
5
.changeset/lovely-impalas-pretend.md
Normal file
5
.changeset/lovely-impalas-pretend.md
Normal file
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
'astro': patch
|
||||
---
|
||||
|
||||
Prevent script re-evaluation on page transition
|
|
@ -63,9 +63,16 @@ const { fallback = 'animate' } = Astro.props as Props;
|
|||
return 'animate';
|
||||
}
|
||||
|
||||
function markScriptsExec() {
|
||||
for (const script of document.scripts) {
|
||||
script.dataset.astroExec = '';
|
||||
}
|
||||
}
|
||||
|
||||
function runScripts() {
|
||||
let wait = Promise.resolve();
|
||||
for (const script of Array.from(document.scripts)) {
|
||||
if(script.dataset.astroExec === '') continue;
|
||||
const s = document.createElement('script');
|
||||
s.innerHTML = script.innerHTML;
|
||||
for (const attr of script.attributes) {
|
||||
|
@ -77,6 +84,7 @@ const { fallback = 'animate' } = Astro.props as Props;
|
|||
}
|
||||
s.setAttribute(attr.name, attr.value);
|
||||
}
|
||||
s.dataset.astroExec = '';
|
||||
script.replaceWith(s);
|
||||
}
|
||||
return wait;
|
||||
|
@ -100,6 +108,18 @@ const { fallback = 'animate' } = Astro.props as Props;
|
|||
const href = el.getAttribute('href');
|
||||
return doc.head.querySelector(`link[rel=stylesheet][href="${href}"]`);
|
||||
}
|
||||
if(el.tagName === 'SCRIPT') {
|
||||
let s1 = el as HTMLScriptElement;
|
||||
for(const s2 of doc.scripts) {
|
||||
if(
|
||||
// Inline
|
||||
(s1.textContent && s1.textContent === s2.textContent) ||
|
||||
// External
|
||||
(s1.type === s2.type && s1.src === s2.src)) {
|
||||
return s2;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
|
@ -207,6 +227,7 @@ const { fallback = 'animate' } = Astro.props as Props;
|
|||
} finally {
|
||||
document.documentElement.removeAttribute('data-astro-transition');
|
||||
await runScripts();
|
||||
markScriptsExec();
|
||||
onload();
|
||||
}
|
||||
}
|
||||
|
@ -225,6 +246,8 @@ const { fallback = 'animate' } = Astro.props as Props;
|
|||
}
|
||||
|
||||
if (supportsViewTransitions || getFallback() !== 'none') {
|
||||
markScriptsExec();
|
||||
|
||||
document.addEventListener('click', (ev) => {
|
||||
let link = ev.target;
|
||||
if (link instanceof Element && link.tagName !== 'A') {
|
||||
|
|
|
@ -20,6 +20,16 @@ const { link } = Astro.props as Props;
|
|||
</style>
|
||||
<ViewTransitions />
|
||||
<DarkMode />
|
||||
<meta name="script-executions" content="0">
|
||||
<script is:inline defer>
|
||||
{
|
||||
// Increment a global to see if this is running more than once
|
||||
globalThis.scriptExecutions = globalThis.scriptExecutions == null ? -1 : globalThis.scriptExecutions;
|
||||
globalThis.scriptExecutions++;
|
||||
const el = document.querySelector('[name="script-executions"]');
|
||||
el.setAttribute('content', globalThis.scriptExecutions);
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<header transition:animate="morph">
|
||||
|
|
|
@ -279,4 +279,19 @@ test.describe('View Transitions', () => {
|
|||
// Count should remain
|
||||
await expect(cnt).toHaveText('6');
|
||||
});
|
||||
|
||||
test('Scripts are only executed once', async ({ page, astro }) => {
|
||||
// Go to page 1
|
||||
await page.goto(astro.resolveUrl('/one'));
|
||||
const p = page.locator('#one');
|
||||
await expect(p, 'should have content').toHaveText('Page 1');
|
||||
|
||||
// go to page 2
|
||||
await page.click('#click-two');
|
||||
const article = page.locator('#twoarticle');
|
||||
await expect(article, 'should have script content').toHaveText('works');
|
||||
|
||||
const meta = page.locator('[name="script-executions"]');
|
||||
await expect(meta).toHaveAttribute('content', '0');
|
||||
});
|
||||
});
|
||||
|
|
Loading…
Reference in a new issue