jquery - Detecting if an element is visible on the page with JavaScript or Puppeteer - Stack Overflow

admin2025-04-18  0

I'm building a framework that crawls websites hosted on our infrastructure to verify there is nothing against our policies. If there is content prohibited, we are taking a screenshot of it among other details.

Obviously, you can't take screenshot if the element is not visible due to being inside of an overflow: hidden parent or because there is an absolute element positioned above it.

Is there a way in 2019 to know if an element can be visible to the human eye?

Since it's inside Puppeteer I can use either native Puppeteer API or whatever JavaScript library needed since I can inject it to the page.

Example of the problem:

$('#above').html(`You can see green: ${$('#below').is(':visible')}. But can you really?`)
#above {
  width: 600px;
  height: 600px;
  position: absolute;
  background-color: red;
  text-align: center;
  font-size: 30px;
  padding: 30px;
}

#below {
  width: 440;
  height: 200;
  background-color: red;
}
<script src=".3.1/jquery.min.js"></script>

<div id="above"></div>
<div id="below"></div>

I'm building a framework that crawls websites hosted on our infrastructure to verify there is nothing against our policies. If there is content prohibited, we are taking a screenshot of it among other details.

Obviously, you can't take screenshot if the element is not visible due to being inside of an overflow: hidden parent or because there is an absolute element positioned above it.

Is there a way in 2019 to know if an element can be visible to the human eye?

Since it's inside Puppeteer I can use either native Puppeteer API or whatever JavaScript library needed since I can inject it to the page.

Example of the problem:

$('#above').html(`You can see green: ${$('#below').is(':visible')}. But can you really?`)
#above {
  width: 600px;
  height: 600px;
  position: absolute;
  background-color: red;
  text-align: center;
  font-size: 30px;
  padding: 30px;
}

#below {
  width: 440;
  height: 200;
  background-color: red;
}
<script src="https://cdnjs.cloudflare./ajax/libs/jquery/3.3.1/jquery.min.js"></script>

<div id="above"></div>
<div id="below"></div>

(Yes, I saw this question was asked, but it was 7 years ago and much was changed since and I can also use puppeteer which in his case was not possible).

Share edited Feb 28, 2020 at 11:02 hardkoded 21.8k3 gold badges61 silver badges74 bronze badges asked May 8, 2019 at 15:05 gdorongdoron 150k59 gold badges302 silver badges371 bronze badges 2
  • Is there a way in 2019 to know if an element can be visible to the human eye? Definitely not with 100% certainty. – leonheess Commented May 8, 2019 at 15:08
  • @MiXT4PE well, 50% is better than 40%, so whatever improves the odds is helpful to me. – gdoron Commented May 8, 2019 at 15:13
Add a ment  | 

4 Answers 4

Reset to default 2

I never did this, but the hover function will fail if the element is not visible or "hoverable". You could do something like this:

let error = null;
await page.hover('yourSelector').catch(e => error = e);
if (!error) {
   //The element should be visible here.
}

I found a way (thanks @hardkoded for planting the idea in my head...)
I didn't test it on all edge cases, but it does seem to work on my example.

Basically I'm adding an eventListener for mouseover event that sets a boolean flag and then calling puppeteer's hover function that moves the mouse to the center of the element.
If the flag is set, the element is visible.

await page.goto('c:/temp/code.html')
const el = await page.$('#below')
await page.evaluate(el => {
  el.addEventListener('mouseover', function () {
    this.setAttribute('mouseover-worked', 'true')
    console.log('hovered!!!')
  })
}, el)
await el.hover()
const hovering_works = await page.evaluate(el => el.getAttribute('mouseover-worked'), el)
console.log(hovering_works)

If someone finds a flaw, please let me know...

There is an easy way built into the puppeteer library to check this. You can give a { visible: false } option to the page.waitForSelector function that will check or actually wait until an element is visible:

const element = await page.waitForSelector('#selector', { visible: true });

This will do a decent check. See the full code of the function here. It will check the following:

  • Does the element exist?
  • Is the element not hidden (style.visibility !== 'hidden') according to window.getComputedStyle?
  • Has the element a height/width/top/bottom greater than 0 according to element.getBoundingClientRect()?

The latter case would also cover your example code as the #below element should be having a height of 0. So it's a decent check but you will always be able to construct cases in which the code does not work. If you want to cover more edge cases, you can also expand the approach of the puppeteer devs.

Full code sample

As the function will not resolve if the element does not exist, you have to use a small helper Promise to timeout the check:

const element = await Promise.race([
    new Promise(resolve => setTimeout(() => resolve(), 200)), // resolves without value after 200ms
    page.waitForSelector('#selector', { visible: true })
]);
if (element) {
    // element is visible
}

Maybe you can using elementHandle.boundingBox()

It will return a Promise that show a bounding box of the element (relative to the main frame), or null if the element is not visible.

The snippet example:

      const loadMoreButton = await getDataPage.$(
        'button.ao-tour-reviews__load-more-cta.js-ao-tour-reviews__load-more-cta'
      );

      const buttonVisible = await loadMoreButton.boundingBox();

      if (buttonVisible) {
        await loadMoreButton.click().catch((e) => {
          console.log('
转载请注明原文地址:http://conceptsofalgorithm.com/Algorithm/1744949655a276255.html

最新回复(0)