Select elements by dragging a box

by Génesis García Morilla

HTML

<ul></ul>

CSS

ul {
  list-style-type: none;
  display: flex;
  flex-flow: row wrap;
}

li {
  width: 80px;
  height: 80px;
  margin: 2px;
  font-size: 2rem;
  color: darkslategrey;
  display: flex;
  justify-content: center;
  align-items: center;
  user-select: none;
}

#box {
  position: absolute;
  left: 0;
  top: 0;
  border: 1px dotted darkslategrey;
}

.selected {
  background: #FECA40;
}

JavaScript

document.querySelector('ul').innerHTML = [...Array(1000).keys()]
  .reduce((sum, _, i) => sum + `<li>${i}</li>`, '')

// Select elements by dragging a box
const $$li = document.querySelectorAll('li')

document.onmousedown = ({ pageX: x0, pageY: y0 } = event) => {
  $$li.forEach($li => $li.classList.remove('selected'))

  let $box = document.createElement('div')
  $box.id = 'box'
  document.body.append($box)
  $box.style.left = `${x0}px`
  $box.style.top = `${y0}px`

  document.onmousemove = ({ pageX: x, pageY: y } = event) => {
    if (x0 > x) $box.style.left = `${x}px`
    if (y0 > y) $box.style.top = `${y}px`
    $box.style.width = `${Math.abs(x - x0)}px`
    $box.style.height = `${Math.abs(y - y0)}px`
  }

  document.onmouseup = ({ pageX: x, pageY: y } = event) => {
  	$box.remove()
    document.onmousemove = null
    
    // Swap variables to allow selections from all directions
    if (x0 > x) [x0, x] = [x, x0]
    if (y0 > y) [y0, y] = [y, y0]
		
    // First one
    let i = [...$$li].findIndex($li => {
    	let { left: a, top: b } = $li.getBoundingClientRect()
    	return a + scrollX + 40 > x0 && b + scrollY + 40 > y0
    })
    
    if (i < 0) return
    
    while (i < $$li.length) {
      let { left: a, top: b } = $$li[i].getBoundingClientRect()

      a += scrollX + 40
      b += scrollY + 40

      if (a > x0 && b > y0 && a < x && b < y)
        $$li[i].classList.add('selected')
      else if (b > y) break // last one

      i++
    }
  }
}