Recommended Free Tools
Some links on this page are affiliate links: if you buy through them we may earn a commission, at no extra cost to you.
Use Selenium 4’s wheel actions when you need browser-like scrolling, and use JavaScript when you need direct DOM control. For a known element, call ActionChains(driver).scroll_to_element(element).perform(). For a fixed distance, use scroll_by_amount(0, pixels). For a particular scroll origin, use scroll_from_origin. JavaScript’s scrollIntoView() is a practical alternative when your test needs page-context scrolling rather than a wheel gesture.
The examples below use Python because its Selenium API documents all three wheel patterns clearly. The wheel reference is labeled Chromium Only, so verify support for your exact browser and language binding before depending on it outside Chromium.
Choose the scrolling method by intent
| What you need | Recommended API | What it does |
|---|---|---|
| Reveal a known element | scroll_to_element(element) |
Uses wheel input and positions the page with the element’s bottom at the bottom of the screen. |
| Move a defined distance | scroll_by_amount(delta_x, delta_y) |
Scrolls from the upper-left of the viewport; negative values move left or up. |
| Scroll from an element or viewport point | scroll_from_origin(origin, delta_x, delta_y) |
Applies deltas relative to an element (optionally offset) or a viewport coordinate. |
| Invoke page scrolling behavior | execute_script() |
Runs synchronous JavaScript in the current window or frame, such as scrollIntoView(). |
Selenium’s wheel actions were introduced in Selenium 4.2. Actions do not automatically scroll a target into view before another action, so explicitly reveal an off-screen element before clicking, typing, or reading it.
Prerequisites and a reliable starting point
- Install Selenium 4 for Python:
python -m pip install -U selenium. - Use a WebDriver for the browser under test and navigate to the page before locating the target.
- Wait for the element you intend to scroll to be present and, when necessary, visible. Scrolling does not repair a stale reference or a page that has not finished rendering.
- For wheel methods, check the current Selenium documentation for your browser. The official wheel guide is explicitly marked Chromium Only.
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
options = webdriver.ChromeOptions()
driver = webdriver.Chrome(options=options)
wait = WebDriverWait(driver, 20)
driver.get("https://example.com")
target = wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, "footer")))
Scroll a page to a specific element
Wheel action: the browser-like approach
from selenium.webdriver.common.action_chains import ActionChains
ActionChains(driver).scroll_to_element(target).perform()
This is the documented common case for wheel input. The element is brought into view, with its bottom positioned at the bottom of the screen. It is a scroll operation, not a click: follow it with your intended interaction.
#1 Best Overall
ActionChains(driver).scroll_to_element(target).perform()
wait.until(EC.element_to_be_clickable((By.CSS_SELECTOR, "footer a"))).click()
If a sticky header covers the element after scrolling, use JavaScript with an offset or scroll again after measuring the layout. Do not assume that an element being in the viewport means every pixel of it is unobstructed.
JavaScript: direct DOM scrolling
driver.execute_script(
"arguments[0].scrollIntoView({block: 'center', inline: 'nearest'});",
target,
)
execute_script runs synchronously in the current window or frame. The script above centers the element vertically, which is often more useful than aligning it to an edge when a fixed navigation bar is present. The simpler documented pattern is:
driver.execute_script("arguments[0].scrollIntoView();", target)
Scroll down or up by a fixed amount
Wheel distance
# Down 500 CSS pixels
ActionChains(driver).scroll_by_amount(0, 500).perform()
# Up 300 CSS pixels
ActionChains(driver).scroll_by_amount(0, -300).perform()
# Horizontal movement is the first argument
ActionChains(driver).scroll_by_amount(250, 0).perform()
delta_x and delta_y are measured from the upper-left of the viewport. A negative vertical value moves upward. A single large movement can skip lazy-loading triggers or intermediate content; several smaller movements are safer when the page loads content as it becomes visible.
Free tools Windows power users keep installed
One-click scans. No signup required.
JavaScript distance and absolute positions
# Relative movement
driver.execute_script("window.scrollBy(0, arguments[0]);", 500)
# Absolute vertical position
driver.execute_script("window.scrollTo(0, arguments[0]);", 1200)
Remove the leading space before driver if you paste this into a Python file. JavaScript scrolling is useful when you need an exact document coordinate, but it does not emulate a physical wheel gesture.
Scroll relative to an element or a viewport point
Element origin
An element origin lets you move relative to a scrollable area or a meaningful control. If the origin is outside the viewport, Selenium first brings it into view. An offset that lands outside the viewport raises an exception.
from selenium.webdriver.common.action_chains import ActionChains
origin = ActionChains(driver).scroll_from_origin
# Scroll 200 pixels down from the element's center
from selenium.webdriver.common.actions.wheel_input import ScrollOrigin
scroll_origin = ScrollOrigin.from_element(target)
ActionChains(driver).scroll_from_origin(scroll_origin, 0, 200).perform()
For an offset origin, construct the origin with the element and its horizontal and vertical offsets according to your installed Python binding’s API. Keep the offset inside the viewport after Selenium reveals the element.
Rank #2
Viewport origin
from selenium.webdriver.common.actions.wheel_input import ScrollOrigin
# Start at a viewport coordinate, then move down
viewport_origin = ScrollOrigin.from_viewport(400, 300)
ActionChains(driver).scroll_from_origin(viewport_origin, 0, 250).perform()
Origin-based scrolling is particularly useful when a page contains nested scroll regions. The origin identifies where the wheel input starts; it does not guarantee that an arbitrary container will scroll unless that container is the active scrollable context.
Scroll a nested, scrollable container
Many applications keep the document fixed while a panel, modal, table, or chat history scrolls. First locate the container, then use JavaScript to set its scrollTop or bring a child into view inside that container.
panel = wait.until(EC.presence_of_element_located(
(By.CSS_SELECTOR, ".results-panel")
))
# Move the panel down by 400 pixels
driver.execute_script(
"arguments[0].scrollTop += arguments[1];", panel, 400
)
# Reveal a row within that panel
row = panel.find_element(By.CSS_SELECTOR, "tr[data-id='42']")
driver.execute_script(
"arguments[0].scrollIntoView({block: 'nearest'});", row
)
Using block: 'nearest' avoids needlessly moving the whole page when the child is already partly visible. If the panel uses virtualization, wait for the row to be rendered before locating it; a row that is not in the DOM cannot be scrolled into view.
Wait for content that appears after scrolling
Scrolling and waiting are separate operations. Infinite-scroll pages often append content asynchronously. After each movement, wait for a concrete condition rather than sleeping for an arbitrary time.
from selenium.common.exceptions import TimeoutException
old_count = len(driver.find_elements(By.CSS_SELECTOR, ".card"))
ActionChains(driver).scroll_by_amount(0, 700).perform()
try:
wait.until(lambda d: len(d.find_elements(By.CSS_SELECTOR, ".card")) > old_count)
except TimeoutException:
# The page may have reached its end, failed to load, or require a different trigger.
pass
Other useful conditions include the presence of a “load more” control, disappearance of a spinner, or a footer becoming visible. For a page that loads images lazily, verify the image’s complete property or wait for an application-specific readiness marker.
The Tool Desk
Outbyte Driver Updater FREEScan for outdated or missing drivers - takes under a minuteDriver Scan →Outbyte PC Repair FREEClear out junk files and repair common Windows errorsFree Scan →Rank #3
Common failures and fixes
Element is outside the viewport
Cause: an action such as click or send keys was attempted without first scrolling. Fix: call scroll_to_element or scrollIntoView(), then wait for the element to be interactable.
Wheel action is unsupported or has no effect
Cause: the wheel reference is Chromium Only, or the installed binding/browser combination does not implement the method consistently. Fix: verify current browser support and Selenium version; use JavaScript scrolling when you need a portable fallback.
Wrong region scrolls
Cause: the page has a nested scroll container, but the command targets the window. Fix: identify the container with a selector and change its scrollTop, or use an element-origin action after confirming which region receives wheel input.
Sticky header hides the target
Cause: the target is technically visible but covered. Fix: center it with scrollIntoView({block: 'center'}), apply a controlled offset, or hide the header only in a test-specific style.
Stale element reference
Cause: infinite scrolling or a framework rerender replaced the DOM node. Fix: locate the element again after the scroll and wait for the replacement.
Rank #4
Timeout after scrolling
Cause: the page reached its end, a request failed, a bot check appeared, or the application needs a click rather than a wheel event. Fix: inspect the DOM and network-visible state, add an explicit end-of-content check, and handle the application’s actual loading control.
Performance and reliability practices
- Prefer a target-based scroll over repeated blind distances when the test has a semantic destination.
- Use one explicit wait tied to the state you need; excessive fixed sleeps make suites slow and still fail under variable latency.
- Break very long pages into moderate movements when lazy loading or intersection observers are involved.
- Re-find elements after operations that can rerender the page.
- Record the browser, Selenium version, binding, and scroll method in failure logs so a browser-support issue is distinguishable from an application bug.
- Keep wheel and JavaScript paths as separate helper functions. That makes it straightforward to switch when a browser or component behaves differently.
Or skip the browser setup
If your real goal is a clean image or PDF of a URL rather than interactive browser automation, ScreenshotNeo provides a single screenshot request. It accepts consent banners before capture and removes more than 60 known consent platforms, newsletter popups, and chat widgets; each cleanup step can be disabled. Bot checks, CAPTCHAs, blank pages, timeouts, failed loads, and cache hits are not billed, and response headers identify the page verdict and billing result. It also offers an MCP server for AI clients, with take_screenshot, get_page_info, and capture_pdf tools.
One-call examples
See the parameter details in the ScreenshotNeo documentation.
Quick wins for a faster PC:
Fix the driver behind crashes, sound loss and screen glitchesFind Drivers →Repair Windows errors before they cause bigger problemsFix Now →Scan for outdated or missing drivers - takes under a minuteDriver Scan →curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
import requests
r = requests.get("https://api.screenshotneo.com/v1/shot", params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"}, timeout=90)
open("shot.webp", "wb").write(r.content)
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
ScreenshotNeo supports full-page captures with lazy images loaded, element selectors, dark mode, device presets and custom viewports, retina scale, PDFs with paper and page options, custom CSS and JavaScript, clicks, waits, request blocking, headers, cookies, user agents, authorization, timezone, geolocation, transparent backgrounds, resizing, chosen cache TTLs, signed image links, asynchronous webhooks, bulk capture of up to 100 URLs per call, a usage API, and an OpenAPI specification. Existing parameter names used by other screenshot APIs also work, easing migration.
The free plan includes 1,000 screenshots per month with no card. Paid plans start at $5 for 3,000 shots; every feature is available on every plan, and yearly billing gives two months free. Create a free ScreenshotNeo account to start.
FAQ
Which method should a new Selenium test use?
Use scroll_to_element when the test has a known target, and JavaScript when you need DOM-specific positioning or a fallback for an unsupported wheel environment.
Best Value
Can Selenium scroll an iframe?
Switch into the frame first, then locate and scroll elements in that document. Switch back to the default content when the frame interaction is complete.
Outdated Drivers Are Slowing You Down
One free scan finds every outdated or missing driver and matches the right update for your exact hardware.Free scan · exact hardware matchWindows Errors? Fix Them Before They Spread
Repair common Windows errors and clear accumulated junk for a smoother, more stable PC - no reinstall needed.Free scan · no reinstallDoes scrolling guarantee that an image has loaded?
No. Scrolling may trigger lazy loading, but wait for the image or application state that proves loading finished.
Frequently Asked Questions
Can I use negative values to scroll upward?
Yes. Pass a negative vertical delta to scroll_by_amount, such as scroll_by_amount(0, -300).
Why does my scroll target move but remain unclickable?
A fixed header, overlay, disabled state, or rerender may still block it. Center the element, wait for clickability, and locate it again after dynamic updates.
Quick Recap
Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.
What’s actually slowing this PC down?
Pick the symptom - the matching free tool is one click away.

