Javascript puppeteer :等待元素可见?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/46135853/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-23 03:20:34  来源:igfitidea点击:

puppeteer : wait an element is visible?

javascriptnode.jsgoogle-chrome-devtoolspuppeteer

提问by Vivien Pipo

I would like to know if I can tell puppeteer to wait until an element in displayed.

我想知道我是否可以告诉 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();

Is there any way I can accomplish this?

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

回答by turmuka

I think you can use page.waitForSelector(selector[, options])function for that purpose.

我认为您可以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();
});

To check the options avaible, please see the github link.

要检查可用的选项,请参阅github 链接。

回答by finn

If you want to ensure the element is actually visible, you have to use

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

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

Otherwise you are just looking for the element in the DOM and not checking for visibility.

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

回答by ewwink

Note, All the answers submitted until today are incorrect

注意,直到今天提交的所有答案都是错误的

Because it answer for an element if Exist or Locatedbut NOTVisible or Displayed

因为如果Exist 或 Location Visible 或 Displayed,它会回答一个元素but NOT

The right answer is to check an element size or visibility using page.waitFor()or page.waitForFunction(), see explaination below.

正确的答案是使用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();

Explanation

解释

The element that Exist on the DOM of page not always Visible if has CSS property display:noneor visibility:hiddenthat why using page.waitForSelector(selector)is not good idea, let see the different in the snippet below.

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

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')
.bd {border: solid 2px blue;}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="bd">
  <div id="idA" style="display:none">#idA, hidden element</div>
</div>
<br>
<div class="bd">
  <div id="idB">#idB, visible element</div>
</div>

on the snippet above the function isExist()is simulate

在上面的代码片段中,函数isExist()是模拟

page.waitForSelector('#myId');

and we can see while running isExist()for both element #idAan #idBis return exist.

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

But when running isVisible()the #idAis not visible or dislayed.

但是,当运行isVisible()#idA不可见或dislayed。

And here other objects to check if an element is displayed or using CSS property display.

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

scrollWidth
scrollHeight
offsetTop
offsetWidth
offsetHeight
offsetLeft
clientWidth
clientHeight

for style visibilitycheck with not hidden.

用于样式visibility检查 not hidden

note: I'm not good in Javascript or English, feel free to improve this answer.

注意:我不擅长 Javascript 或英语,请随时改进此答案。

回答by Grant Miller

You can use page.waitFor(), page.waitForSelector(), or page.waitForXPath()to wait for an element on a page:

您可以使用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);

Note:In reference to a frame, you can also use frame.waitFor(), frame.waitForSelector(), or frame.waitForXPath().

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

回答by luschn

Updated answer with some optimizations:

更新了一些优化的答案:

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();
})();

回答by Moshisho

While I agree with @ewwink answer. Puppeteer's API checks for not hidden by default, so when you do:

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

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

You get not hidden and visible by CSS. To ensure rendering you can do as @ewwink's waitForFunction. However to completely answer your question, here's a snippet using puppeteer's API:

你不会被 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()
}

After writing some methods like this myself, I found expect-puppeteerwhich does this and more better (see toMatchElement).

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