Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Question] What is the best way to scroll page to the bottom? #4302

Closed
Pooort opened this issue Nov 1, 2020 · 16 comments
Closed

[Question] What is the best way to scroll page to the bottom? #4302

Pooort opened this issue Nov 1, 2020 · 16 comments

Comments

@Pooort
Copy link

Pooort commented Nov 1, 2020

No description provided.

@arjunattam
Copy link
Contributor

arjunattam commented Nov 2, 2020

You can run JS in the page with page.evaluate to scroll.

await page.evaluate(() => window.scrollTo(0, document.body.scrollHeight));

@Pooort
Copy link
Author

Pooort commented Nov 3, 2020

Thank you @arjunattam but it doesn't work. There is no any scroll in mobile version.

@lycanthropes
Copy link

how to smoothly scroll page to the bottom? It is important for lazyload web pages.

@fzerman
Copy link

fzerman commented Sep 29, 2021

how to smoothly scroll page to the bottom? It is important for lazyload web pages.

await page.evaluate(() => {
                for (let i = 0; i < document.body.scrollHeight; i+100) {
                   window.scrollTo(0, i)
                }
            });

@tackelua
Copy link
Contributor

tackelua commented Oct 10, 2021

  await page.evaluate(async () => {
    for (let i = 0; i < document.body.scrollHeight; i += 100) {
      window.scrollTo(0, i);
    }
  });

@oleksiisedun
Copy link

oleksiisedun commented May 20, 2022

await page.evaluate(async () => {
  const delay = ms => new Promise(resolve => setTimeout(resolve, ms));
  for (let i = 0; i < document.body.scrollHeight; i += 100) {
    window.scrollTo(0, i);
    await delay(100);
  }
});

@laurentmmeyer
Copy link

laurentmmeyer commented Jun 24, 2022

I pimped the function because I wanted to simulate two speeds and two directions.

let scroll = async (args) => {
    const {direction, speed} = args;
    const delay = ms => new Promise(resolve => setTimeout(resolve, ms));
    const scrollHeight = () => document.body.scrollHeight;
    const start = direction === "down" ? 0 : scrollHeight();
    const shouldStop = (position) => direction === "down" ? position > scrollHeight() : position < 0;
    const increment = direction === "down" ? 100 : -100;
    const delayTime = speed === "slow" ? 50 : 10;
    console.error(start, shouldStop(start), increment)
    for (let i = start; !shouldStop(i); i += increment) {
        window.scrollTo(0, i);
        await delay(delayTime);
    }
};

Call it that way:

await page.evaluate(scroll, {direction: "down", speed: "slow"});
await page.evaluate(scroll, {direction: "up", speed: "fast"});

@villesau
Copy link

villesau commented Mar 2, 2023

Here is one alternative using mouse events:

  const scrollY = await page.evaluate(() => document.body.scrollHeight); // get the page height
  await page.mouse.wheel(0, scrollY); // scroll to the bottom

Edit: Even though this is probably semantically more correct, this still has the same problems than other instant scrolls and is probably as flaky as await page.evaluate(() => window.scrollTo(0, document.body.scrollHeight));

@inad9300
Copy link

inad9300 commented Apr 2, 2023

I got this working (using documentElement instead of body):

function scrollToBottom() {
   return page.evaluate(() => window.scrollTo(0, document.documentElement.scrollHeight))
}

@mgraf0
Copy link

mgraf0 commented Aug 11, 2023

leveraged this within the context of a backstop js playwright engine_scripts onReady.js file and it worked for me

  async function scrollToBottom() {
      await page.evaluate(async () => {
        for (let i = 0;  window.scrollY * i < window.document.documentElement.scrollHeight; i++) {
          const delay = ms => new Promise(resolve => setTimeout(resolve, ms))
          window.scrollTo(0, window.scrollY + window.document.documentElement.clientHeight);
          await delay(100);
        }
      });
  }

  await scrollToBottom();

versions of tooling
node v18.15.0
backstop js 6.2.2
playwright 1.36.2

@damaon
Copy link

damaon commented Jan 9, 2024

I've used this one to have scrolling and rendering reliably work:

