I am currently facing an issue where the <keep-alive> stops working after adding a :key to the parent <div>. This <div> and also the :key is needed, otherwise the <transition> won't work. Anyone got a solution for that?
Sorry i am not able to provide more code.
<template>
<router-view v-slot="{ Component, route }">
<transition :name="transitionName">
<div :key="route.name">
<keep-alive include="SpecialComponent">
<component :is="Component" />
</keep-alive>
</div>
</transition>
</router-view>
</template>
It looks like even putting you component in a div alone prevents transitions.
My test app components inside a div didn't do any transitions even if I don't use <keep-alive> and :key.
Why do you need to wrap it in a div?
Shouldn't it work so?
<transition :name="transitionName">
<keep-alive include="SpecialComponent">
<component :is="Component" :key="route.name" />
</keep-alive>
</transition>
I'm having a hard time getting a vue transition to work inside a slot. I should mention I've searched through numerous stackoverflow questions and I haven't seen one that matches my issue. All the ones I've seen so far are talking about <slot> inside the <transition>, eg: vue transition component doesn't work correctly, whereas for me it's the other way round.
There are three vue components, component1, component2 and component3.
component1 has something like:
<template>
<slot name="content">
<slot name="side">
<slot name="default">
</template>
component2 looks something like below:
<component1>
<template #content>Some stuff here</template>
<template #side>Some stuff here</template>
<template #default>
<component3/> #Component3 contains the transition stuff
</template>
</component1>
component3 contains the transition stuff:
<transition
:name="right ? 'slide-right': 'slide-left'"
#before-enter="$emit('before-enter')"
#after-leave="$emit('after-leave')"
>
<div
v-show="$attrs.value"
v-bind="$attrs"
v-on="listeners"
class="u-drawer bg-white"
:class="extraClasses"
:style="getStyles">
<slot v-if="!hideClose" name="close">
<u-button #click="$emit('input', false)"
class="u-drawer-close-button" icon>
<u-icon icon="x" size="1rem" class="mx-auto" />
</u-button>
</slot>
<slot name="default" />
</div>
</transition>
<transition name="fade">
<div v-if="overlay" #click.stop="closeDrawer" v-show="$attrs.value" style="width:100%; height:100%" class="u-overlay"></div>
</transition>
For whatever reason the transition does not work, it's a drawer that is supposed to slide out but it doesn't, instead it just appears instantaneously.
I should mention that, component3 has been used elsewhere in the project where the sliding effect does occur. The only difference here is that it's within a <slot name=default>. Does anyone know what the problem might be?
App.vue has a transition tag to fade the pages out and in.
<router-view v-slot="{ Component }">
<transition name="fade" mode="out-in" appear>
<component :is="Component"></component>
</transition>
</router-view>
The Page.vue file has a simple structure, however, it also has a basic sliderjs component which throws the error <Transition> renders non-element root node that cannot be animated. If the transition tag is removed, everything works fine.
<div v-if="page.isReady">
<swiper>
<swiper-slide>Slide 1</swiper-slide>
<swiper-slide>Slide 2</swiper-slide>
<swiper-slide>Slide 3</swiper-slide>
</swiper>
</div>
https://swiperjs.com/vue/
The file also has the following:
import { Swiper, SwiperSlide } from 'swiper/vue';
import 'swiper/swiper.scss';
export default {
components: {
Swiper,
SwiperSlide,
},
setup () {
return {
page: usePage()
}
}
}
Is there any trick to fix the error? Thanks for any tips!
No.
<template>
<div></div>
<div>~someone~</div>
</template>
Yes.
<template>
<div>
<div></div>
~someone~
</div>
</template>
If you do not use a "div" tag just inside the "Template" tag, you will get the same error. (By the way, it was possible to use other than div tags)
Transitions require single children nodes. Therefore you can wrap the <component> tag inside a <div>, however, a plain <div> inside a <transition> won't trigger the transition, but changing the key attribute does.
We can obtain a unique key by getting the route name:
<router-view v-slot="{ Component, route }">
<transition name="fade" mode="out-in">
<div :key="route.name">
<component :is="Component"></component>
</div>
</transition>
</router-view>
This will effectively transition between routes with a different name, but if you also want to transition between routes of the same name with different parameters, you can use route.fullPath instead of route.name as the key.
I can't fully take credit for this one...but I was having a similar issue and the problem was I had multiple nodes in my view, and found this guy's post on the Vue.js forums:
Found my mistake too. Transition required a single root in components! Since Vue 3 no longer requires a single root node for components I thought this also applies to transitions.
But it’s also logical. CSS requires a single root to which the transitions can refer.
When toggling between elements that have the same tag name, you must tell Vue that they are distinct elements by giving them unique key attributes. Otherwise, Vue’s compiler will only replace the content of the element for efficiency. Even when technically unnecessary though, it’s considered good practice to always key multiple items within a component.
<div>
<router-link to="/"></router-link>
<router-link to="/about"></router-link>
</div>
<router-view v-slot="{ Component, route }">
<transition name="route" :key="route" mode="out-in">
<component :is="Component" />
</transition>
</router-view>
I solved it, it was a minor mistake, there was a character outside a html tag, directly after the tag (comma).
<template>,
<div>
<div>
<swiper>
<swiper-slide>Slide 1</swiper-slide>
<swiper-slide>Slide 2</swiper-slide>
<swiper-slide>Slide 3</swiper-slide>
</swiper>
</div>
</div>
</template>
your dynamic component instance must have a root element.
in you example,'Swiper' and 'SwiperSlide' must have a root element!
don't use RouterView in component parameter of the router. if you need to do that put it inside a root element
<router-view v-slot="{ Component }">
<transition name="fade" mode="out-in" appear>
<!-- root element -->
<div>
<component :is="Component"></component>
</div>
</transition>
</router-view>
A solution that worked for me using Nuxt3:
Parent page was:
<template>
<NuxtPage />
</template>
but should be also wrapped into a root node:
<template>
<div>
<NuxtPage />
</div>
</template>
Otherwise I got a transition exception:
Component inside <Transition> renders non-element root node that cannot be animated
I'm doing like this and it works.
<template>
<router-view v-slot="slotProps">
<transition name="route" mode="out-in">
<component :is="slotProps.Component"></component>
</transition>
</router-view>
</template>
Here is code example:
https://jsfiddle.net/9jypnkue/
The transition v-on:enter="enter" for the nested view is not called when also the main router-view is transitioning.
<router-view ref="nestedView" v-slot="{ Component }">
<transition v-on:enter="enter">
<component :is="Component"/>
</transition>
</router-view>
I would like both animations being called simultaneously.
Using Vue 3 and Vue Router 4 release candidate/beta.
Adding 'appear' to the router fixes it:
<router-view ref="nestedView" v-slot="{ Component }" appear>
<transition v-on:enter="enter">
<component :is="Component"/>
</transition>
</router-view>
https://jsfiddle.net/27qae5zj/1/
Could someone explain me, why the following code doesn't work?
<template>
<div class="modal">
<transition name="slide-in">
<div #click.stop class="modal__container">
<div #click="close" class="modal__button">
<span class="modal__button--close">Close</span>
</div>
</div>
</transition>
<transition name="fade-in">
<div #click="close" class="modal__overlay"/>
</transition>
</div>
</template>
I'm trying to create modal with two different animations (slide-in for text area and fade-in for modal overlay).
If i delete the element with class modal and edit code to the following everything works fine.
<template>
<transition name="slide-in">
<div #click.stop class="modal__container">
<div #click="close" class="modal__button">
<span class="modal__button--close">Close</span>
</div>
</div>
</transition>
</template>
Referencing Vue.js documentation on transitions
Vue provides a variety of ways to apply transition effects when items are inserted, updated, or removed from the DOM
That means that DOM nodes that transition applies classes to, should be those which are inserted/updated/removed.
Since it is a modal window, I assume that it has v-if directive applied in parent component to handle it's visibility. In order for transition to work, it should wrap DOM element that will be updated.
You can understand it more easily if you move code of your modal window into the parent component. Just for better visualization of elements tree and transition's behavior.
In first example, conditional rendering (v-if) applies to <div class="modal">, which is not wrapped with transition - therefore no animation will be triggered. At the same time, nested nodes are wrapped with transition, but there is nothing that will update or remove them. They are statically displayed and inserted initially on component's creation. Nothing to animate.
In order for it to work as expected following structure would be advisable:
<template>
<transition name="fade-in">
<div
class="modal__overlay"
#click="close"
>
<transition name="slide-in">
<div
v-if="containerVisible"
class="modal__container"
#click.stop
>
<div #click="close" class="modal__button">
<span class="modal__button--close">Close</span>
</div>
</div>
</transition>
</div>
</transition>
</template>
This solution expects modal__overlay to have position: fixed; style and variable containerVisible to be set to true inside mounted hook of modal component.