What causes the transition to not enter when inside an element with v-if, yet it successfully exits?

Explaining the title pretty much covers it all. I've looked for a solution on StackOverflow, but neither of them provided any help.

Here's an example using a classic modal component:

Vue.component('modal', {
  template: `
  <transition name="modal">
    <div class="modal-wrapper">
      <div class="modal-dialog">
        <slot></slot>
      </div>
    </div>
  </transition>
  `,
})

const app = new Vue({
  el: '#app',
  data: {
    showModal: false,
  },
})
/* transition */
.modal-enter-active,
.modal-leave-active {
  transition: opacity .5s;
}

.modal-enter,
.modal-leave-to {
  opacity: 0;
}

.modal-wrapper {
  position: absolute;
  left: 0; right: 0;
  top: 0; bottom: 0;
  display: flex;
  justify-content: center;
  align-items: center;
  background: rgba(0, 0, 0, .25);
}

.modal-dialog {
  max-width: 90%;
  padding: 1em;
  background: white;
}
<script src="https://vuejs.org/js/vue.js"></script>
<div id="app">
  <p><button @click="showModal = true">Show modal</button></p>
  <modal v-if="showModal">
    <h3>Hello world</h3>
    <p>Amet quam alias amet incidunt voluptatum sapiente Mollitia</p>
    <p><button @click="showModal = false">Close</button></p>
  </modal>
</div>

(There are no errors in the console as well)

However, everything is fine when using v-show. But I can't use it instead of v-if in my project.

Vue.component('modal', {
  template: `
  <transition name="modal">
    <div class="modal-wrapper">
      <div class="modal-dialog">
        <slot></slot>
      </div>
    </div>
  </transition>
  `,
})

const app = new Vue({
  el: '#app',
  data: {
    showModal: false,
  },
})
/* transition */
.modal-enter-active,
.modal-leave-active {
  transition: opacity .5s;
}

.modal-enter,
.modal-leave-to {
  opacity: 0;
}

.modal-wrapper {
  position: absolute;
  left: 0; right: 0;
  top: 0; bottom: 0;
  display: flex;
  justify-content: center;
  align-items: center;
  background: rgba(0, 0, 0, .25);
}

.modal-dialog {
  max-width: 90%;
  padding: 1em;
  background: white;
}
<script src="https://vuejs.org/js/vue.js"></script>
<div id="app">
  <p><button @click="showModal = true">Show modal</button></p>
  <modal v-show="showModal">
    <h3>Hello world</h3>
    <p>Amet quam alias amet incidunt voluptatum sapiente Mollitia</p>
    <p><button @click="showModal = false">Close</button></p>
  </modal>
</div>

