Make both overlapping divs clickable?

Viewed 229

Is it possible to make two overlapping divs, both clickable?

I've appended divs to two containers, #container and #container2. Their styles are exactly the same only except one is flex-direction: column; and one is flex-direction: column;. Both position:absolute with #container2 on top. I made each of the appended child clickable to fill its background color. Only the div on top is clickable so far, is there a way to make both clickable? or is there another way to have the bottom div react to my clicks?

window.addEventListener('load', init);

function init() {
  calculateGrid();

  //calculate grid
  function calculateGrid() {

    var w = window.innerWidth;
    var h = window.innerHeight;

    var totalNum = Math.trunc(w / 25) * Math.trunc(h / 25);

    function randomInRange(from, to) {
      let x = Math.random() * (to - from);
      return x + from;
    };

    for (var i = 0; i < totalNum; i++) {
      var div = document.createElement('div');
      div.setAttribute('class', 'grid');
      div.style.width = randomInRange(3, 10) + 'vw';
      div.style.height = randomInRange(5, 10) + 'vh';
      document.getElementById('container').appendChild(div);
      document.getElementById('container2').appendChild(div.cloneNode(true));
    }
  };

  $(".grid").click(function() {
    $(this).toggleClass('selected');
  });

};
#container {
  width: 100vw;
  height: 95vh;
  position: absolute;
  display: flex;
  flex-wrap: wrap;
  align-content: flex-start;
  flex-direction: column;
  overflow: hidden;
}

#container .grid {
  border: 1px solid blue;
}

#container2 {
  width: 100vw;
  height: 95vh;
  position: absolute;
  display: flex;
  flex-wrap: wrap;
  align-content: flex-start;
  flex-direction: row;
  overflow: hidden;
}

#container2 .grid {
  border: 1px solid red;
}

.grid {
  font-size: 10px;
  color: white;
}

#container .selected {
  background-color: blue;
}

#container2 .selected {
  background-color: red;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
<div id="wrapper">
  <div id="container"></div>
  <div id="container2"></div>
</div>

View on CodePen

2 Answers

One method is to use Document.elementsFromPoint() to return "an array of all elements at the specified coordinates". Iterate through that array, adding the "selected" class to "grid" elements.

window.addEventListener('load', init);

function init() {

  // build grid
  function calculateGrid() {

    var w = window.innerWidth;
    var h = window.innerHeight;

    var totalNum = Math.trunc(w / 25) * Math.trunc(h / 25);

    function randomInRange(from, to) {
      let x = Math.random() * (to - from);
      return x + from;
    };

    for (var i = 0; i < totalNum; i++) {
      var div = document.createElement('div');
      div.setAttribute('class', 'grid');
      div.style.width = randomInRange(3, 10) + 'vw';
      div.style.height = randomInRange(5, 10) + 'vh';
      document.getElementById('container1').appendChild(div);
      document.getElementById('container2').appendChild(div.cloneNode(true));
    }

  };

  // handle grid clicks
  function handleGridClick(e) {
    let elms = document.elementsFromPoint(e.clientX, e.clientY);
    Array.from(elms).forEach(elm => {
      if (elm.classList.contains('grid'))
        elm.classList.add('selected');
    });
  }

  // initialize grid and click handler
  calculateGrid();
  document.addEventListener('click', handleGridClick);

};
.container {
  width: 100vw;
  height: 95vh;
  position: absolute;
  display: flex;
  flex-wrap: wrap;
  align-content: flex-start;
  overflow: hidden;
}

#container1 {
  flex-direction: column;
}
#container1 .grid {
  border: 1px solid blue;
}
#container1 .grid.selected {
  background-color: blue;
}

#container2 .grid {
  border: 1px solid red;
}
#container2 .grid.selected {
  background-color: red;
}
<div id="wrapper">
  <div id="container1" class="container"></div>
  <div id="container2" class="container"></div>
</div>

You can't actually hover two items at the same time in plain 'ol HTML/CSS - for that you will need JavaScript as explained in the accepted solution. However, there's a CSS-only solution to allow hovering over the different layers, which was fun to figure out at the very least.

So the idea is that you have these invisible boxes on top of the visible ones. The invisible boxes only have borders such that any time your mouse hits a border, some clever z-index swapping takes place to make the visible containers change their stacking order.

For every .grid item you need to create a corresponding .box item: https://jsfiddle.net/ryanwheale/01v5yz86/93/

Related