Drag select cells
by Alexander Tymchuk
HTML
Click and drag mouse or use shift key to select cells.
<table id="table">
<tr>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
</table>
CSS
table {
border-collapse: collapse;
}
table td {
border: 1px solid #999;
width: 40px;
height: 40px;
margin: 10px;
user-select: none;
}
td.selected {
background-color: green;
}
JavaScript
const root = document.querySelector('#table');
let isMouseDown = false;
let rowIndex = null;
let startCellIndex = null;
function getElementIndex(element) {
return [...element.parentNode.children].findIndex(el => element === el);
}
function removeClass(parentNode, className) {
Array.from(parentNode.querySelectorAll('.' + className))
.forEach(el => el.classList.remove(className));
}
function addListeners(nodeList, eventName, listener) {
Array.from(nodeList).forEach(node => {
node.addEventListener(eventName, listener, false);
});
}
function selectTo(cell) {
const cellIndex = getElementIndex(cell);
let cellStart, cellEnd;
if (cellIndex < startCellIndex) {
cellStart = cellIndex;
cellEnd = startCellIndex;
} else {
cellStart = startCellIndex;
cellEnd = cellIndex;
}
// force to stay on the same row
if (getElementIndex(cell.parentNode) !== rowIndex) {
const row = root.querySelectorAll('tr').item(rowIndex);
[...row.children]
.filter((_, i) => i >= cellStart && i <= cellEnd)
.forEach(el => el.classList.add('selected'));
return;
}
const { children: cells } = cell.parentNode;
[...cell.parentNode.children]
.filter((_, i) => i >= cellStart && i <= cellEnd)
.forEach(el => el.classList.add('selected'));
}
function onMousedown(e) {
isMouseDown = true;
const cell = e.target;
// deselect everything
removeClass(root, 'selected');
if (e.shiftKey) {
selectTo(cell);
} else {
cell.classList.add('selected');
const { children: cells } = cell.parentNode;
startCellIndex = [...cells].findIndex(td => cell === td);
const { children: rows } = cell.parentNode.parentNode;
rowIndex = [...rows].findIndex(row => row === cell.parentNode);
}
return false;
// prevent text selection
}
function onMouseover(e) {
if (!isMouseDown) return;
...