#Script execution with <ClientRouter />

7 messages · Page 1 of 1 (latest)

dire heron
#

Hi, i have <ClientRouter /> on my Layout, so I understand that the scripts are only executed on the first render, that's why I use astro:page-load for example, then, why is this animation executed always if it's outside the astro:page-load event listener?

<script>
    import { animate, stagger } from "motion";

    animate(
        ".tripadvisor-image",
        {
            scale: [1, 1.05, 1],
        },
        {
            duration: 0.8,
            repeat: Infinity,
            repeatDelay: 3,
            ease: "easeInOut",
            delay: stagger(0.2),
        },
    );

    document.addEventListener("astro:page-load", () => {
        const openModalButton = document.getElementById("open-tripadvisor-modal-button") as HTMLButtonElement | null;

        if (openModalButton) {
            openModalButton.addEventListener("click", () => {
                window.dispatchEvent(new CustomEvent("open-tripadvisor-modal"));
            });
        }
    });
</script>
cunning stratusBOT
#

Hi @dire heron. I'm kapa.ai, your friendly support bot. I will try to answer your question by searching through the knowledge base. :wave:

#

Great question! The behavior you're observing is actually expected, and here's why:

Bundled module scripts run once — but that's enough

Your <script> tag is a bundled module script (the default in Astro). As documented, these scripts are only ever executed once — on the first page load. After that, they are ignored even if the script exists on a new page after a transition. [Script re-execution]

Why the animation still works

The key is that the animate(...) call sets up a continuously repeating animation (repeat: Infinity). Once it runs on the first page load, it keeps running indefinitely — it doesn't need to be re-executed on subsequent navigations because it was never "stopped". The animation is a persistent, ongoing process initiated once.

This is different from something like an event listener, which gets lost when the DOM is replaced during navigation. That's why your openModalButton click listener correctly lives inside astro:page-load — the button element is part of the DOM that gets swapped out on each navigation, so the listener needs to be re-attached every time.

Summary

Code Needs astro:page-load? Why
animate(...) with repeat: Infinity ❌ No Runs once, persists indefinitely
addEventListener on a DOM element ✅ Yes DOM is replaced on each navigation

So your code is actually structured correctly. The animation runs once and loops forever, while the event listener is properly re-registered on every page load via astro:page-load. [astro:page-load]

Note: you can ask me a follow up question by @ mentioning me again :speech_balloon:

#

@dire heron Kapa.ai is still learning and improving, please let me know how I did by reacting below.

dire heron
#

@cunning stratus Okey!, I understand that all elements of the DOM are replaced on each navigation, so how can the animation (persistent) find .tripadvisor-image if the element is replaced on each navigation?

cunning stratusBOT
#

Let me check the knowledge base for you again. One moment! :books:

#

This is a great follow-up question, but I have to be honest: the knowledge sources provided don't contain enough information to fully explain this specific behavior — i.e., how a motion animation with a DOM selector continues to work after the DOM is replaced during view transitions.

What the knowledge sources do confirm is:

However, the specifics of how the motion library internally handles its animation targets (e.g., whether it holds a reference to the original DOM nodes, or re-queries the selector on each frame) are outside the scope of Astro's documentation and depend on the motion library's own implementation.

My honest assessment: It's possible that:

  1. The animation holds a direct reference to the original DOM node(s) captured at the time animate() was called, and continues animating those — even if they're no longer in the live DOM (meaning the animation may actually not be working on the new page's elements, just the old cached references).
  2. Or the page you're navigating to is the same page, so the elements happen to match again.

I'd recommend verifying in your browser's DevTools whether the animated elements are truly the new DOM nodes or the old ones. For a definitive answer on this behavior, the motion library's documentation would be the right place to look.

Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: