I'm working with pure JS hooks for the <transition-group>
element on VueJS, and I am quite puzzled on how the enter
hook actually works. Based on the documentation, I understand that I will have to call done()
to avoid events being called synchronously:
When using JavaScript-only transitions, the
done
callbacks are required for theenter
andleave
hooks. Otherwise, the hooks will be called synchronously and the transition will finish immediately.
However, even when I use it, it seems to stop CSS transitions from happening in the entering transition. The only solution I found is to use window.setTimeout
to set the style, which I think is a code smell. Here is a quick visual comparison between the code without the timeout, and the one with (the one with the timeout is the desired effect):
Broken enter transition (no transition of the left padding and opacity):
Desired enter transition:
In the example below, I am displaying a list using <transition-group>
and wanted to use JS-hooks so that I can create staggered paddings on individual list items. It seems to work with the exception that in the enter
transition, the CSS transitions on the padding property does not work.
new Vue({
el: '#app',
data: {
items: [
'Lorem',
'Ipsum',
'Dolor',
'Sit',
'Amet'
],
toggle: false
},
computed: {
filteredItems: function() {
if (!this.toggle)
return [];
return this.items;
}
},
methods: {
toggleItems: function() {
this.toggle = !this.toggle;
},
beforeEnter: function(el) {
el.style.paddingLeft = '0px';
el.style.opacity = '0';
},
enter: function(el, done) {
el.style.paddingLeft = `${10 * +el.dataset.index}px`;
el.style.opacity = '1';
done();
},
beforeLeave: function(el) {
el.style.paddingLeft = '0px';
el.style.opacity = '0';
}
}
})
ul {
list-style: none;
margin: 0;
padding: 0;
}
ul li {
transition: all 500ms ease-in-out;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<button @click="toggleItems">
Toggle items
</button>
<transition-group
tag="ul"
@before-enter="beforeEnter"
@enter="enter"
@before-leave="beforeLeave">
<li
v-for="(item, i) in filteredItems"
v-bind:key="i"
v-bind:data-index="i">
{{ item }}
</li>
</transition-group>
</div>
If you wrap all the logic inside the enter
method inside an arbitrary timout, then it works:
enter: function(el, done) {
window.setTimeout(() => {
el.style.paddingLeft = `${10 * +el.dataset.index}px`;
el.style.opacity = '1';
done();
}, 100);
},
And this is where I am a little confused: does the enter
hook not wait for beforeEnter
to complete first? The working snippet is as follow
Changing the @enter
hook to @after-enter
should fix it
I have no clue why the @enter
hook isn't working for this, as looking at the documentation it should but this should at least get rid of the timeout without being a hack
new Vue({
el: '#app',
data: {
items: [
'Lorem',
'Ipsum',
'Dolor',
'Sit',
'Amet'
],
toggle: false
},
computed: {
filteredItems: function() {
if (!this.toggle)
return [];
return this.items;
}
},
methods: {
toggleItems: function() {
this.toggle = !this.toggle;
},
beforeEnter: function(el) {
el.style.paddingLeft = '0px';
el.style.opacity = '0';
},
afterEnter: function(el) {
el.style.paddingLeft = `${10 * +el.dataset.index}px`;
el.style.opacity = '1';
},
beforeLeave: function(el) {
el.style.paddingLeft = '0px';
el.style.opacity = '0';
}
}
})
ul {
list-style: none;
margin: 0;
padding: 0;
}
ul li {
transition: all 500ms ease-in-out;
}
li.v-enter-active {
transition: none
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<button @click="toggleItems">
Toggle items
</button>
<transition-group
tag="ul"
@before-enter="beforeEnter"
@after-enter="afterEnter"
@before-leave="beforeLeave">
<li v-for="(item, i) in filteredItems" v-bind:key="i" v-bind:data-index="i">
{{ item }}
</li>
</transition-group>
</div>
As a side note, if you're using SCSS or SASS, you can achieve this with that rather than JavaScript