summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.changeset/sharp-seas-smile.md15
-rw-r--r--packages/astro/components/ViewTransitions.astro21
-rw-r--r--packages/astro/e2e/fixtures/view-transitions/src/pages/two.astro6
-rw-r--r--packages/astro/e2e/view-transitions.test.js23
4 files changed, 61 insertions, 4 deletions
diff --git a/.changeset/sharp-seas-smile.md b/.changeset/sharp-seas-smile.md
new file mode 100644
index 000000000..1bc478e80
--- /dev/null
+++ b/.changeset/sharp-seas-smile.md
@@ -0,0 +1,15 @@
+---
+'astro': patch
+---
+
+Execute scripts when navigating to a new page.
+
+When navigating to an new page with client-side navigation, scripts are executed (and re-executed) so that any new scripts on the incoming page are run and the DOM can be updated.
+
+However, `type=module` scripts never re-execute in Astro, and will not do so in client-side routing. To support cases where you want to modify the DOM, a new `astro:load` event listener been added:
+
+```js
+document.addEventListener('astro:load', () => {
+ updateTheDOMSomehow();
+});
+```
diff --git a/packages/astro/components/ViewTransitions.astro b/packages/astro/components/ViewTransitions.astro
index d7703bc82..c2f9a7206 100644
--- a/packages/astro/components/ViewTransitions.astro
+++ b/packages/astro/components/ViewTransitions.astro
@@ -25,6 +25,7 @@ const { fallback = 'animate' } = Astro.props as Props;
const supportsViewTransitions = !!document.startViewTransition;
const transitionEnabledOnThisPage = () =>
!!document.querySelector('[name="astro-view-transitions-enabled"]');
+ const onload = () => document.dispatchEvent(new Event('astro:load'));
async function getHTML(href: string) {
const res = await fetch(href);
@@ -40,6 +41,23 @@ const { fallback = 'animate' } = Astro.props as Props;
return 'animate';
}
+ function runScripts() {
+ let wait = Promise.resolve();
+ for(const script of Array.from(document.scripts)) {
+ const s = document.createElement('script');
+ s.innerHTML = script.innerHTML;
+ for(const attr of script.attributes) {
+ if(attr.name === 'src') {
+ const p = new Promise(r => {s.onload = r});
+ wait = wait.then(() => p as any);
+ }
+ s.setAttribute(attr.name, attr.value);
+ }
+ script.replaceWith(s);
+ }
+ return wait;
+ }
+
const parser = new DOMParser();
async function updateDOM(dir: Direction, html: string, fallback?: Fallback) {
@@ -95,6 +113,8 @@ const { fallback = 'animate' } = Astro.props as Props;
await finished;
} finally {
document.documentElement.removeAttribute('data-astro-transition');
+ await runScripts();
+ onload();
}
}
@@ -171,5 +191,6 @@ const { fallback = 'animate' } = Astro.props as Props;
{ passive: true, capture: true }
);
});
+ addEventListener('load', onload)
}
</script>
diff --git a/packages/astro/e2e/fixtures/view-transitions/src/pages/two.astro b/packages/astro/e2e/fixtures/view-transitions/src/pages/two.astro
index ec5b699d8..84b16f259 100644
--- a/packages/astro/e2e/fixtures/view-transitions/src/pages/two.astro
+++ b/packages/astro/e2e/fixtures/view-transitions/src/pages/two.astro
@@ -3,4 +3,10 @@ import Layout from '../components/Layout.astro';
---
<Layout link="/two.css">
<p id="two">Page 2</p>
+ <article id="twoarticle"></article>
</Layout>
+<script>
+ document.addEventListener('astro:load', () => {
+ document.getElementById('twoarticle')!.textContent = 'works';
+ }, { once: true });
+</script>
diff --git a/packages/astro/e2e/view-transitions.test.js b/packages/astro/e2e/view-transitions.test.js
index da5252709..d799b5ef3 100644
--- a/packages/astro/e2e/view-transitions.test.js
+++ b/packages/astro/e2e/view-transitions.test.js
@@ -128,10 +128,6 @@ test.describe('View Transitions', () => {
});
test('Stylesheets in the head are waited on', async ({ page, astro }) => {
- page.addListener('console', (data) => {
- console.log(data);
- });
-
// Go to page 1
await page.goto(astro.resolveUrl('/one'));
let p = page.locator('#one');
@@ -143,4 +139,23 @@ test.describe('View Transitions', () => {
await expect(p, 'should have content').toHaveText('Page 2');
await expect(p, 'imported CSS updated').toHaveCSS('font-size', '24px');
});
+
+ test('astro:load event fires when navigating to new page', 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');
+ });
+
+ test('astro:load event fires when navigating directly to a page', async ({ page, astro }) => {
+ // Go to page 1
+ await page.goto(astro.resolveUrl('/two'));
+ const article = page.locator('#twoarticle');
+ await expect(article, 'should have script content').toHaveText('works');
+ });
});