Here is it.
Just keep {x, y, width, height}
from getBoundingClientRect
for each selectable item, then check the rectangle intersection of each item with the selecting area in a loop.
There are 29 lines of code from here to create the select area rectangle (with only 2 extra lines),
5 lines to check the rectangle intersection,
5 lines to create demo div
s,
and only 17 lines for the main logic.
It works fine even with scrolling while selecting.
for (let i = 0; i < 50; i++) {
const div = document.createElement("div");
div.style.backgroundColor = `hsl(${Math.random() * 360}, 100%, 50%)`;
div.classList.add("selectable");
document.body.append(div);
}
// --- Main ---
const selectables = [];
const selectableElems = [...document.querySelectorAll(".selectable")];
for (const selectable of selectableElems) {
const {x, y, width, height} = selectable.getBoundingClientRect();
selectables.push({x: x + window.scrollX, y: y + window.scrollY, width, height, elem: selectable});
selectable.dataset.info = JSON.stringify({x, y, width, height});
}
function checkSelected(selectAreaElem) {
const select = selectAreaElem.getBoundingClientRect();
const {x, y, height, width} = select;
for (const selectable of selectables) {
if (checkRectIntersection({x: x + window.scrollX, y: y + window.scrollY, height, width}, selectable)){
selectable.elem.classList.add("intersected");
} else {
selectable.elem.classList.remove("intersected");
}
}
}
// ------------
function checkRectIntersection(r1, r2) {
return !(r1.x + r1.width < r2.x ||
r2.x + r2.width < r1.x ||
r1.y + r1.height < r2.y ||
r2.y + r2.height < r1.y);
}
addEventListener("pointerdown", createDiv);
async function createDiv(event){
event.preventDefault();
const x = event.pageX;
const y = event.pageY;
const div = document.createElement("div");
div.style.position = "absolute";
div.style.width = "0";
div.style.height = "0";
div.style.left = x + "px";
div.style.top = y + "px";
div.classList.add("drag-select");
document.body.append(div);
function resize(event) {
const diffX = event.pageX - x;
const diffY = event.pageY - y;
div.style.left = diffX < 0 ? x + diffX + "px" : x + "px";
div.style.top = diffY < 0 ? y + diffY + "px" : y + "px";
div.style.height = Math.abs(diffY) + "px";
div.style.width = Math.abs(diffX) + "px";
checkSelected(div);
}
selectables.forEach(item => item.elem.classList.remove("intersected"));
addEventListener("pointermove", resize);
addEventListener("pointerup", () => {
removeEventListener("pointermove", resize);
div.remove();
});
}
html, body {
display: inline-grid;
justify-content: center;
width: 100%;
}
div {
width: 128px;
height: 32px;
}
.drag-select {
background-color: rgba(20, 137, 189, 0.5);
}
.intersected {
border: 5px solid black;
box-sizing: border-box;
}