async function scrollToBottom(page) {
  const [scrollY, scrollHeight] = await page.evaluate(() => [
    window.scrollY,
    window.document.documentElement.scrollHeight,
  ])

  for (let i = 0; scrollY * i < scrollHeight; i += 100) {
    await page.evaluate((i) => {
      window.scrollTo(0, i, { behavior: 'smooth' })
    }, i)
    await sleep(0.1)
  }
}

function sleep(seconds) {
  return new Promise((resolve) => setTimeout(resolve, seconds * 1000))
}

@mahdiporkar
Copy link

mahdiporkar commented Jan 26, 2024

Hi bro, i prefer use this :
if you page has lazy load and append dom after each scroll i think this is best solution
`

    public async Task ParsPage(string url)
    {
     
        await page.GotoAsync(url);
        float scrollY ; float height =0 ;
        do
        {
             scrollY = await page.EvaluateAsync<float>("() => document.body.scrollHeight"); // get the page height
             await page.Mouse.WheelAsync(height, scrollY); // scroll to the bottom
             height = await page.EvaluateAsync<float>("() => document.body.scrollHeight");
             Thread.Sleep(2000); //ms

        } while (scrollY <= height);

    }

`

@akwasin
Copy link

akwasin commented Mar 4, 2024

Thank you @arjunattam but it doesn't work. There is no any scroll in mobile version.

This works for me this is my playwright.config.ts

export default defineConfig({ ... use: { headless: false, viewport: iPhone13PM.viewport, userAgent: iPhone13PM.userAgent, launchOptions: { ... }, ... screenshot: { mode: 'only-on-failure', fullPage: true, }, }, projects: [ { name: 'Mobile Chrome', use: { ...devices['iPhone 13'] }, }, ], });

@karlhorky
Copy link
Contributor

karlhorky commented Mar 18, 2024

For waiting for all (visible) lazy-loaded images on the page to load, I wrote a new trick for this, which also tests image loading with naturalWidth:

const lazyImages = await page.locator('img[loading="lazy"]:visible').all();

for (const lazyImage of lazyImages) {
  await lazyImage.scrollIntoViewIfNeeded();
  await expect(lazyImage).not.toHaveJSProperty('naturalWidth', 0);
}

Be aware, using .all() can be problematic if new images are being added, removed, shown or hidden while the test code is running.

One workaround for this is to assert the length of the .all() array (if you know it) to wait for it to stabilize:

const lazyImagesLocator = page.locator('img[loading="lazy"]:visible');

// Assert on length to wait for image visibility to stabilize
// after client-side JavaScript hides some images
// https://github.com/microsoft/playwright/issues/31737#issuecomment-2233775909
await expect(lazyImagesLocator).toHaveCount(13);

const lazyImages = await lazyImagesLocator.all();

for (const lazyImage of lazyImages) {
  await lazyImage.scrollIntoViewIfNeeded();
  await expect(lazyImage).not.toHaveJSProperty('naturalWidth', 0);
}

Source: https://github.com/karlhorky/playwright-tricks#load-all-lazy-images

@NikolozCh
Copy link

await page.evaluate(async () => {
  const delay = ms => new Promise(resolve => setTimeout(resolve, ms));
  for (let i = 0; i < document.body.scrollHeight; i += 100) {
    window.scrollTo(0, i);
    await delay(100);
  }
});

When GitHub comment has multiple positive reactions,. you know this is the answer. Thanks for saving my time :)

@sovetski
Copy link

Scrolling is cool but if there is some infinity scroll or DOM changes, the suggestions above will not work, there is my solution which will detect all changes with MutationObserver (lazy load, infinity load etc.)

export const scrollPage = async (page) => {
    await page.evaluate(async () => {
        await new Promise((resolve) => {
            let totalHeight = 0;
            const distance = 100;

            const scroll = () => {
                const scrollHeight = document.body.scrollHeight;
                window.scrollBy(0, distance);
                totalHeight += distance;

                if (totalHeight >= scrollHeight) {
                    clearInterval(timer);
                    observer.disconnect();
                    resolve();
                }
            };

            const timer = setInterval(scroll, 100);

            const observer = new MutationObserver(() => {
                totalHeight = 0;
                scroll();
            });

            observer.observe(document.body, { childList: true, subtree: true });
        });
    });
};

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests