[Solved] How can I check that an element is visible with Puppeteer and pure JavaScript?

I wish to check that a DOM element is visible with Puppeteer and pure JavaScript (not jQuery), how can I do this? By visible I mean that the element is displayed through CSS, and not hidden (f.ex. by display: none).

For example, I can determine whether my element #menu is not hidden via CSS rule display: none, in the following way:

const isNotHidden = await page.$eval('#menu', (elem) => {
  return elem.style.display !== 'none'
})

How can I determine in general though if the element is hidden or not, and not just through display: none?

Enquirer: aknuds1

||

Solution #1:

I found that Puppeteer has an API method for this purpose: Page.waitForSelector, via its visible option. I wasn’t aware of the latter option, but it lets you wait until an element is visible.

await page.waitForSelector('#element', {
  visible: true,
})

Conversely you can wait for an element to be hidden, via the hidden option.

I think this is the idiomatic answer, with regards to the Puppeteer API. Thanks to Colin Cline though as I think his answer is probably useful as a general JavaScript solution.

Respondent: aknuds1

Solution #2:

One is by checking its display style value.
Second is by checking its height, for exp if the element is a child of an element which is display: none, the offsetHeight will be 0 and thus you know the element is not visible despite its display value. opacity: 0 is not considered as hidden element so we will not checking it.

const isNotHidden = await page.$eval('#menu', (elem) => {
    return window.getComputedStyle(elem).getPropertyValue('display') !== 'none' && elem.offsetHeight
});

You can check elem.offsetWidth as well and is not bad before any calculation, check if element exist or not.

Respondent: Colin Cline

Solution #3:

Use boundingBox()

This method returns the bounding box of the element (relative to the main frame), or null if the element is not visible.

API: https://github.com/puppeteer/puppeteer/blob/master/docs/api.md#elementhandleboundingbox

Respondent: huypham

Solution #4:

The current accepted answer involves waiting for an element to appear and become visible.

If we are not interested in waiting on the element, and we would simply like to test the visibility of the element, we can use a combination of getComputedStyle() and getBoundingClientRect() to test whether or not the element is visible.

We can first check that the visibility is not set to hidden.

Then we can validate that the bounding box is visible by checking that the bottom, top, height, and width attributes are not set to 0 (this will filter out elements that have display set to none as well).

const element_is_visible = await page.evaluate(() => {
  const element = document.querySelector('#example');
  const style = getComputedStyle(element);
  const rect = element.getBoundingClientRect();

  return style.visibility !== 'hidden' && !!(rect.bottom || rect.top || rect.height || rect.width);
});
Respondent: Grant Miller

Solution #5:

Maybe you can using elementHandle.boundingBox() (thank to @huypham idea)

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('💥💥💥: ' + e)
        });
      }
Respondent: Thinh NV

Solution #6:

Apparently here’s how jQuery does it:

visible = await page.evaluate((e) => e.offsetWidth > 0 && e.offsetHeight > 0, element)
Respondent: pguardiario

Solution #7:

If you just want to know if an element is visible or not then you can use this function. You should make sure that the page is ready before calling this function. You can do that by using waitForSelector on other elements you expect to be visible.

async function isVisible(page, selector) {
  return await page.evaluate((selector) => {
    var e = document.querySelector(selector);
    if (e) {
      var style = window.getComputedStyle(e);

      return style && style.display !== 'none' && style.visibility !== 'hidden' && style.opacity !== '0';
    }
    else {
      return false;
    }
  }, selector);
}


// Example usage:
page.waitForSelector('#otherPeerElement');
var myElementIsVisible = await isVisible(page, '#visibleOrNot');

if (myElementIsVisible) {
// Interact with #visibleOrNot
}
Respondent: Wade

Solution #8:

this code definitely help you.
It basically means the element is already available on the page but is not visible yet or in CSS, the display property is set as none or visibility is hidden. Now, while writing our tests, we assume that as soon as the element is available, do an action on it like clicking or typing. But as this element is not yet visible, Puppeteer fails to perform that action.

async function isLocatorReady(element, page) {
  const isVisibleHandle = await page.evaluateHandle((e) => 
{
    const style = window.getComputedStyle(e);
    return (style && style.display !== 'none' && 
    style.visibility !== 'hidden' && style.opacity !== '0');
 }, element);
  var visible = await isVisibleHandle.jsonValue();
  const box = await element.boxModel();
  if (visible && box) {
    return true;
  }
  return false;
}
Respondent: ashish

Solution #9:

const firstName= await page.$('[name=firstName]')
expect(firstName!=null).equal(true)
Respondent: Salman Srabon

Solution #10:

based on playwright’s logic for checking if the element is visible – https://github.com/microsoft/playwright/blob/master/src/server/injected/injectedScript.ts#L120-L129

function isVisible(element: Element): boolean {
    // Note: this logic should be similar to waitForDisplayedAtStablePosition() to avoid surprises.
    if (!element.ownerDocument || !element.ownerDocument.defaultView)
      return true;
    const style = element.ownerDocument.defaultView.getComputedStyle(element);
    if (!style || style.visibility === 'hidden')
      return false;
    const rect = element.getBoundingClientRect();
    return rect.width > 0 && rect.height > 0;
  }
Respondent: Meir Blachman

Solution #11:

I would use @aknuds1 ‘s approach, but you can also do the following.

expect((await page.$('#element')) !== null).toEqual(true)

If you are fetching a resource asynchronously, be aware that the above expectation may not pass, since it won’t wait for the changes to reflect on the UI. That’s why this approach may not be preferred in this scenario.

Respondent: Antuan

The answers/resolutions are collected from stackoverflow, are licensed under cc by-sa 2.5 , cc by-sa 3.0 and cc by-sa 4.0 .

Leave a Reply

Your email address will not be published.