5

Can javascript tell if element is on top other element?

Consider this markup:

<!doctype html>
<html>
<style>
section div {width: 100px; position: absolute; border: 1px solid black; }
</style>
<body>
    <section>
        <div id="div1" style="height: 400px; background: blue;"></div>
        <div id="div2" style="height: 300px; background: red;"></div>
        <div id="div3" style="height: 200px; background: yellow;"></div>
        <div id="div4" style="height: 100px; background: green;"></div>
    </section>
</body>
</html>

How can I know (for example) if there is something under div3?

A. Meshu
  • 4,053
  • 2
  • 20
  • 34
  • Do you want just a collision detection (something is touching `div3`), or checking all elements for positions? – Jack Bashford Feb 20 '19 at 22:45
  • 1
    @JackBashford yes but just inside this particular section and just for divs. – A. Meshu Feb 20 '19 at 22:48
  • When you say under, are you referring to it being a sibling element ,or under, as in div3 is visually obscuring some other element? – Evan Trimboli Feb 20 '19 at 22:54
  • you can get the bounding box of the div3 using getBoundingClientRect() and loop through all the position absolute element, get their bounding box and check if it is inside the bounding box of div3 and has z-index greater than div3. Note: in case both of them have same z-index, div coming later in DOM tree would be above. – kanhai shah Feb 20 '19 at 22:54
  • @EvanTrimboli i mean visually under / on other div. – A. Meshu Feb 20 '19 at 22:56
  • this question https://stackoverflow.com/questions/3735278/how-to-get-a-list-of-all-elements-that-resides-at-the-clicked-point will probably help. Though instead of a click event you would use the div position for the coordinates. you can check `z-index` for visibility – andrew Feb 20 '19 at 22:58
  • @kanhaishah i didn't now that there is something like getBoundingClientRect(). i'm reading on it right now. Can you be kind and post example? – A. Meshu Feb 20 '19 at 23:00
  • you can read it here: https://developer.mozilla.org/en-US/docs/Web/API/Element/getBoundingClientRect . – kanhai shah Feb 20 '19 at 23:02
  • 1
    @kanhaishah this is exactly what i am reading right now. – A. Meshu Feb 20 '19 at 23:02
  • 1
    @RickHitchcock actually div4 is on top all other 3. – A. Meshu Feb 20 '19 at 23:10

4 Answers4

3

The code below will return true if something is underneath a div. In your example, it returns true for all divs except div1, because its height is larger than the others.

const isOnTop = (id) => {
  let element = document.querySelector(id),
      divs = document.querySelectorAll('section div');

  return [...divs].some(div =>
    div.getBoundingClientRect().bottom > element.getBoundingClientRect().bottom
  );
}

const isOnTop = (id) => {
  let element = document.querySelector(id),
      divs = document.querySelectorAll('section div');

  return [...divs].some(div =>
    div.getBoundingClientRect().bottom > element.getBoundingClientRect().bottom
  );
}

console.log(isOnTop('#div1'));  // false
console.log(isOnTop('#div2'));  // true
console.log(isOnTop('#div3'));  // true
console.log(isOnTop('#div4'));  // true
section div {
  width: 100px;
  position: absolute;
  border: 1px solid black;
}
<section>
  <div id="div1" style="height: 400px; background: blue;"></div>
  <div id="div2" style="height: 300px; background: red;"></div>
  <div id="div3" style="height: 200px; background: yellow;"></div>
  <div id="div4" style="height: 100px; background: green;"></div>
</section>
Rick Hitchcock
  • 35,202
  • 5
  • 48
  • 79
2

Here is the code which I think should work (it is a pseudo code)

var div3_position = document.getElementById('div3').getBoundingClientRect();
var divs = document.getElementsByTagName("div");
var inner_div_pos = null;
var div3_zindex = getStyle('div3', "zIndex");
var zInd = null;
for (var i = 0; i < divs.length; i++) {
    if (divs[i].id !== 'div3') {
        inner_div_pos = divs[i].getBoundingClientRect();
        zInd = getStyle(divs[i].id, "zIndex");
        if (!doesPointCollide(inner_div_pos) && zInd < div3_zindex) {
            console.log('element is under');
        }
    }
}

function doesPointCollide(p) {
    return !(p.x < div3_position.left || p.x > div3_position.right || p.y >
        div3_position.bottom || p.y < div3_position.top)
}

function getStyle(el, styleProp) {
    var x = document.getElementById(el);

    if (window.getComputedStyle) {
        var y = document.defaultView.getComputedStyle(x, null).getPropertyValue(styleProp);
    } else if (x.currentStyle) {
        var y = x.currentStyle[styleProp];
    }

    return y;
}
kanhai shah
  • 1,193
  • 10
  • 14
1

Okay, I didn't understand the behavior... beyond the correct answer, if someone wants to know if two elements overlap...

function overLaps(el1, el2) {
  const a = el1.getBoundingClientRect();
  const b = el1.getBoundingClientRect();

  if (a.top < b.top && a.bottom > b.top) return true;
  if (a.top < b.bottom && a.bottom > b.bottom) return true;
  if (a.left < b.left && a.left > b.left) return true;
  if (a.left < b.right && a.left > b.right) return true;

  return false;
}

Original Answer:

You'll need to check the elements from the parent/tree for zIndex, offset position and order. It will require a bit of recursion and depending on the depth may bit a bit cumbersome. If you are controlling the rendering/adjustments, it may be easier to do collision detection with your data model/controller.

Tracker1
  • 19,103
  • 12
  • 80
  • 106
  • Thank for your reply. i intentionally didn't set z-index because i want to change the position if the condition is true. If i figure how to know if there is div under it i will move the last one 100px right. – A. Meshu Feb 20 '19 at 23:13
  • So you want to know if there's an overlap between two elements in a common parent? You'll want to check the following properties. `offsetTop` `offsetLeft` `offsetHeight` `offsetWidth`, and if one is inside the other's range... between top and top+height and between left and left+width for the effective left/right values of the second element. – Tracker1 Feb 20 '19 at 23:22
  • The third line has a type - should be referencing el2 instead of el1: `const b = el2.getBoundingClientRect();` – TimBryanDev Aug 14 '23 at 10:17
0

Improving on @Tracker1 's response,

// Checks if two elements collide
const elementsColliding = function (el1, el2) {
    const a = el1.getBoundingClientRect()
    const b = el2.getBoundingClientRect()
    
    if (
        ((a.top < b.top) && ((a.top+a.height) > b.top)) ||
        ((a.bottom < b.bottom) && ((a.bottom+a.height) > b.bottom)) ||
        ((a.left < b.left) && ((a.left+a.width) > b.left)) ||
        ((a.right < b.right) && ((a.right+a.width) > b.right))
    ) {
        return true
    }

    return false
}

This checks if two elements collide any way. Not sure why original answer wasn't working for me.. But a.left < b.left && a.left > b.left isn't really doing nothing because it's an impossible statement. Also, the previous answer isn't checking if the elements touch partially (e.g el1 x-axis: 60 to 80, el2 y-axis 79 to 100)

Ricardo Vilaça
  • 846
  • 1
  • 7
  • 18