// Store references to DOM elements we'll need:
var lists = [
document.querySelector(".js-list0"),
document.querySelector(".js-list1")
];
var items = Array.prototype.slice.call(document.querySelectorAll("li"));
// The function that triggers the css transitions:
var transition = (function() {
var keyIndex = 0,
bboxesBefore = {},
bboxesAfter = {},
storeBbox = function(obj, element) {
var key = element.getAttribute("data-key");
if (!key) {
element.setAttribute("data-key", "KEY_" + keyIndex++);
return storeBbox(obj, element);
}
obj[key] = element.getBoundingClientRect();
},
storeBboxes = function(obj, elements) {
return elements.forEach(storeBbox.bind(null, obj));
};
// `action` is a function that modifies the DOM from state *before* to state *after*
// `elements` is an array of HTMLElements which we want to monitor and transition
return function(action, elements) {
if (!elements || !elements.length) {
return action();
}
// Store old position
storeBboxes(bboxesBefore, elements);
// Turn off animation
document.body.classList.toggle("animated", false);
// Call action that moves stuff around
action();
// Store new position
storeBboxes(bboxesAfter, elements);
// Transform each element from its new position to its old one
elements.forEach(function(el) {
var key = el.getAttribute("data-key");
var bbox = {
before: bboxesBefore[key],
after: bboxesAfter[key]
};
var dx = bbox.before.left - bbox.after.left;
var dy = bbox.before.top - bbox.after.top;
el.style.transform = "translate3d(" + dx + "px," + dy + "px, 0)";
});
// Force repaint
elements[0].parentElement.offsetHeight;
// Turn on CSS animations
document.body.classList.toggle("animated", true);
// Remove translation to animate to natural position
elements.forEach(function(el) {
el.style.transform = "";
});
};
}());
// Event handler & sorting/moving logic
document.querySelector("div").addEventListener("click", function(e) {
var currentList = e.target.getAttribute("data-list");
if (currentList) {
var targetIndex = e.target.getAttribute("data-index");
var nextIndex = 0;
// Get the next list from the lists array
var newListIndex = (+currentList + 1) % lists.length;
var newList = lists[newListIndex];
for (nextIndex; nextIndex < newList.children.length; nextIndex++) {
if (newList.children[nextIndex].getAttribute("data-index") > targetIndex) {
break;
}
}
// Call the transition
transition(function() {
newList.insertBefore(e.target, newList.children[nextIndex]);
e.target.setAttribute("data-list", newListIndex);
}, items);
}
});
div { display: flex; justify-content: space-between; }
.animated li {
transition: transform .5s ease-in-out;
}
<h2>Example</h2>
<div>
<ul class="js-list0">
<li data-index="0" data-list="0">Item 1</li>
<li data-index="3" data-list="0">Item 2</li>
<li data-index="5" data-list="0">Item 4</li>
<li data-index="7" data-list="0">Item 6</li>
</ul>
<ul class="js-list1">
<li data-index="4" data-list="1">Item 3</li>
<li data-index="6" data-list="1">Item 5</li>
</ul>
</div>