【发布时间】:2021-06-03 05:53:47
【问题描述】:
我有一个类似数组的节点对象(它是一个轮播),它们的顺序是在每次页面刷新时随机生成的,playwright 发现所有元素都是可见的,但其中一些在视口之外(基于收到错误)。我需要确保在尝试单击该元素时该元素在视口内,否则我会收到错误消息,指出该元素在外面。
如何判断一个随机选取的类数组对象的节点元素是否真的在视口内?
【问题讨论】:
标签: puppeteer viewport playwright
我有一个类似数组的节点对象(它是一个轮播),它们的顺序是在每次页面刷新时随机生成的,playwright 发现所有元素都是可见的,但其中一些在视口之外(基于收到错误)。我需要确保在尝试单击该元素时该元素在视口内,否则我会收到错误消息,指出该元素在外面。
如何判断一个随机选取的类数组对象的节点元素是否真的在视口内?
【问题讨论】:
标签: puppeteer viewport playwright
const firstId = "#someId";
// it happens that I am evaluating in a frame, rather than page
const result = await frame.evaluate((firstId) => {
// define a function that handles the issue
// returns true if element is within viewport, false otherwise
function isInViewport(el) {
// find element on page
const element = document.querySelector(el);
const rect = element.getBoundingClientRect();
return (
rect.top >= 0 &&
rect.left >= 0 &&
rect.bottom <=
(window.innerHeight || document.documentElement.clientHeight) &&
rect.right <=
(window.innerWidth || document.documentElement.clientWidth)
);
};
return isInViewport(firstId);
}, firstId);
// back to node context
console.log(result);
【讨论】:
不幸的是,Playwright 在 Puppeteer 中还没有类似 isInterSectingViewport 的方法。(likethis)
所以 Playwright 的作者在 Slack 社区帮助我(你可以在官方网站上找到它)。
const result = await page.$eval(selector, async element => {
const visibleRatio: number = await new Promise(resolve => {
const observer = new IntersectionObserver(entries => {
resolve(entries[0].intersectionRatio);
observer.disconnect();
});
observer.observe(element);
// Firefox doesn't call IntersectionObserver callback unless
// there are rafs.
requestAnimationFrame(() => {});
});
return visibleRatio > 0;
});
我使用这种方法的案例: 我想知道,在我单击某个元素后 - 我滚动到另一个元素。不幸的是,boundingBox 方法对我没有帮助。
您也可以将此功能添加到我的 BasePage 类中
/**
* @returns {!Promise<boolean>}
*/
isIntersectingViewport(selector: string): Promise<boolean> {
return this.page.$eval(selector, async element => {
const visibleRatio: number = await new Promise(resolve => {
const observer = new IntersectionObserver(entries => {
resolve(entries[0].intersectionRatio);
observer.disconnect();
});
observer.observe(element);
// Firefox doesn't call IntersectionObserver callback unless
// there are rafs.
requestAnimationFrame(() => {});
});
return visibleRatio > 0;
});
}
附: 其实除了一行之外的所有代码都取自GitHub Puppeteer中方法isInterSettingViewport的实现
【讨论】:
使用 css 选择器检查元素是否在视口中:
import { test, expect, devices } from '@playwright/test'
const url = 'https://example.com'
const selector = 'h1'
test.use({
headless: false,
browserName: 'webkit',
...devices['iPhone 13 Mini'],
})
test('visibility', async ({ page }) => {
await page.goto(url)
const box = await page.locator(selector).boundingBox() // it contains x, y, width, and height only
let isVisible = await page.evaluate((selector) => {
let isVisible = false
let element = document.querySelector(selector)
if (element) {
let rect = element.getBoundingClientRect()
if (rect.top >= 0 && rect.left >= 0) {
const vw = Math.max(document.documentElement.clientWidth || 0, window.innerWidth || 0)
const vh = Math.max(document.documentElement.clientHeight || 0, window.innerHeight || 0)
if (rect.right <= vw && rect.bottom <= vh) {
isVisible = true
}
}
}
return isVisible
}, selector)
await expect(isVisible).toBeTruthy()
})
【讨论】: