【问题标题】:puppeteer: how to wait until an element is visible?puppeteer:如何等到元素可见?
【发布时间】:2018-02-18 12:34:18
【问题描述】:

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

有什么办法可以做到吗?

【问题讨论】:

  • 关于模态的注意事项,以防万一(我知道这不是被问到的,但我觉得这是一个很常见的陷阱):淡入/淡出的模态元素的可见性很棘手。一个元素可以是可见的,但由于模态不透明等原因还不能点击。您可以禁用转换以进行测试,或者只注册显示/隐藏的钩子,在窗口上写入一个布尔变量,并在围绕模态交互的测试中等待正确的值.节省大量薄片。

标签: javascript node.js google-chrome-devtools puppeteer


【解决方案1】:

我认为您可以为此目的使用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();
});

要查看可用选项,请参阅 github link.

【讨论】:

  • waitForXPath() ?
  • @joy,你​​可以用page.waitFor,它用途广泛
  • 这是一个好的答案吗?代码 sn-p 甚至没有进入网页!
  • @shafeen 我建议对 sn-p 进行编辑,但它仍未被接受:/ 但是我认为 sn-p 仍然相关,因为它演示了@987654326 @可以用
  • 您不需要代码 sn-p 即可访问网页来回答此问题。在搜索waitForSelector@shafeen之前先学习puppeteer,如果它不进入网页,这个问题应该也是错误的,因为这个平台不是提供免费复制粘贴的代码而是学习。
【解决方案2】:

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

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

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

【讨论】:

  • 在我的情况下,我需要相反的 page.waitForSelector('#myId', {hidden: true}) 等到加载程序被隐藏后再继续
  • 如果元素的高度/宽度为 0,则此方法不起作用。请检查此答案以获取 stackoverflow.com/a/54103671/528468
【解决方案3】:

注意,直到今天提交的所有答案都不正确

因为如果存在或位于 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();

说明

Exist 在页面的 DOM 上的元素并不总是 Visible 如果有 CSS 属性 display:nonevisibility:hidden 那为什么使用 page.waitForSelector(selector) 不是一个好主意,让我们看看下面的 sn-p 中的不同。

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>

在sn-p上面的函数isExist()是模拟

page.waitForSelector('#myId');

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

但是在运行isVisible() 时,#idA 不可见或不显示。

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

scrollWidth
scrollHeight
offsetTop
offsetWidth
offsetHeight
offsetLeft
clientWidth
clientHeight

对于样式visibility,请检查不使用hidden

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

【讨论】:

  • 完全正确!然而,实际上在 puppeteer 中有一个选项。看我的回答。
  • @finn,除非下一页具有完全相同的选择器,否则您无法区分当前页面和下一页。 waitForFunction 允许你这样做。
  • 这对我有用
【解决方案4】:

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

// 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,您还可以使用frame.waitFor()frame.waitForSelector()frame.waitForXPath()

【讨论】:

  • 等待page.waitFor不是多余的吗?
  • 是的,现在它也被弃用了。
【解决方案5】:

通过一些优化更新了答案:

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

【讨论】:

    【解决方案6】:

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

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

    您不会被 CSS 隐藏和显示。 为确保渲染,您可以像 @ewwink 的 waitForFunction 那样做。但是,要完全回答您的问题,这里有一个使用 puppeteer 的 API 的 sn-p:

    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)。

    【讨论】:

      【解决方案7】:
      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})
      

      【讨论】:

        猜你喜欢
        • 2020-05-20
        • 2017-09-23
        • 2016-07-18
        • 2021-09-05
        • 2014-05-03
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-03-14
        相关资源
        最近更新 更多