In this case, I have to enclose <modal> with <transition> wherever the modal is used and remove the transition from the modal itself (which doesn't seem ideal).

<transition name="modal">
  <modal>
  ...
  </modal>
</transition>

Why is that and how can we make the entering animation work (with v-if, and <transition> inside the modal component?

I've observed that there is no such issue with Vue 2.5 (as opposed to Vue 2.6). Something has definitely changed since then.

Answer №1

It seems like you overlooked the appear attribute.

In Vue, when an element is first inserted, there won't be any animation by default. Here's what the documentation says:

If you want to have a transition when a node is initially rendered, you can include the appear attribute:

<transition appear>
  <!-- ... -->
</transition>

By default, it will use the specified enter and leave transitions. However, you can also define custom CSS classes:

<transition
  appear
  appear-class="custom-appear-class"
  appear-to-class="custom-appear-to-class"
  appear-active-class="custom-appear-active-class"
>
<!-- ... -->
</transition>

Simply adding appear should resolve the issue.

Answer №2

One reason for this behavior is that v-if adds or removes elements from the DOM, while v-show simply hides them without removing them. In the provided example, if transition is placed above the v-if statement, the transition will not work correctly. Moving v-if below the transition element should resolve the issue.

Vue.component('modal', {
  props: ['showModal'],
  template: `
  <transition name="modal">
    <div class="modal-wrapper" v-if="showModal">
      <div class="modal-dialog">
        <slot></slot>
      </div>
    </div>
  </transition>
  `,
})

const app = new Vue({
  el: '#app',
  data: {
    showModal: false,
  },
})
/* transition */
.modal-enter-active,
.modal-leave-active {
  transition: opacity .5s;
}

.modal-enter,
.modal-leave-to {
  opacity: 0;
}

.modal-wrapper {
  position: absolute;
  left: 0; right: 0;
  top: 0; bottom: 0;
  display: flex;
  justify-content: center;
  align-items: center;
  background: rgba(0, 0, 0, .25);
}

.modal-dialog {
  max-width: 90%;
  padding: 1em;
  background: white;
}
<script src="https://vuejs.org/js/vue.js"></script>
<div id="app">
  <p><button @click="showModal = true">Show modal</button></p>
  <modal :show-modal="showModal">
    <h3>Hello world</h3>
    <p>Amet quam alias amet incidunt voluptatum sapiente Mollitia</p>
    <p><button @click="showModal = false">Close</button></p>
  </modal>
</div>

Answer №3

Here is a solution that should work:

{
  template: `
  <transition name="modal">
    <div v-if="show">...</div>
  </transition>
  `,
  data() {return {
    show: false,
  }},
  mounted() {
    this.show = true
  },
}

In my opinion, this approach introduces a one Vue tick delay to the animation, which could potentially cause slowdowns if used excessively.

Vue.component('modal', {
  template: `
  <transition name="modal">
    <div class="modal-wrapper" v-if="show">
      <div class="modal-dialog">
        <slot></slot>
      </div>
    </div>
  </transition>
  `,
  data() {return {
    show: false,
  }},
  mounted() {
    this.show = true
  }
})

const app = new Vue({
  el: '#app',
  data: {
    showModal: false,
  },
})
/* transition */
.modal-enter-active,
.modal-leave-active {
  transition: opacity .5s;
}

.modal-enter,
.modal-leave-to {
  opacity: 0;
}

.modal-wrapper {
  position: absolute;
  left: 0; right: 0;
  top: 0; bottom: 0;
  display: flex;
  justify-content: center;
  align-items: center;
  background: rgba(0, 0, 0, .25);
}

.modal-dialog {
  max-width: 90%;
  padding: 1em;
  background: white;
}
<script src="https://vuejs.org/js/vue.js"></script>
<div id="app">
  <p><button @click="showModal = true">Show modal</button></p>
  <modal v-if="showModal">
    <h3>Hello world</h3>
    <p>Amet quam alias amet incidunt voluptatum sapiente Mollitia</p>
    <p><button @click="showModal = false">Close</button></p>
  </modal>
</div>

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Retrieve the value of the target element when the Quasar q-checkbox is checked

I'm currently utilizing QUASAR and encountering an issue where I am unable to retrieve the state of my q-checkbox to determine whether it is checked or not. Despite using event.target.checked and event.target.value, both return as undefined. Below is ...

Include previous input as a "phantom" thumbnail to the slider element of type "range"

https://i.stack.imgur.com/JnuCN.png Using a tutorial from w3schools, I have customized a regular range slider. The objective is to send a new value command to an external MQTT-based home automation system and display the previous value as a ghost-thumb in ...

Is there a way to specifically transmit ComponentArt CallbackEventArgs from a JavaScript function during a callback?

One of the challenges I'm facing involves implementing a callback in my ComponentArt CallBack control using javascript when the dropdown list is changed. I specifically want to pass both the control and the associated ComponentArt.Web.UI.CallBackEvent ...

Angular allows for displaying objects within other objects using interpolation

Below is the object stored in EntryList variable within my component: { "id": 0, "startAddress": { "id": 6, "addressString": "addressString" }, "endAddress": { ...

Input the chosen icon list values into a designated box

As a beginner in the world of Javascript, I am venturing into creating a password generator that involves using icons. The concept is simple - by clicking on any number of icons from a list, the corresponding hex code will be displayed in an input box. The ...

Incorporating JavaScript in NodeJS

I'm exploring ways to optimize my FLOPS performance in NodeJS, which is why I am interested in incorporating bitwise operations. For example: var a = 6, b = 12; a + b Instead of the simple addition above, I've created a more intricate function ...

What's better in React: using pure components or non-pure components? Is it okay to fetch data in componentDidMount or

Exploring React in Meteor has led me to observe two distinct approaches... Take the meteor leaderboard example, where a list of players displays their names and scores. The pure approach involves fetching all players and passing them into the playersList ...

Automatically populate login credentials on a webpage using Javascript scripting

Can anyone provide insights on why the input credentials on a webpage cannot be autofilled with this code? Is there a solution to make it functional? Trying to automate login process using JavaScript: function Test() { var name = document.getElementBy ...

What is the best way to display two timers on one page?

I'm having an issue with displaying two timers for a 3-minute countdown. The problem is that the second timer works perfectly, but the first timer doesn't. Even if I remove the second timer, there is still some problem with the first one. The cod ...

Utilize a separate function within the ngOnInit() lifecycle hook

Currently, I am developing a mapping application using OpenLayers (4.6.5) within Angular (6). In order to execute requests and retrieve GeoJSON files, I am utilizing a French API made available by the French government. Previously, I successfully implemen ...

Close modal using jQuery when the submit button is clicked

My bootstrap modal is quite large. <div className="add-date"> <div className="modal" id="eventSelectedModal" tabIndex="-1" role="dialog" aria-labelledby="myModalLabel1" aria-hidden="true"> <div className="modal-dialog" role="do ...

JavaScript disrupting CSS animations

I've just embarked on creating a simple landing-page website. One component of the page is a basic image slider with navigation controls powered by JavaScript. I managed to get everything functioning except for achieving a smooth transition between im ...

The Google Map is not showing all the details

Our app is developed using ReactJS on Rails API, with a Google map integrated. However, when visiting this link and clicking "Map View", some grey space appears under the Google Map. An example image can be found here: example We are having trouble findi ...

Trouble Arising from Regional Settings in my Hybrid App Developed with CapacitorJS and Vue.js

Issue with capacitor app input Correct input in chrome app I'm facing a problem related to regional settings in my mobile application, developed using CapacitorJS and Vue.js 2. The datetime-local control is appearing in a language that isn't the ...

Issue with using the bootstrap template plugin for cube portfolio

I have integrated cubeportfolio.js into a bootstrap template, but I am encountering an issue with the custom .js code included in the template that is causing an error in the console. You can view the template I am using here, and it appears to be functio ...

What is the best way to utilize javascript/jQuery/ajax to output a separate page?

After executing the code below, a print dialog box is displayed and successfully prints the page. However, I am wondering how I can make it print a different page when the same button is clicked. The name of the different page is: letterprint.php <div ...

The front-end is responsible for removing the last item from an array

Having an issue with my React code. Here is my parent component: class RoomPrice extends React.Component { constructor(props){ super(props) this.state = { room: this.props.room, prices: [] }; this.handleDeletePrice = this.h ...

Adjust the autofocus to activate once the select option has been chosen

Is there a way to automatically move the cursor after selecting an option from a form select? <select name="id" class="form-control"> <option>1</option> <option>2</option> <option>3</option&g ...

error in css styling detected

Why was the CSS style "background-position: center;" missed? It was because the background will override the background-position property. <html> <head> <script src="https://cdn.jsdelivr.net/npm/<a href="/cdn-cgi/l/email-protection" ...

Having difficulty incorporating a watcher into my test scripts using vue-utils

Currently, I am attempting to include a watcher in my "test" script as specified in the package.json file. The aim is simple: every time I make changes to a file ending with .spec.js in the tests/FrontEnd directory, I want the command "npm run test" to be ...