List Enter/Exit
Animate items as they enter and leave a data-list.
Live Demo
Source
HTML + JavaScript + CSS
<div data-component="animated-list">
<button data-action="addItem">+ Add</button>
<button data-action="removeFirst">Remove First</button>
<div data-list="items" data-key="id">
<template>
<div class="list-item" style="animation: list-item-in 0.3s ease;">
<span data-bind="label"></span>
<button data-action="removeItem">×</button>
</div>
</template>
</div>
</div>
<style>
@keyframes list-item-in {
from { opacity: 0; transform: translateX(-20px); }
to { opacity: 1; transform: translateX(0); }
}
@keyframes list-item-out {
from { opacity: 1; transform: translateX(0); max-height: 50px; }
to { opacity: 0; transform: translateX(20px); max-height: 0; }
}
</style>
<script>
wildflower.component('animated-list', {
state: {
nextId: 4,
items: [
{ id: 1, label: 'Item 1' },
{ id: 2, label: 'Item 2' },
{ id: 3, label: 'Item 3' }
]
},
addItem() {
this.items.push({ id: this.nextId, label: 'Item ' + this.nextId });
this.nextId++;
},
_animateOut(el, callback) {
el.style.animation = 'list-item-out 0.25s ease forwards';
el.style.overflow = 'hidden';
el.addEventListener('animationend', callback, { once: true });
},
removeFirst() {
const el = this.find('[data-list] .list-item');
if (!el) return;
this._animateOut(el, () => this.items.shift());
},
removeItem(e, el, { index }) {
const row = el.closest('.list-item');
if (!row) return;
this._animateOut(row, () => this.items.splice(index, 1));
}
});
</script>
Key Points
- Enter: CSS
animationon the template's root element runs automatically when items are added to the DOM - Exit: Apply an exit animation, then remove the item in the
animationendcallback data-key="id"ensures correct item identity, preventing stale animations on reorder- The
_animateOuthelper keeps exit logic reusable across remove methods