Quantcast
Channel: Active questions tagged svelte - Stack Overflow
Viewing all articles
Browse latest Browse all 1806

Offcanvas as a hash route using SvelteKit 2.16.1 and Svelte 5.19.1

$
0
0

I want to support opening an offcanvas like this <a href="#cart">Cart</a> from any page. When the offcanvas is open, I want to be able to use the Back button to close it and be at the same scroll position where I was before opening it.

This is my base Offcanvas.svelte that wraps the cart content in my /src/routes/+layout.svelte (with an id of cart)...

<script lang="ts">    import { onMount } from 'svelte'    import type { Snippet } from 'svelte'    import { fade, fly } from 'svelte/transition'    interface Props {        id: string        title: string        onshown?: () => void        children?: Snippet    }    let { id, title, onshown, children }: Props = $props()    const titleId = `${id}Title`    let isVisible = $state(false)    onMount(() => {        if (window.location.hash === `#${id}`) {            open()        }    })    export const open = () => {        isVisible = true        document.body.style.overflow = 'hidden' // Prevent scrolling        if (onshown) onshown()    }    export const close = () => {        isVisible = false        document.body.style.overflow = '' // Restore scrolling        history.back() // clears hash invoked to open modal preserving scroll position    }    // Handle escape key to close the offcanvas    const onkeydown = (event: KeyboardEvent) => {        if (isVisible && window.location.hash === `#${id}` && event.key === 'Escape') {            close()        }    }    // Handle hash change to open/close the offcanvas    const onhashchange = () => {        // If hash changes to #id, open (e.g., user navigates forward or direct link)        if (!isVisible && window.location.hash === `#${id}`) {            open()        }        // If it's visible and the hash changes away from #id, close (e.g., back button)        else if (isVisible && window.location.hash !== `#${id}`) {            isVisible = false            document.body.style.overflow = ''        }    }    const onbackdropclick = (event: MouseEvent) => {        event.stopPropagation()        close()    }</script><svelte:window {onhashchange} {onkeydown} />{#if isVisible}<div        onclick={onbackdropclick}        transition:fade={{ duration: 150 }}        class="tw:fixed tw:inset-0 tw:bg-black/50 tw:z-1040"        role="presentation"></div><div        {id}        class="tw:translate-x-0 tw:z-1045 tw:fixed tw:top-0 tw:bottom-0 tw:right-0 tw:w-100 tw:max-w-full tw:bg-white tw:border-l tw:border-gray-200 tw:shadow-lg"        transition:fly={{ x: 400, duration: 300 }}        tabindex="-1"        aria-labelledby={titleId}        aria-hidden={!isVisible}        aria-modal={isVisible}        role="dialog"><div class="tw:flex tw:items-center tw:justify-between tw:p-4"><h2 id={titleId} class="tw:!mb-0 tw:!text-xl tw:font-semibold">{title}</h2><button                onclick={close}                type="button"                class="tw:p-0 tw:border-0 tw:!mb-1 tw:!bg-transparent tw:text-plum tw:hover:text-amber-500"                aria-label="Close"><svg height="24" width="24"><title>Close offcanvas</title><use href="/images/icons/icons.svg#x" /></svg></button></div><div class="tw:p-4">            {@render children?.()}</div></div>{/if}

It works but stepping back, I'm wondering if this is fundamentally the right approach or whether there's a way to "register" hash routes like #cart, #contact, #newsletter, etc. (without all the folders since these load on top of existing pages).


Viewing all articles
Browse latest Browse all 1806

Trending Articles



<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>