Search code examples
javascriptdombounding-box

How to check if two elements are intersecting in vanilla JavaScript?


I have code like this:

$(function() {
  var $selection = $('.selection');
  $('li').filter(function() {
    var self = $(this);
    return /* ????? */;
  }).addClass('selected');
});
.widget {
  width: 320px;
  height: 200px;
  border: 1px solid gray;
  position: absolute;
  overflow: scroll;
}
.selection {
  position: absolute;
  top: 90px;
  left: 90px;
  border: 1px dotted black;
  height: 120px;
  width: 120px;
}
ul {
  list-style: none;
  margin: 0;
  padding: 0;
}
li {
  float: left;
  background: blue;
  width: 40px;
  height: 40px;
  margin: 10px;
}
li.selected {
  background: red;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="widget">
  <div class="selection"></div>
  <ul>
    <li></li>
    <li></li>
    <li></li>
    <li></li>
    <li></li>
    <li></li>
    <li></li>
    <li></li>
    <li></li>
    <li></li>
    <li></li>
    <li></li>
    <li></li>
    <li></li>
    <li></li>
    <li></li>
    <li></li>
    <li></li>
    <li></li>
    <li></li>
  </ul>
</div>

How can I select only those li elements that are intersecting with .selection rectangle?


Solution

  • Using standard DOM techniques, you can iterate over each LI element and obtain a bounding rectangle, which gives the coordinates of the LI's rectangle.

    Do this for the selection rectangle too and then you can simply check whether the coordinates are within the range of the selection's.

    Element.getBoundingClientRect()

    The Element.getBoundingClientRect() method returns the size of an element and its position relative to the viewport.

    The returned value is a DOMRect object which is the union of the rectangles returned by getClientRects() for the element, i.e., the CSS border-boxes associated with the element.

    The returned value is a DOMRect object, which contains read-only left, top, right, bottom, x, y, width, height properties describing the border-box in pixels. Properties other than width and height are relative to the top-left of the viewport.

    Please see below, the edited code which selects the LI elements that are fully contained or partially intersecting the selection.

    var selection = document.querySelector(".selection");
    var rectSelection = selection.getBoundingClientRect();
    
    // Iterate over all LI elements.
    [].forEach.call(document.querySelectorAll("li"), function(li) {
        var rect = li.getBoundingClientRect();
    
        if(rect.bottom > rectSelection.top 
        && rect.right > rectSelection.left 
        && rect.top < rectSelection.bottom 
        && rect.left < rectSelection.right) {
            li.classList.add("selected");
        }
    });
    .widget {
      width: 320px;
      height: 200px;
      border: 1px solid gray;
      position: absolute;
      overflow: scroll;
    }
    .selection {
      position: absolute;
      top: 90px;
      left: 90px;
      border: 1px dotted black;
      height: 120px;
      width: 120px;
    }
    ul {
      list-style: none;
      margin: 0;
      padding: 0;
    }
    li {
      float: left;
      background: blue;
      width: 40px;
      height: 40px;
      margin: 10px;
    }
    li.selected {
      background: red;
    }
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
    <div class="widget">
      <div class="selection"></div>
      <ul>
        <li></li>
        <li></li>
        <li></li>
        <li></li>
        <li></li>
        <li></li>
        <li></li>
        <li></li>
        <li></li>
        <li></li>
        <li></li>
        <li></li>
        <li></li>
        <li></li>
        <li></li>
        <li></li>
        <li></li>
        <li></li>
        <li></li>
        <li></li>
      </ul>
    </div>