explorer-like selection and move
by Stalk
HTML
<main>
<section>
<ul id="left">
<li>Apples</li>
<li>Butter</li>
<li>Charlie</li>
<li>Duff</li>
<li>Edward</li>
</ul>
</section>
<section class="mid">
<button id="to-left">◀</button>
<button id="to-right">▶</button>
</section>
<section>
<ul id="right">
</ul>
</section>
</main>
CSS
body {
height: 100vh;
margin: 0;
font-family: monospace;
font-size: 20px;
}
main {
display: flex;
}
section {
width: calc(100% / 3);
border: 1px solid lightgray;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
button {
margin-top: 1em;
}
.selected {
background: #9f9;
}
ul {
list-style-type: none;
padding: 1em;
}
li {
cursor: pointer;
user-select: none;
}
JavaScript
let left = document.getElementById('left'),
right = document.getElementById('right');
selectableList(left, 'selected')
selectableList(right, 'selected')
document.getElementById('to-right')
.addEventListener('click', () => move(left, right));
document.getElementById('to-left')
.addEventListener('click', () => move(right, left));
function move(from, to) {
from.querySelectorAll('.selected')
.forEach(el => to.appendChild(el))
}
function selectableList(parent, cls) {
let last = parent.firstElementChild
parent.onmousedown = function(e) {
let elems = Array.from(parent.children)
if (e.target.matches('li')) {
if (e.ctrlKey || e.metaKey) e.target.classList.toggle(cls);
else if (e.shiftKey && parent.contains(last)) {
let range = [elems.indexOf(last), elems.indexOf(e.target)].sort()
elems.forEach((el, i) =>
el.classList.toggle(cls, (i >= range[0] && i <= range[1]))
)
} else {
elems.forEach(el => el.classList.remove(cls))
e.target.classList.toggle(cls)
}
last = e.target
}
}
}