Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Playwright - how to check if element is in viewport?

I have an array-like object of nodes (it is a carousel), their order is randomly generated with each page refresh, playwright finds all the elements to be visible, but some of them are outside the viewport (based on the error received). I need to make sure, that element is inside the viewport when attempting to click it, otherwise I get an error stating the element is outside.

How to determine if a randomly picked node element of an array-like object is actually within the viewport?

like image 260
Arson Avatar asked Jul 14 '26 03:07

Arson


2 Answers

Playwright has added a feature to enable checking this using "expect". The usage info is below:

 (method) LocatorAssertions.toBeInViewport(options?: {
    ratio?: number;
    timeout?: number;
}): Promise<void>
Ensures the Locator points to an element that intersects viewport, according to the intersection observer API.

Usage

const locator = page.getByRole('button');
// Make sure at least some part of element intersects viewport.
await expect(locator).toBeInViewport();
// Make sure element is fully outside of viewport.
await expect(locator).not.toBeInViewport();
// Make sure that at least half of the element intersects viewport.
await expect(locator).toBeInViewport({ ratio: 0.5 });
like image 156
DexNoKnees Avatar answered Jul 15 '26 15:07

DexNoKnees


Playwright unfortunately doesn't have a method like isInterSectingViewport in Puppeteer yet.(likethis)

So authors of Playwright help me in Slack community(you can find it on official site).

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

The case in which I used this method: I want know that after I’m click some element - I have scrolling to another element. Unfortunately boundingBox method doesn’t help in my case.

also You can add this functionality to my BasePage class

/**
     * @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;
    });
  }

P.S. In fact, all the code except for one line is taken from the realisation of method isInterSectingViewport in GitHub Puppeteer

like image 20
cardinalX Avatar answered Jul 15 '26 15:07

cardinalX



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!