'Vue 3/Vue Router - Unplanned firing of onClick event listener

Working with Vue 3 and Vue Router. Event listeners work as planned on initial 'click' navigations, to and from Details.vue page. However, upon successive 'click' attempts, hideDetails() is immediately called without a perceived 'click' event.


Home.vue

<template>
    <main class="rounded">
        <item-one />
        <item-two />
    </main>
    <aside class="details__modal">
        <router-view></router-view>
    </aside>
</template>

ItemOne.vue, ItemTwo.vue

<template>
    <button @click="showDetails(id)">Item Name</button>
</template>

<script lang="ts">
import {showDetails} from '@/scripts/utils'
</script>

Vue Router

Details.vue is set up as a child of Home.vue. addEventListener() is called via global navigation guard.

...
const routes: Array<RouteRecordRaw> = [
    {
        path: '/',
        name: 'home',
        component: Home,
        children: [
            {
                path: 'details',
                name: 'details',
                component: () =>
                    import(
                        /* webpackChunkName: "details" */ '../views/Details.vue'
                    ),
                props: route => route.params,
            },
        ],
    },
]

const router = createRouter({
    history: createWebHistory(),
    routes
})

router.afterEach(to => {
    if (to.name === 'details')
        document.addEventListener('click', handle_hideDetails)
})

Details.vue

On showDetails(), Details.vue is rendered as a child of Home.vue and displayed within a modal. If props.id does not exists, hideDetails() redirects to Home.vue. Before leaving page, removeEventListener() is called via the component navigation guard onBeforeRouteLeave().

<template>
    <div class="details__wrapper">...</div
</template>

<script lang="ts">
...
export default defineComponent({
    props: {
        id: {
            type: String
        }
    },
    setup(props){
        if(!props.id) hideDetails()

        onBeforeRouteLeave(_ => document.removeEventListener('click', handle_hideDetails))
    }
})
</script>

utils.ts

All utility functions are here.

import vm from '@/main'

export const handle_hideDetails = (e: Event): void => {
    const el = <HTMLElement>document.querySelector('div.details__wrapper')

    if (!e.composedPath().includes(el)) hideDetails()
}

export const showDetails = (id: string): void => {
    document.querySelector('aside.details__modal')?.classList.add('show')

    vm.$router.push({
        name: 'details',
        params: { id },
    })
}

export const hideDetails = (): void => {
    document.querySelector('aside.details__modal')?.classList.remove('show')
    setTimeout(() => vm.$router.push({ name: 'home' }), 2000)
}


Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source