如何使用 Puppeteer 和纯 JavaScript 检查元素是否可见?
How can I check that an element is visible with Puppeteer and pure JavaScript?
我想用 Puppeteer 和纯 JavaScript(不是 jQuery)检查 DOM 元素是否可见,我该怎么做?可见是指元素通过 CSS 显示,而不是隐藏(f.ex。通过 display: none
)。
例如,我可以通过CSS规则display: none
判断我的元素#menu
是否没有被隐藏,方法如下:
const isNotHidden = await page.$eval('#menu', (elem) => {
return elem.style.display !== 'none'
})
我一般如何确定元素是否隐藏,而不仅仅是通过 display: none
?
一个是通过检查其显示样式值。
第二个是通过检查它的高度,对于exp如果该元素是display: none
元素的子元素,则offsetHeight
将是0
并且因此您知道该元素不可见,尽管它具有显示值。 opacity: 0
不被视为隐藏元素,因此我们不会对其进行检查。
const isNotHidden = await page.$eval('#menu', (elem) => {
return window.getComputedStyle(elem).getPropertyValue('display') !== 'none' && elem.offsetHeight
});
你也可以检查elem.offsetWidth
,在任何计算之前都不错,检查元素是否存在。
我发现 Puppeteer 有一个用于此目的的 API 方法:Page.waitForSelector,通过它的 visible
选项。我不知道后一个选项,但它让你等到元素可见。
await page.waitForSelector('#element', {
visible: true,
})
相反,您可以通过 hidden
选项等待元素被隐藏。
我认为这是关于 Puppeteer API 的惯用答案。感谢 Colin Cline,因为我认为他的回答可能作为一般 JavaScript 解决方案很有用。
我会使用@aknuds1 的方法,但您也可以执行以下操作。
expect((await page.$('#element')) !== null).toEqual(true)
如果您正在异步获取资源,请注意上述预期可能不会通过,因为它不会等待更改反映在 UI 上。这就是为什么这种方法在这种情况下可能不是首选的原因。
显然,jQuery 是这样做的:
visible = await page.evaluate((e) => e.offsetWidth > 0 && e.offsetHeight > 0, element)
如果你只是想知道一个元素是否可见,那么你可以使用这个函数。在调用此函数之前,您应该确保页面已准备就绪。您可以通过在您希望可见的其他元素上使用 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
}
当前接受的答案涉及等待元素出现和变得可见。
如果我们对等待元素不感兴趣,只想测试元素的可见性,我们可以使用getComputedStyle()
and getBoundingClientRect()
的组合来测试元素是否可见。
我们可以先检查visibility
没有设置为hidden
。
然后我们可以通过检查 bottom
、top
、height
和 width
属性是否未设置为 [= 来验证边界框是否可见19=](这将过滤掉 display
也设置为 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);
});
使用 boundingBox()
此方法returns元素的边界框(相对于主框架),如果元素不可见则为 null。
API: https://github.com/puppeteer/puppeteer/blob/master/docs/api.md#elementhandleboundingbox
这段代码绝对能帮到你。
它基本上意味着该元素已经在页面上可用但尚不可见或在 CSS 中,显示 属性 设置为 none 或隐藏可见性。现在,在编写我们的测试时,我们假设一旦元素可用,就对其执行操作,例如单击或键入。但由于此元素尚不可见,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;
}
const firstName= await page.$('[name=firstName]')
expect(firstName!=null).equal(true)
也许你可以使用 elementHandle.boundingBox()
(感谢@huypham 的想法)
它将 return 显示元素边界框(相对于主框架)的 Promise,如果元素不可见则为 null。
片段示例:
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)
});
}
基于剧作家检查元素是否可见的逻辑 - 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(不是 jQuery)检查 DOM 元素是否可见,我该怎么做?可见是指元素通过 CSS 显示,而不是隐藏(f.ex。通过 display: none
)。
例如,我可以通过CSS规则display: none
判断我的元素#menu
是否没有被隐藏,方法如下:
const isNotHidden = await page.$eval('#menu', (elem) => {
return elem.style.display !== 'none'
})
我一般如何确定元素是否隐藏,而不仅仅是通过 display: none
?
一个是通过检查其显示样式值。
第二个是通过检查它的高度,对于exp如果该元素是display: none
元素的子元素,则offsetHeight
将是0
并且因此您知道该元素不可见,尽管它具有显示值。 opacity: 0
不被视为隐藏元素,因此我们不会对其进行检查。
const isNotHidden = await page.$eval('#menu', (elem) => {
return window.getComputedStyle(elem).getPropertyValue('display') !== 'none' && elem.offsetHeight
});
你也可以检查elem.offsetWidth
,在任何计算之前都不错,检查元素是否存在。
我发现 Puppeteer 有一个用于此目的的 API 方法:Page.waitForSelector,通过它的 visible
选项。我不知道后一个选项,但它让你等到元素可见。
await page.waitForSelector('#element', {
visible: true,
})
相反,您可以通过 hidden
选项等待元素被隐藏。
我认为这是关于 Puppeteer API 的惯用答案。感谢 Colin Cline,因为我认为他的回答可能作为一般 JavaScript 解决方案很有用。
我会使用@aknuds1 的方法,但您也可以执行以下操作。
expect((await page.$('#element')) !== null).toEqual(true)
如果您正在异步获取资源,请注意上述预期可能不会通过,因为它不会等待更改反映在 UI 上。这就是为什么这种方法在这种情况下可能不是首选的原因。
显然,jQuery 是这样做的:
visible = await page.evaluate((e) => e.offsetWidth > 0 && e.offsetHeight > 0, element)
如果你只是想知道一个元素是否可见,那么你可以使用这个函数。在调用此函数之前,您应该确保页面已准备就绪。您可以通过在您希望可见的其他元素上使用 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
}
当前接受的答案涉及等待元素出现和变得可见。
如果我们对等待元素不感兴趣,只想测试元素的可见性,我们可以使用getComputedStyle()
and getBoundingClientRect()
的组合来测试元素是否可见。
我们可以先检查visibility
没有设置为hidden
。
然后我们可以通过检查 bottom
、top
、height
和 width
属性是否未设置为 [= 来验证边界框是否可见19=](这将过滤掉 display
也设置为 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);
});
使用 boundingBox()
此方法returns元素的边界框(相对于主框架),如果元素不可见则为 null。
API: https://github.com/puppeteer/puppeteer/blob/master/docs/api.md#elementhandleboundingbox
这段代码绝对能帮到你。 它基本上意味着该元素已经在页面上可用但尚不可见或在 CSS 中,显示 属性 设置为 none 或隐藏可见性。现在,在编写我们的测试时,我们假设一旦元素可用,就对其执行操作,例如单击或键入。但由于此元素尚不可见,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;
}
const firstName= await page.$('[name=firstName]')
expect(firstName!=null).equal(true)
也许你可以使用 elementHandle.boundingBox()
(感谢@huypham 的想法)
它将 return 显示元素边界框(相对于主框架)的 Promise,如果元素不可见则为 null。
片段示例:
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)
});
}
基于剧作家检查元素是否可见的逻辑 - 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;
}