question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

waitForSelector with visible:true not returning the first visible element, causes timeout.

See original GitHub issue

Steps to reproduce

Tell us about your environment:

What steps will reproduce the problem? Open Website OR use this file:

<html>
  <head>
    <meta charset="UTF-8" />
  </head>

  <body>
    <input style="display: none" type="number" class="my-input-class" />
    <input type="number" class="my-input-class" />
  </body>
</html>

Please include code that reproduces the issue.

const browser = await puppeteer.launch();

const page = await browser.newPage();
await page.goto('https://j4q389wzv3.codesandbox.io/');

try {
  const visibleInput = await page.waitForSelector('.my-input-class', {visible: true, timeout: 1000 })
  console.log('Found visible Element')
} catch (e) {
  console.log('Could NOT find a visible element ', e.message)
}

const inputs = await page.$$('.my-input-class')
console.log(`Found ${inputs.length} inputs`)

await browser.close()

What is the expected result? There are 2 elements matching the CSS Selector on the page. the first one is hidden, the second one is visible. The page.waitForSelector with {visible: true} should have found and returned the visible element on the page.

What happens instead? It Times Out since there was another HIDDEN element matching the CSS selector higher up in the DOM structure, causing it to wait until timeout even though a visible element exists on the page.

Issue Analytics

  • State:open
  • Created 4 years ago
  • Reactions:7
  • Comments:16 (2 by maintainers)

github_iconTop GitHub Comments

5reactions
razorman8669commented, Apr 28, 2019

A working solution (though, not ideal) is as follows. Ideally the waitForSelector with { visible:true } would behave this way… or the documentation clearly states that it doesn’t do this.

/** Internal method to determine if an elementHandle is visible on the page. */
const _isVisible = async(page, elementHandle) => await page.evaluate((el) => {
  if (!el || el.offsetParent === null) {
    return false;
  }

  const style = window.getComputedStyle(el);
  return style && style.display !== 'none' && style.visibility !== 'hidden' && style.opacity !== '0';
}, elementHandle);

/**
 * Checks if an element with selector exists in DOM and is visible.
 * @param {*} page
 * @param {*} selector CSS Selector.
 * @param {*} timeout amount of time to wait for existence and visible.
 */
const waitForVisible = async(page, selector, timeout=25) => {
  const startTime = new Date();
  try {
    await page.waitForSelector(selector, { timeout: timeout });
    // Keep looking for the first visible element matching selector until timeout
    while (true) {
      const els = await page.$$(selector);
      for(const el of els) {
        if (await _isVisible(page, el)) {
          console.log(`PASS Check visible : ${selector}`);
          return el;
        }
      }
      if (new Date() - startTime > timeout) {
        throw new Error(`Timeout after ${timeout}ms`);
      }
      page.waitFor(50);
    }
  } catch (e) {
    console.log(`FAIL Check visible : ${selector}`);
    return false;
  }
};
4reactions
aslushnikovcommented, Jun 1, 2019

The page.waitForSelector with {visible: true} should have found and returned the visible element on the page.

@razorman8669 Ok I see what you mean here. Indeed, the behavior is somewhat confusing.

However, I disagree with the proposed approach. CSS selector is the only thing that we should use to identify the element. Adding the suggested logic adds magic to how we find elements - making it equally hard to debug.

The best solution here is to update the documentation - so that we do a better job explaining what’s going on.

Read more comments on GitHub >

github_iconTop Results From Across the Web

puppeteer: how to wait until an element is visible?
I think you can use page.waitForSelector(selector[, options]) function for that purpose. const puppeteer = require('puppeteer'); ...
Read more >
Page.waitForSelector() method - Puppeteer
Wait for the selector to appear in page. If at the moment of calling the method the selector already exists, the method will...
Read more >
Puppeteer documentation - DevDocs
The method runs document.querySelector within the page. If no element matches the selector, the return value resolves to null . Shortcut for page.mainFrame()....
Read more >
API Reference — Pyppeteer 0.0.25 documentation
Non visible pages, such as "background_page" , will not be listed here. ... If browser instance is created by pyppeteer.launcher.connect() , return None...
Read more >
How to make puppeteer wait for page to load - Urlbox
We can use Page.waitForSelector() like so, the {visible: true} option tells Puppeteer to wait for the element to be present in the DOM...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found