如何使用 Puppeteer 和纯 JavaScript 检查元素是不是可见?

Posted

技术标签:

【中文标题】如何使用 Puppeteer 和纯 JavaScript 检查元素是不是可见?【英文标题】:How can I check that an element is visible with Puppeteer and pure JavaScript?如何使用 Puppeteer 和纯 JavaScript 检查元素是否可见? 【发布时间】:2018-05-22 14:24:31 【问题描述】:

我希望使用Puppeteer 和纯 javascript(不是 jQuery)检查 DOM 元素是否可见,我该怎么做?可见是指元素通过 CSS 显示,而不是隐藏(例如 display: none)。

例如,我可以通过 CSS 规则 display: none 判断我的元素 #menu 是否未被隐藏,方法如下:

const isNotHidden = await page.$eval('#menu', (elem) => 
  return elem.style.display !== 'none'
)

我如何才能确定元素是否隐藏,而不仅仅是通过display: none

【问题讨论】:

我不知道它对其他隐藏方法的效果如何,但elem.getBoundingClientRect() 返回了您可以测试的独特数据。 @AuxTaco usingelem.getBoundingClientRect() 在console.log 上返回,无论元素是否准备好:( 由于Puppeteer总是可以运行正常的JS,规范线程Check if element is visible in DOM应该被链接,因为它有大量的资源可以通过Puppeteer中的.evaluate()使用。 @PayamB。您是否尝试返回JSON.stringify(elem.getBoundingClientRect())?它可能很重要的原因是 elem.getBounding... 是只读的 DOMRect 而不是普通对象,因此 Puppeteer 的序列化似乎受到影响并且没有捕获完整的对象。 【参考方案1】:

One 是通过检查其显示样式值。 第二是检查它的高度,如果元素是display: none元素的子元素,则offsetHeight将是0,因此您知道该元素不可见,尽管其显示值。 opacity: 0 不被视为隐藏元素,因此我们不会对其进行检查。

const isNotHidden = await page.$eval('#menu', (elem) => 
    return window.getComputedStyle(elem).getPropertyValue('display') !== 'none' && elem.offsetHeight
);

您也可以检查elem.offsetWidth,并且在任何计算之前都不错,检查元素是否存在。

【讨论】:

和"window" var在哪里定义? @pabgaran window 对象表示一个包含 DOM 文档的窗口; getComputedStyle。您可以将窗口缓存到变量 ofc 中,但它是一个全局对象 你是对的。我是木偶的初学者。所有这些代码都在“导航器”中运行,因此窗口、文档等始终可用。 这个解决方案节省了我的时间,除了我检查了元素的高度。【参考方案2】:

我发现 Puppeteer 有一个用于此目的的 API 方法:Page.waitForSelector,通过它的visible 选项。我不知道后一种选择,但它可以让您等到元素可见。

await page.waitForSelector('#element', 
  visible: true,
)

相反,您可以通过hidden 选项等待隐藏元素。

我认为这是关于 Puppeteer API 的惯用答案。感谢 Colin Cline,因为我认为他的回答可能作为通用 JavaScript 解决方案有用。

【讨论】:

如果元素不可见,这将抛出。如果元素不可见是完全可以接受的,那么投掷是不合适的。我希望开发人员停止为正常行为抛出异常,只返回一个类似 null 的值! @Gerry 这正是我的问题,我想检查元素,然后在元素尚未准备好时根据该元素做一些事情,但是在查找元素时它会抛出并导致应用程序崩溃跨度> 问题是如何测试一个元素是否可见,而不是等待它被添加到 DOM 中然后变得可见,这就是这个“接受”的解决方案所做的。【参考方案3】:

我会使用 @aknuds1 的方法,但您也可以执行以下操作。

expect((await page.$('#element')) !== null).toEqual(true)

如果您正在异步获取资源,请注意上述预期可能不会通过,因为它不会等待更改反映在 UI 上。这就是为什么这种方法在这种情况下可能不是首选的原因。

【讨论】:

@Gerry 你完全错了。在发表评论之前,请仔细阅读问题。 看来您的答案是期望使用 puppeteer 测试框架,例如 jest-puppeteer,而 OP 没有指定他们正在测试。【参考方案4】:

显然这是 jQuery 的做法:

visible = await page.evaluate((e) => e.offsetWidth > 0 && e.offsetHeight > 0, element)

【讨论】:

【参考方案5】:

如果你只是想知道一个元素是否可见,那么你可以使用这个函数。在调用此函数之前,您应该确保页面已准备就绪。您可以通过在您希望可见的其他元素上使用 waitForSelector 来做到这一点。

async function isVisible(page, selector) 
  return await page.evaluate((selector) => 
    var e = document.querySelector(selector);
    if (e) 
      var style = window.getComputedStyle(e);

      return style && style.display !== 'none' && style.visibility !== 'hidden' && style.opacity !== '0';
    
    else 
      return false;
    
  , selector);



// Example usage:
page.waitForSelector('#otherPeerElement');
var myElementIsVisible = await isVisible(page, '#visibleOrNot');

if (myElementIsVisible) 
// Interact with #visibleOrNot

【讨论】:

【参考方案6】:

当前接受的答案涉及等待元素出现变得可见。

如果我们对等待元素不感兴趣,只是想测试元素的可见性,我们可以使用getComputedStyle()getBoundingClientRect() 的组合来测试元素是否可见。

我们可以先检查visibility没有设置为hidden

然后我们可以通过检查bottomtopheightwidth属性未设置为0来验证边界框是否可见(这将过滤掉具有@的元素987654324@ 也设置为 none)。

const element_is_visible = await page.evaluate(() => 
  const element = document.querySelector('#example');
  const style = getComputedStyle(element);
  const rect = element.getBoundingClientRect();

  return style.visibility !== 'hidden' && !!(rect.bottom || rect.top || rect.height || rect.width);
);

【讨论】:

【参考方案7】:

使用boundingBox()

此方法返回元素的边界框(相对于主框架),如果元素不可见,则返回 null。

API:https://github.com/puppeteer/puppeteer/blob/master/docs/api.md#elementhandleboundingbox

【讨论】:

【参考方案8】:

此代码绝对可以帮助您。 它基本上意味着该元素已经在页面上可用但尚不可见或在 CSS 中,显示属性设置为无或隐藏可见性。现在,在编写测试时,我们假设一旦元素可用,就对其进行操作,例如单击或键入。但由于该元素尚不可见,因此 Puppeteer 无法执行该操作。

async function isLocatorReady(element, page) 
  const isVisibleHandle = await page.evaluateHandle((e) => 

    const style = window.getComputedStyle(e);
    return (style && style.display !== 'none' && 
    style.visibility !== 'hidden' && style.opacity !== '0');
 , element);
  var visible = await isVisibleHandle.jsonValue();
  const box = await element.boxModel();
  if (visible && box) 
    return true;
  
  return false;

【讨论】:

【参考方案9】:
const firstName= await page.$('[name=firstName]')
expect(firstName!=null).equal(true)

【讨论】:

【参考方案10】:

也许你可以使用elementHandle.boundingBox()(感谢@huypham 的想法)

它将返回一个显示元素边界框的 Promise(相对于主框架),如果元素不可见,则返回 null。

sn-p 示例:

      const loadMoreButton = await getDataPage.$(
        'button.ao-tour-reviews__load-more-cta.js-ao-tour-reviews__load-more-cta'
      );

      const buttonVisible = await loadMoreButton.boundingBox();

      if (buttonVisible) 
        await loadMoreButton.click().catch((e) => 
          console.log('???: ' + e)
        );
      

【讨论】:

【参考方案11】:

基于剧作家检查元素是否可见的逻辑 - https://github.com/microsoft/playwright/blob/master/src/server/injected/injectedScript.ts#L120-L129

function isVisible(element: Element): boolean 
    // Note: this logic should be similar to waitForDisplayedAtStablePosition() to avoid surprises.
    if (!element.ownerDocument || !element.ownerDocument.defaultView)
      return true;
    const style = element.ownerDocument.defaultView.getComputedStyle(element);
    if (!style || style.visibility === 'hidden')
      return false;
    const rect = element.getBoundingClientRect();
    return rect.width > 0 && rect.height > 0;
  

【讨论】:

以上是关于如何使用 Puppeteer 和纯 JavaScript 检查元素是不是可见?的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 webpack 捆绑 puppeteer 进行生产部署?

如何使用无头使用 puppeteer 下载文件:真的?

如何使用 NodeJS 和 puppeteer 从 udemy 抓取图像

如何使用Puppeteer拍摄包含视频的页面的屏幕截图

如何使用 Puppeteer 填充输入字段?

如何打印 puppeteer 发送的原始 devtools 请求?