puppeteer:等待元素可见?

Viv*_*ipo 29 javascript node.js google-chrome-devtools puppeteer

我想知道我是否可以告诉木偶操作员等到显示的元素.

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();
Run Code Online (Sandbox Code Playgroud)

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

tur*_*uka 32

我认为你可以page.waitForSelector(selector[, options])为此目的使用功能.

const puppeteer = require('puppeteer');

puppeteer.launch().then(async browser => {
  const page = await browser.newPage();
  page
    .waitForSelector('#myId')
    .then(() => console.log('got it');
  browser.close();
});
Run Code Online (Sandbox Code Playgroud)

  • 这是一个很好的答案?代码片段甚至没有网页! (4认同)
  • `waitForXPath()` ? (2认同)
  • @shafeen我提出了一个片段编辑工作,但它还没有被接受:/我认为该片段仍然相关,因为它演示了如何使用`page.waitForSelector` (2认同)

fin*_*inn 18

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

page.waitForSelector('#myId', {visible: true})
Run Code Online (Sandbox Code Playgroud)

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

  • 在我的情况下,我需要相反的`page.waitForSelector('#myId', {hidden: true})` 来等待加载器被隐藏后再继续 (4认同)

eww*_*ink 17

注意,以上所有答案均不正确!

因为它回答一个元素,如果存在或位于 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();
Run Code Online (Sandbox Code Playgroud)

说明

如果具有CSS属性,则页面DOM上存在的元素并不总是可见的,display:none或者visibility:hidden为什么使用page.waitForSelector(selector)不是一个好主意,让我们在下面的代码片段中看到不同的内容。

// 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();
Run Code Online (Sandbox Code Playgroud)
function isExist(selector) {
  let el = document.querySelector(selector);
  let exist = el.length != 0 ? 'Exist!' : 'Not Exist!';
  console.log(selector + ' is ' + exist)
}

function isVisible(selector) {
  let el = document.querySelector(selector).clientHeight;
  let visible = el != 0 ? 'Visible, ' + el : 'Not Visible, ' + el;
  console.log(selector + ' is ' + visible + 'px')
}

isExist('#idA');
isVisible('#idA');
console.log('=============================')
isExist('#idB')
isVisible('#idB')
Run Code Online (Sandbox Code Playgroud)
.bd {border: solid 2px blue;}
Run Code Online (Sandbox Code Playgroud)

在上面的代码片段上,isExist()模拟

page.waitForSelector('#myId');
Run Code Online (Sandbox Code Playgroud)

我们可以看到,同时运行isExist()两个元素#idA#idB是回报存在。

但是在运行时isVisible()#idA看不见或布局。

这里还有其他对象来检查元素是否显示或使用CSS属性display

scrollWidth
scrollHeight
offsetTop
offsetWidth
offsetHeight
offsetLeft
clientWidth
clientHeight
Run Code Online (Sandbox Code Playgroud)

visibility用not 进行样式检查hidden

注意:我的Java语言或英语不佳,请随时改善此答案。

  • 绝对正确!然而,在 puppeteer 中实际上有一个选项。看我的回答。 (3认同)

lus*_*chn 11

更新了一些优化的答案:

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();
})();
Run Code Online (Sandbox Code Playgroud)


Gra*_*ler 7

您可以使用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 );
Run Code Online (Sandbox Code Playgroud)

注:在参照框架,你也可以使用frame.waitFor(),frame.waitForSelector()frame.waitForXPath().

  • 是的,确实如此,但现在它也已被弃用。 (3认同)

Mos*_*sho 5

虽然我同意@ewwink 的回答。Puppeteer 的 API 默认检查未隐藏,因此当您执行以下操作时:

await page.waitForSelector('#id', {visible: true})
Run Code Online (Sandbox Code Playgroud)

你不会被 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()
}
Run Code Online (Sandbox Code Playgroud)

在我自己写了一些这样的方法之后,我发现expect- puppeteer可以做到这一点并且做得更好(参见toMatchElement)。