javascript puppeteer:如何等待直到一个元素可见?

cqoc49vn  于 2023-01-29  发布在  Java
关注(0)|答案(8)|浏览(334)

我想知道我是否可以告诉 puppet 师等待,直到一个元素显示。

const inputValidate = await page.$('input[value=validate]');
await inputValidate.click()
        
// I want to do something like that 
waitElemenentVisble('.btnNext ')

const btnNext = await page.$('.btnNext');
await btnNext.click();

我有什么办法可以做到这一点吗?

2skhul33

2skhul331#

我认为您可以使用page.waitForSelector(selector[, options])函数来实现此目的。

const puppeteer = require('puppeteer');

puppeteer.launch().then(async browser => {

   const browser = await puppeteer.launch({executablePath: "C:\\Program Files\\Google\\Chrome\\Application\\chrome.exe", headless: false});
   const page = await browser.newPage();
   await page.setUserAgent(options.agent);
   await page.goto("https://www.url.net", {timeout: 60000, waitUntil: 'domcontentloaded'});
   
   page
    .waitForSelector('#myId')
    .then(() => console.log('got it'));
    browser.close();
});

要检查可用选项,请参见github link.

1bqhqjot

1bqhqjot2#

如果要确保元素实际可见,则必须使用

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

否则,您只是在DOM中查找元素,而不检查可见性。

brtdzjyr

brtdzjyr3#

    • 注意,截至今天提交的所有答案均不正确**

因为如果存在或已定位but NOT可见或已显示,它将回答元素
正确答案是使用page.waitFor()page.waitForFunction()检查元素大小或可见性,请参见下面的说明。

// wait until present on the DOM
// await page.waitForSelector( css_selector );
// wait until "display"-ed
await page.waitForFunction("document.querySelector('.btnNext') && document.querySelector('.btnNext').clientHeight != 0");
// or wait until "visibility" not hidden
await page.waitForFunction("document.querySelector('.btnNext') && document.querySelector('.btnNext').style.visibility != 'hidden'");

const btnNext = await page.$('.btnNext');
await btnNext.click();

解释

如果页面的DOM上存在的元素具有CSS属性display:nonevisibility:hidden,则该元素不总是可见的,这就是为什么使用page.waitForSelector(selector)不是好主意的原因,让我们在下面的代码片段中查看不同之处。
x一个一个一个一个x一个一个二个一个x一个一个三个一个
在上面的代码片段中,函数isExist()是模拟的

page.waitForSelector('#myId');

我们可以看到,当对两个元素#idA#idB运行isExist()时,返回存在。
但运行isVisible()时,#idA不可见或显示。
这里还有其他对象来检查元素是否显示或使用CSS属性display

scrollWidth
scrollHeight
offsetTop
offsetWidth
offsetHeight
offsetLeft
clientWidth
clientHeight

对于样式visibility,请检查而不是hidden
注意:我不擅长Javascript或英语,请随时改进这个答案。

fjaof16o

fjaof16o4#

您可以使用page.waitFor()page.waitForSelector()page.waitForXPath()来等待页面上的元素:

// Selectors

const css_selector = '.btnNext';
const xpath_selector = '//*[contains(concat(" ", normalize-space(@class), " "), " btnNext ")]';

// Wait for CSS Selector

await page.waitFor(css_selector);
await page.waitForSelector(css_selector);

// Wait for XPath Selector

await page.waitFor(xpath_selector);
await page.waitForXPath(xpath_selector);

**注:**在引用帧时,也可以使用frame.waitFor()frame.waitForSelector()frame.waitForXPath()

ykejflvf

ykejflvf5#

更新答案,进行一些优化:

const puppeteer = require('puppeteer');

(async() => {
    const browser = await puppeteer.launch({headless: true});
    const page = await browser.newPage();

    await page.goto('https://www.somedomain.com', {waitUntil: 'networkidle2'});
    await page.click('input[value=validate]');
    await page.waitForSelector('#myId');
    await page.click('.btnNext');
    console.log('got it');

    browser.close();
})();
r6vfmomb

r6vfmomb6#

虽然我同意@ewwink的回答。Puppeteer的API默认检查不隐藏,所以当你这样做:

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

你不会被CSS隐藏和显示。为了确保渲染效果,你可以像@ewwink的waitForFunction那样做。然而,为了完全回答你的问题,这里有一个使用puppeteer的API的片段:

async waitElemenentVisble(selector) {
  function waitVisible(selector) {
    function hasVisibleBoundingBox(element) {
      const rect = element.getBoundingClientRect()
      return !!(rect.top || rect.bottom || rect.width || rect.height)
    }
    const elements = [document.querySelectorAll(selector)].filter(hasVisibleBoundingBox)
    return elements[0]
  }
  await page.waitForFunction(waitVisible, {visible: true}, selector)
  const jsHandle = await page.evaluateHandle(waitVisible, selector)
  return jsHandle.asElement()
}

在我自己编写了一些类似的方法之后,我发现expect-puppeteer可以做得更好(请参见toMatchElement)。

3xiyfsfu

3xiyfsfu7#

async function waitForVisible (selector){
    //const selector = '.foo';
  return  await page.waitForFunction(
      (selector) => document.querySelector(selector) && document.querySelector(selector).clientHeight != 0",
      {},
      selector
    );
}

上述函数使其具有通用性,以便您可以在任何地方使用它。
但是,如果您使用的是pptr,还有另一种更快更简单的解决方案:
https://pptr.dev/#?product=Puppeteer&version=v10.0.0&show=api-pagewaitforfunctionpagefunction-options-args

page.waitForSelector('#myId', {visible: true})
ev7lccsx

ev7lccsx8#

刚刚通过浏览一个健身网站进行了测试。@ewwink、@0fnt和@caram提供了最完整的答案。
仅仅因为DOM元素可见并不意味着它的内容已经完全填充。
今天,我跑了:

await page.waitForSelector("table#some-table", {visible:true})
const data = await page.$eval("table#some-table",(el)=>el.outerHTML)
console.log(data)

并且错误地接收到以下内容,因为表DOM没有被运行时完全填充。您可以看到外部HTML是空的。
user@env:$ <table id="some-table"></table>
添加1秒的暂停可以修复此问题,这可能是意料之中的:

function sleep(ms) {
    return new Promise(resolve => setTimeout(resolve, ms));
}

await page.waitForSelector("table#some-table", {visible:true})
await sleep(1000)
const data = await page.$eval("table#some-table",(el)=>el.outerHTML)
console.log(data)

user@env:$ <table id="some-table"><tr><td>Data</td></tr></table>
但@ewwink的回答也是如此,更优雅(没有人为的超时):

await page.waitForSelector("table#some-table", {visible:true})
await page.waitForFunction("document.querySelector('table#sched-records').clientHeight != 0")
const data = await page.$eval("table#some-table",(el)=>el.outerHTML)
console.log(data)

user@env:$ <table id="some-table"><tr><td>Data</td></tr></table>

相关问题