Vue 2 Transition not working - vue.js

I have no idea where my code went wrong. It should be a simple transition. When I click the button the message shows correctly, but just that the there is no fade transition happening at all.
<template>
<div>
<transition name="fade">
<message v-show="showMessage" class="tr pop-up-message">
<p slot="header">This is Header</p>
<span slot="body">This is Body</span>
</message>
</transition>
<div v-if="!showMessage" class="block" #click.prevent="showMessage = true">
<a class="button is-primary">Primary</a>
</div>
<div v-else-if="showMessage" class="block" #click.prevent="showMessage = false">
<a class="button is-primary">Primary</a>
</div>
</div>
</template>
<script>
import message from './Message.vue'
export default {
components:{
'message': message,
},
data(){
return{
showMessage: false
}
},
}
</script>

Have you added these CSS as well:
.fade-enter-active, .fade-leave-active {
transition: opacity .5s
}
.fade-enter, .fade-leave-to /* .fade-leave-active in <2.1.8 */ {
opacity: 0
}
I have tried to reproduce your code here with above CSS which works.

I had two <p> tags right next to each other like below
<p v-if="!isControlling">Take control of the camera by clicking</p>
<p v-else>Press <kbd>Esc</kbd> to exit camera. <kbd>W</kbd> <kbd>A</kbd> <kbd>S</kbd> <kbd>D</kbd> <kbd>Space</kbd> <kbd>Shift</kbd> to move. Mouse to look.</p>
and they just didn't work. It looked like Vue was reusing the same <p> tag in the markup to render both. Adding a key to each made the transition work (thanks #Mark). The fix should look something like below
<p key="asd" v-if="!isControlling">Take control of the camera by clicking</p>
<p key="asf" v-else>Press <kbd>Esc</kbd> to exit camera. <kbd>W</kbd> <kbd>A</kbd> <kbd>S</kbd> <kbd>D</kbd> <kbd>Space</kbd> <kbd>Shift</kbd> to move. Mouse to look.</p>

Related

Vue transition works for "enter" state but not for "leave" state

