Search code examples
vue.jsvuejs2velocity.js

Vue transition: v-on:before-leave hides element before callback executes


I'm learning Vue and very confused about this.

I'm trying to replicate jQuery's slideToggle().

I have a list of items, each with a heading and a body.

Initially the body of each list item is hidden, and when the user clicks the title it should do a slide transition.

Because the body of each list item is a variable height, I need to do this in JS, because CSS can't correctly do the transition for height: auto. (Even max-height is far from smooth.

So I'm using the Velocity.js library, and slideDown is perfect. slideUp can't run because (I think) Vue has already hidden the element.

Here's the (simplified) template:

<ul class="list-group">
    <li class="list-group-item" v-for="word in saved_definitions">
        <h3 @click="word.show = !word.show">
            {{ word.word }}
        </h3>
        <transition v-on:before-enter="slide_down" v-on:before-leave="slide_up" :css="false">
            <div v-show="word.show">
                {{ word.definition }}
            </div>
        </transition>
    </li>
</ul>

saved_definitions is an array of {word: '', definition: '', show: true} objects.

Here are the relevant methods:

slide_down: function(el) {
    Velocity(el, 'slideDown', 'fast');
},

slide_up: function(el) {
    console.log(el);
    Velocity(el, 'slideUp', 'fast');
}

slide_down() is working perfectly.

slide_up() doesn't work, the definition disappears with no transition. The console.log() shows that the element has display: none before Velocity can do the slideUp.

jsFiddle is here: https://jsfiddle.net/zmoc2v3n/ - you can see slideDown is smooth, slideUp doesn't work.

I've tried the leave and after-leave hooks, which don't help.

And if I use v-if, I see the element get rendered first, then hidden, and then slideDown happens. I still have the same issue with before-leave hiding the element immediately.

Thanks in advance for your help!


Solution

  • I asked this question on the Vue Forum, and thought I'd post the answer here.

    Try using the enter and leave events instead, as they allow you to let Vue know when your animation is finished.

    <transition @enter="slide_down" @leave="slide_up"></transition>
    

    In the js:

    function slide_down(el, done) {
        Velocity(el, 'slideDown', {
            duration: 'fast',
            complete: done
        })
    }
    
    function slide_up(el, done) {
        Velocity(el, 'slideUp', {
            duration: 'fast',
            complete: done
        })
    }
    

    Turns out my issue was more with Velocity than Vue.

    Here is the working jsfiddle.