0

Is there any method or JavaScript library that can see if an element is really fully visible? As an example, suppose you need some UI button shown between certain elements and not take up more space on the page, as on this example: https://jsfiddle.net/akyh7e5f/

html:

<div style="width:50%; display:inline-block">
<div class="a">
<div>
Some content here.
</div>
<div class="inserter">
insertme
</div>
</div>
<div class="b">
 <div>
 some other content
 </div>
</div>
</div>

<div style="width:49%; display:inline-block">
<div class="a" style="z-index:1">
<div>
Some content here.
</div>
<div class="inserter">
insertme
</div>
</div>
<div class="b" style="z-index:2">
 <div>
 some other content
 </div>
</div>
</div>

css:

.a {
  height:80%;
  background: blue;
  position:relative;
}
.b {
  padding:30px;
  background:green;
  position:relative;
}

.inserter {
  position:absolute; z-index:9999;
  left:50%; margin-left:-20px;
  background: red;
}

In one case the element is inside and shows, in the other case it is under because some other element has more zindex than a container of the insertme element. I could make it body > element direct child, but then EVERY change to the height of the element would required a change to the position.

You could move the red element up one dom element until it is after the element and visible, then do some calculations for top/left,

or, perhaps more performant, change it from absolute to relative and make it take up space, in the second case it will not show (which is not as great from a design perspective).

In either of these two cases, I need to know whether the element (red element in this fiddle) is actually visible and clickable with no element above it. Is there any library that can find this?

Please do not reply with a specific suggestion for the jsfiddle - this is a minimal test case, assume many different styles may theme the page in any way, not to mention user-added styles. Also please do not mention Selenium as this has to be a browser-only dynamic app.

NoBugs
  • 9,310
  • 13
  • 80
  • 146
  • @guradio You clearly haven't read the question. – ceejayoz Jan 18 '17 at 02:26
  • 1
    I see an example that might help here: http://stackoverflow.com/questions/5598953/find-elements-that-are-stacked-under-visually-an-element-in-jquery ...could be a slow way to do it. Hope this helps. – Leon Bambrick Jan 18 '17 at 02:31
  • @LeonBambrick is pretty closer to the question, also you might find this helpful. http://stackoverflow.com/questions/4230029/jquery-javascript-collision-detection – Deepak Bandi Jan 18 '17 at 02:35

2 Answers2

5

Can use elementFromPoint() to test if the element you want is the top element at it's offset coordinates.

This is not a full test for your issue but should help give you part of it

$('.inserter').each(function(){
   var offset= $(this).offset();
   var topEl = document.elementFromPoint(offset.left, offset.top);
   console.log(topEl == this)

})

Note I only checked the top left corner, you would probably want to see if more of it is displayed than just that corner.

DEMO

charlietfl
  • 170,828
  • 13
  • 121
  • 150
0

For puppeteer I needed this and used @charlietfl answer to do it but with more checks

    await page.waitForFunction(
      (browserSelector) => {
        const browserElement = document.querySelector(browserSelector);
        if (!browserElement) {
          console.log(`Cannot find element "${browserSelector}"`); // eslint-disable-line no-console
          return false;
        }

        const boundingBox = browserElement.getBoundingClientRect();
        if (boundingBox.width === 0) {
          console.log(`Element "${browserSelector}" has no width`); // eslint-disable-line no-console
          return false;
        }
        if (boundingBox.height === 0) {
          console.log(`Element "${browserSelector}" has no height`); // eslint-disable-line no-console
          return false;
        }

        const clickTarget = document.elementFromPoint(
          boundingBox.left + boundingBox.width / 2, // Horizontal center
          boundingBox.top + boundingBox.height / 2, // Vertical center
        );
        if (!browserElement.contains(clickTarget)) {
          console.log(`Element "${browserSelector}" is obstructed`); // eslint-disable-line no-console
          return false;
        }
        return true;
      },
      {}, // No options for waitForFunction
      selector,
    );
Aalex Gabi
  • 1,525
  • 1
  • 18
  • 32