Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Vue.js [v-cloak] does not use CSS transition

My Intention:

Using the Vue.js (v2) attribute [v-cloak], I want to have the "app" hidden until ready. When [v-cloak] is removed, I want the "app" to fade in. Using CSS opacity and transitions to achieve the fade.

The Problem:

When [v-cloak] is removed from my "app" there is no transition as I would expect. It just goes from hidden to visible immediately. Seems to ignore the CSS.

Example:

I have made an exaggerated example with Vue.js and a JavaScript simulated version to show how they both behave.

https://codepen.io/freemagee/pen/wXqrRM

When viewing this example, you will see the "Plain old JavaScript" red box fade into view over 5 seconds. But the Vue controlled version just appears without a fade. They share the same CSS for the transition, so in theory should work the same way.

Anyone used [v-cloak] effectively to achieve smooth transitions?

Codepen Code

HTML

<div id="app" v-cloak>
  <div class="red-box"></div>
  <p>Vue.js {{ message }}</p>
</div>

<div id="app2" v-cloak>
  <div class="red-box"></div>
  <p>Plain old JavaScript</p>
</div>

CSS

html,
body {
  margin: 0;
  height: 100%;
  min-height: 100%;
}

[v-cloak] .red-box {
  opacity: 0;
  visibility: hidden;
  transition: visibility 0s 5s, opacity 5s linear;  
}

#app,
#app2{
  padding-top: 50px;
}

.red-box {
  margin: 0 auto;
  width: 100px;
  height: 100px;
  background: red;
  opacity: 1;
  visibility: visible;
  transition: opacity 5s linear;
}

p {
  text-align: center;
}

JS

new Vue({
  el: "#app",
  data: {
    message: "Hello world"
  }
});

window.setTimeout(function() {
  document.getElementById("app2").removeAttribute("v-cloak");
}, 500);
like image 851
freeMagee Avatar asked Jun 14 '18 14:06

freeMagee


Video Answer


2 Answers

This won't work because after the Vue app instance initializes, the #app div is actually removed, re-rendered and becomes a different div, even though it looks the same. This is probably due to Vue's virtual DOM mechanism.

The #app2 elements is still the same DOM after document.getElementById("app2").removeAttribute("v-cloak");: https://codepen.io/jacobgoh101/pen/PaKQwV

The #app element is a different DOM after new Vue(...): https://codepen.io/jacobgoh101/pen/ERvojx?editors=0010

For the Vue app, the element with v-cloak is removed, another element without v-cloak is added back. There is no element that transition from "with v-cloak" to "without v-cloak". That's why the CSS transition won't work. Hope that this is clear enough.

(If you don't already know this, )You can use Transition Component

like image 189
Jacob Goh Avatar answered Sep 24 '22 20:09

Jacob Goh


As explained, there is no transition possible with [v-cloak], as the DOM element without the v-cloak is a new one.

I've figured a simple workaround, using the mounted methods and its hook vm.$nextTick(), in which case, the use of v-cloak isn't necessary anymore.

Mounted is called when the original app element has been replaced with the new one generated by Vue, but it doesn't necessarily mean that every child has been rendered yet. nextTick is called when every child element has been rendered inside the app view.

First, I have setup my HTML app element like this :

<div id="main-view" :class="{ready: isPageReady}">...</div>

In my vue app :

new Vue({
    el: "#main-view",
    data: {
        isPageReady: false, 
    [...]
    mounted: function() {
        this.$nextTick(function () {
            this.isPageReady = true;
        });
    }
});

Finally in the CSS, I tried a simple fadein using opacity:

#main-view {
    opacity: 0;
}
#main-view.ready {
    opacity: 1;
    transition: opacity 300ms ease-in-out;
}

Beware: the transition doesn't always show if you have the browser's inspector/debugger open.

like image 42
Guillaume Chantraine Avatar answered Sep 22 '22 20:09

Guillaume Chantraine