Animated add/remove list items test

Adding/removing list items with animation.

by Travis Almand

HTML

<button id="add">add item</button>

<ul id="list"></ul>

CSS

html {
  box-sizing: border-box;
}
*, *:before, *:after {
  box-sizing: inherit;
}

body {
  margin: 0;
  padding: 0;
}

button {
  border-radius: 50%;
  cursor: pointer;
  margin: 10px;
  padding: 10px;
}

ul {
  list-style: none;
  margin: 0;
  padding: 0;
  position: relative;
}

li:not(#dummy) {
  background-color: green;
  height: 40px;
  line-height: 40px;
  margin: 10px;
  padding: 0 10px;
}
.new-item {
  animation: newItem 0.5s 0.5s forwards;
  left: 0;
  opacity: 0;
  position: absolute;
  top: -10px;
  width: calc(100% - 20px);
}

.remove-item {
  animation: removeItem 0.5s forwards;
  left: 0;
  position: absolute;
  top: -10px;
  width: calc(100% - 20px);
}

#dummy {
  height: 40px;
  line-height: 40px;
  margin: 10px;
  overflow: hidden;
}
#dummy.in {
  animation: appearDummy 0.5s;
}
#dummy.out {
  animation: goAwayDummy 0.5s 0.5s forwards;
}

@keyframes newItem {
  0% {
    opacity: 0;
  }
  100% {
    opacity: 1;
  }
}

@keyframes removeItem {
  0% {
    opacity: 1;
  }
  100% {
    opacity: 0;
  }
}

@keyframes appearDummy {
  0% {
    height: 0;
    margin: 0 10px;
  }
  100% {
    height: 40px;
    margin: 10px 10px;
  }
}

@keyframes goAwayDummy {
  0% {
    height: 40px;
    margin: 10px 10px;
  }
  100% {
    height: 0;
    margin: 0 10px;
  }
}

JavaScript

var $add = document.querySelector('#add');
var $list = document.querySelector('#list');
var $newItem;
var newItemString = '<li class="new-item">item</li>';
var $removeItem;
var $dummyItem;
var dummyItemString = '<li id="dummy">&nbsp;</li>';

$add.addEventListener('click', addItem);
$list.addEventListener('click', removeItem);

function addItem() {
	$add.disabled = true;
	$list.insertAdjacentHTML('afterbegin', dummyItemString);
  $list.insertAdjacentHTML('afterbegin', newItemString);
  
  $newItem = document.querySelector('.new-item');
  $dummyItem = document.querySelector('#dummy');
  
  $dummyItem.classList.add('in');
  $newItem.addEventListener('animationend', addItemDone, {once: true});
}

function removeItem(e) {
	e.target.classList.add('remove-item');
  e.target.insertAdjacentHTML('beforebegin', dummyItemString);
  
  $removeItem = document.querySelector('.remove-item');
  $dummyItem = document.querySelector('#dummy');
  
  $dummyItem.classList.add('out');
  $removeItem.addEventListener('animationend', removeItemDone, {once: true});
}

function addItemDone() {
	$add.disabled = false;
	$newItem.classList.remove('new-item');
  $dummyItem.remove();
}

function removeItemDone() {
	$removeItem.remove();
  $dummyItem.addEventListener('animationend', function () {
  	$dummyItem.remove();
  }, {once: true});
}