I have a modal rendered on top of a semi-transparent backdrop. Both elements have a v-if controlled by the same variable.
Although the enter transition animation works fine, the `leave`` transition animation is ignored (it should fade out smoothly, instead it disappears instantly). Why?
Codepen
Markup:
<div id="app">
<button #click="showModal = !showModal">Toggle Modal</button>
<div v-if="showModal" class="modalBackdrop">
<transition name="content" appear>
<div v-if="showModal" class="modalContent">
Modal
</div>
</transition>
</div>
</div>
CSS:
.content-enter-active {
animation: slide-up .75s;
}
.content-leave-active {
animation: fade-out .75s;
}
#keyframes slide-up {
0% {
transform: translateY(100%);
}
100% {
transform: translateY(0);
}
}
#keyframes fade-out {
0% {
opacity: 1;
}
100% {
opacity: 0;
}
}
It seems that the div with modalBackdrop class is disappearing before the nested div with class modalContent does its transition, so try to wrap modal Backdrop by a transition component with name backdrop which also takes the fade-out animation :
.backdrop-leave-active,.content-leave-active { /*.backdrop-leave-active is sufficient since the parent opacity is applied on children*/
animation: fade-out .75s;
}
template :
<div id="app">
<button #click="showModal = !showModal">Toggle Modal</button>
<transition name="backdrop" appear>
<div v-if="showModal" class="modalBackdrop">
<transition name="content" appear>
<div v-if="showModal" class="modalContent">
Modal
</div>
</transition>
</div>
</transition>
</div>
DEMO
When showModal is false, the transition element is destroyed immediately. If the only reason you use v-if="showModal" in transition parent is to disable modalBackdrop, then you can assign this class dynamically.
This is working as expected:
<div :class="{ modalBackdrop: showModal }">
<transition name="content" appear>
<div v-if="showModal" class="modalContent">
Modal
</div>
</transition>
</div>

Why does this Vue3 transition break data binding?

I have this issue I've been hitting for hours now; I can't understand why it doesn't work as expected.
I pasted an example code below. The issue is that when editing the name, {{name}} is not updated. However, if I remove either of the <transition> element or the v-if="show" condition, then data binding works as expected. Same if the {{name}} is placed outside the transition.
So it seems the transition blocks data binding? However I don't find anything about it in the docs or elsewere. I tested this code in a Vue2 playground, and it works as expected (data binding works). So the behavior seems to depend on Vue3.
Is there something I'm missing? Is it a bug in Vue3?
Thanks in advance for any input or idea.
<template>
<div id="demo">
<button v-on:click="show = !show">
Toggle
</button>
<transition name="fade">
<div v-if="show">
<p>hello, {{name}}</p>
<input v-model="name" type="text" />
</div>
</transition>
</div>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
export default defineComponent({
data() {
return {
name: "",
show: true,
}
}
});
</script>
<style scoped>
.fade-enter-active,
.fade-leave-active {
transition: opacity 0.8s ease;
}
.fade-enter-from,
.fade-leave-to {
opacity: 0;
}
</style>
It works just fine in plain JS...
So try to focus on the differences:
TypeScript (i cannot use it here on SO) - I really doubt its the cause but you can try
Scoped CSS - did you tried to remove scoped ? There are some issues with scoped CSS and <transition>. Check this issue in Vue-loader. My example is not build with Webpack so Vue-loader is not used but it's for sure used in your project...
const app = Vue.createApp({
data() {
return {
name: "",
show: true,
}
},
template: `
<div id="demo">
<button v-on:click="show = !show">
Toggle
</button>
<transition name="fade">
<div v-if="show">
<p>hello, {{name}}</p>
<input v-model="name" type="text" />
</div>
</transition>
</div>
`
}).mount("#app");
.fade-enter-active,
.fade-leave-active {
transition: opacity 0.8s ease;
}
.fade-enter-from,
.fade-leave-to {
opacity: 0;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/3.0.0/vue.global.js"></script>
<div id="app"></div>
I meet same question, you can try to set the initial value of 'show' to false and at the lifeCycle to modify 'show' for true.

Window is not defined in nuxt js

So, I wanted to bind style with the height of another element that I got from getHeight function, but I kept getting an error that said window is not defined.
Can someone please give me a solution?
Here is my source code:
<template>
<div class="container">
<p class="section-title">past event</p>
<div class="columns is-multiline">
<div
class="column is-one-third is-centered past-events"
v-for="(event, index) in events.slice(0, 2)"
:key="index"
>
<EventCard :event="event" />
</div>
<div class="column is-one-third is-centered">
<div class="link-box" :style="{ height: getHeight() }">
<nuxt-link to="/past-events">
<p style="color: #ffffff; cursor: pointer" class="see-all">
Lihat List Event Lainnya
</p>
</nuxt-link>
</div>
</div>
</div>
<a class="see-all-btn"> </a>
</div>
</template>
<script>
import EventCard from "~/components/EventCard.vue";
export default {
name: "PastEvents",
components: {
EventCard
},
props: ["events"],
data() {
return {};
},
mounted() {
this.getHeight();
},
methods: {
getHeight() {
const height = window.getComputedStyle(
document.querySelector(".past-events")
).height;
console.log(height);
return height + "px";
}
}
};
</script>
Nuxt uses server-sided rendering. This means that when your code is being executed on the server, it does not have something like window. After all, it is not a browser.
The easiest way around this is by wrapping anything that should not be pre-rendered to html, with something like vue-no-ssr. This particular library renders a dummy component on the server, then actually renders the component when it gets to the browser.
Yes window doesn't exist during the mounted lifecycle hook. I assume you're trying to place something based on its position?
In that case, you might be able to utilize CSS to do it for you. You can place elements using the View Height/View Width units. Combine that with CSS calc() and you might get the solution you need.
Example:
.element {
/* make element positon relative to the window */
position: fixed;
/* set position - note vw/vh are % of window */
/* this put the top of your element -200px from the bottom of your window */
top: calc(100vh - 200px);
}
If you're doing something more complex, using Javascript's element.getBoundingClientRect() will likely provide what you need. See this answer for more info.

How can I use transition on individual list elements in Vue.js?

My understanding of transitions in vue.js is that you use <transition>
to animate between individual elements and <transition-group> to animate a whole list.
It seems as though if you wanted to animate a transition within a list item you'd use <transition> within the list. e.g. something like this:
<span v-for="item in items">
<transition>
<div>
Transition from this...
</div>
<div>
...to this.
</div>
</transition>
</span>
Yet, when I make that change the animation doesn't work. Is this an expected behavior?
Update: after some tinkering, I have found that my original hypothesis was correct. I was just doing something else wrong. But it's worth noting for anyone else who comes across this problem.
You can use <transition> inside a list if you want to animate individual components of the list.
You use transition groups to transition all children in the same way.
In addition, try setting the transition group before your v-for
new Vue({
el: "#app",
data: {
items : [
{message: 'sometext', id: 1},
{message: 'sometext', id: 2},
{message: 'sometext', id: 3}
],
id : 3
},
methods: {
addItem(){
this.id++
this.items.push({message: 'sometext', id: this.id});
},
enter(){
console.log('transition enter called');
}
}
})
.fade-enter-active, .fade-leave-active {
transition: opacity .5s;
}
.fade-enter, .fade-leave-to /* .fade-leave-active below version 2.1.8 */ {
opacity: 0;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<div id="container">
<button #click="addItem()">Add Item</button>
<transition-group :name="'fade'" v-on:enter="enter">
<span v-for="item in items" v-bind:key="item.id">
{{item.message}}
</span>
</transition-group>
</div>
</div>
I am not exactly sure what you are trying to do with your example.
If you would like to transition a list
<transition-group name="fade" tag="span">
<div v-for="item in items" v-bind:key="item">
{{ item }}
</div>
</transition-group>
If you would like to transition between two items.
<transition name="fade">
<div v-show="whatever === true">
Transition from this...
</div>
</transition>
<transition name="fade">
<div v-show="whatever === false">
...to this.
</div>
</transition>

How asynchronous render dom in Vue file?

I use $refs.height[index].offsetTop in the 1st part to computed same values, but $refs.height is in the 2rd part ,that render after the 1st part,so reload page has error TypeError: Cannot read property '0' of undefined.
I try comment <div>{{showNumber(index)}}</div>,then run npm dev,then recomment , it's work.
How render the 2rd part at first?
<template>
<div id="app">
//1st part
<div id="catalog">
<div v-for="(baike,index) in baikes">
<div class="catalog">
<h2>{{index+1}}.{{baike.name}}</h2>
<hr style="border:1px dashed #000; flex-grow: 1">
<div>{{showNumber(index)}}</div>
</div>
</div>
</div>
//2rd part
<div id="content">
<div v-for="(baike,index) in baikes" ref="height">
<h3 >{{index+1}}.{{baike.name}}</h3>
<p>{{baike.abstract}}</p>
<ul v-for="(info,key,index) in baike.info">{{key}}-{{info}}</ul></ul>
</div>
</div>
</div>
</template>
<script>
import baikeJSON from '../../items.json'
export default {
name: 'app',
data () {
return {
baikes: baikeJSON,
}
},
methods:{
showNumber(i){
return Math.ceil(this.$refs.height[i].offsetTop/1000)
}
},
}
</script>
Switch 2rd part and 1st part of position then add css
#app{
display:flex;
flex-direction:column-reverse;
}