Created
March 8, 2022 23:14
-
-
Save prescience-data/4e028843d336e6d795bb08708be3757b to your computer and use it in GitHub Desktop.
Puppeteer Scroll To Element
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { randomInt } from "node:crypto" | |
import type Puppeteer from "puppeteer-core" | |
/** Recast Puppeteer private properties **/ | |
type Page = Omit<Puppeteer.Page, "_client"> & { | |
readonly _client: Puppeteer.CDPSession | |
} | |
type ElementHandle = Omit<Puppeteer.ElementHandle, "_page"> & { | |
readonly _page: Page | |
} | |
/** | |
* Determine viewport height to use as base for each scroll distance. | |
* | |
* @param {Page} page | |
* @return {number} | |
*/ | |
const getViewportHeight = (page: Page): number => { | |
const height: number | undefined = page.viewport()?.height | |
if (height) { | |
return height | |
} else { | |
throw new Error(`Viewport not available on page.`) | |
} | |
} | |
/** | |
* Generate a random distance between 30% - 70% of the viewport height. | |
* | |
* @param {number} distance | |
* @return {number} | |
*/ | |
const randomDistance = (distance: number): number => | |
randomInt(distance * 0.3, distance * 0.7) | |
/** | |
* Sends a synthetic scroll gesture via CDP. | |
* | |
* @param {Page} page | |
* @param {number} distance | |
* @return {Promise<void>} | |
*/ | |
export const scrollDown = async ( | |
page: Page, | |
distance: number | |
): Promise<void> => { | |
await page._client.send("Input.synthesizeScrollGesture", { | |
x: 0, | |
y: 0, | |
xDistance: 0, | |
yDistance: distance | |
}) | |
} | |
/** | |
* Primary vertical scroll function. | |
* Accepts a target element and scrolls a randomized distance until intersecting viewport. | |
* | |
* @param {ElementHandle} el | |
* @return {Promise<void>} | |
*/ | |
export const scrollTo = async (el: ElementHandle): Promise<void> => { | |
// Extract the page from the element handle. | |
const page: Page = el._page | |
// Use the viewport height as a baseline to randomize each scroll distance. | |
const viewportHeight: number = getViewportHeight(page) | |
// Continue to scroll random distance until element is in viewport. | |
while (!(await el.isIntersectingViewport())) { | |
await scrollDown(page, randomDistance(viewportHeight)) | |
await page.waitForTimeout(randomInt(50, 500)) | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment