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 the browser’s getBoundingClientRect() method when you need an element’s coordinates relative to the current viewport. Selenium returns the rectangle as a dictionary, so read x (or left) and y (or top), along with width and height:
from selenium.webdriver.common.by import By
el = driver.find_element(By.CSS_SELECTOR, "#target")
rect = driver.execute_script(
"return arguments[0].getBoundingClientRect();",
el,
)
viewport_x = rect["x"] # same value as rect["left"]
viewport_y = rect["y"] # same value as rect["top"]
width = rect["width"]
height = rect["height"]
These are CSS-pixel coordinates from the viewport’s top-left corner, not operating-system screen coordinates or the browser window’s outer position.
What “viewport coordinates” means
The viewport is the page area currently visible inside the browser. A rectangle returned by Element.getBoundingClientRect() describes an element’s size and position relative to that area. Its top-left point is (rect["left"], rect["top"]); x and y are equivalent aliases. The rectangle includes the element’s padding and border.
Because the values are viewport-relative, scrolling changes them. An element near the top of a document can have a large document position but a small (or negative) viewport y after scrolling.
#1 Best Overall
Complete Selenium Python example
The following script starts a browser, waits for a target, optionally scrolls it into a predictable position, then prints fractional coordinates without prematurely rounding them.
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
URL = "https://example.com"
SELECTOR = "h1"
driver = webdriver.Chrome()
try:
driver.get(URL)
wait = WebDriverWait(driver, 15)
el = wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, SELECTOR)))
# Make visibility an intentional part of the measurement.
driver.execute_script(
"arguments[0].scrollIntoView({block: 'center', inline: 'nearest'});",
el,
)
rect = driver.execute_script(
"return arguments[0].getBoundingClientRect();",
el,
)
print({
"x": rect["x"],
"y": rect["y"],
"left": rect["left"],
"top": rect["top"],
"width": rect["width"],
"height": rect["height"],
"right": rect["right"],
"bottom": rect["bottom"],
})
finally:
driver.quit()
Replace URL and SELECTOR with your page and locator. The explicit wait prevents a measurement against a page that has not yet inserted the element. If your page changes layout after loading images, fonts, or client-side data, wait for that state before measuring.
Choosing among Selenium’s geometry APIs
| API | Coordinate frame | Scrolls? | Returns | Best use |
|---|---|---|---|---|
getBoundingClientRect() |
Current viewport, in CSS pixels | No | Position and size, including padding and border | Viewport assertions, visual debugging, screenshot regions |
element.rect |
WebDriver element geometry; state the frame your workflow expects | No | Dictionary containing location and size | General WebDriver geometry |
element.location |
WebDriver x/y location; not automatically the current viewport rectangle | No | Position only | Code that needs WebDriver location semantics |
element.location_once_scrolled_into_view |
Top-left location after Selenium scrolls | Yes | Rounded x/y | Convenience when Selenium’s documented scroll-and-locate behavior is desired |
driver.get_window_rect() |
Outer browser window | No | Window x/y and dimensions | Window management, not DOM element coordinates |
Do not substitute location or rect for viewport coordinates without confirming the coordinate convention required by the next operation. Selenium documents location_once_scrolled_into_view as returning the top-left screen location, or zero coordinates when the element is not visible; Selenium also warns that this behavior can change without warning.
Scrolling before you measure
Deliberate JavaScript scroll
Use scrollIntoView() when the workflow needs the element visible before measuring or clicking. The example above centers it vertically and keeps the nearest horizontal position. Always call getBoundingClientRect() again after scrolling; the old rectangle describes the old scroll position.
Rank #2
Selenium’s convenience property
location_once_scrolled_into_view performs a Selenium-controlled scroll and then reports a rounded location. It is useful when that exact convenience behavior is acceptable, but it is not a replacement for a clearly defined viewport rectangle. Sticky headers, nested scrollers, and page scripts can also alter the final position, so verify the returned rectangle when pixels matter.
Nested scrolling and layout movement
An element may be inside a scrollable panel rather than the document. Scrolling a container can change the viewport rectangle even when the document’s scroll position is unchanged. Measure only after all intended scrolling and layout changes have completed.
Precision, transforms, and visible pixels
Keep the numeric values returned by the browser when sub-pixel precision matters. Device-pixel-ratio settings, CSS transforms, zoom, and fractional layout values can produce decimals. Round only at the boundary where another API requires integer pixels, and document whether you use floor, ceiling, or nearest-integer rounding.
Quick wins for a faster PC:
Clear out junk files and repair common Windows errorsFree Scan →Fix the driver behind crashes, sound loss and screen glitchesFind Drivers →The DOM rectangle is the smallest rectangle containing the complete element, including padding and borders. It is not a mask of every painted pixel: transformed, clipped, or partially obscured content can occupy less visible area than the rectangle suggests. For a click, use a point that is actually exposed; for a screenshot crop, account for clipping and device scale.
Viewport, document, and screen coordinates are different
Viewport coordinates move when the page scrolls. A document coordinate is normally derived by adding the relevant page scroll offsets to the viewport value, but nested scrolling and fixed-position elements make that conversion context-dependent. Operating-system screen coordinates additionally depend on the browser window’s outer position, decorations, and platform behavior.
Rank #3
driver.get_window_rect() reports the browser window’s own x/y and dimensions. It does not convert a DOM rectangle into screen coordinates. Keep the coordinate frame in variable names and comments—for example, viewport_x_css—to prevent accidental mixing.
Using the rectangle in real workflows
Assertions
For responsive tests, assert ranges rather than a single pixel when fonts, rendering engines, or device scale can vary:
What’s actually slowing this PC down?
Pick the symptom - the matching free tool is one click away.
assert 0 <= rect["x"] <= driver.execute_script("return innerWidth")
assert rect["width"] > 0
Choose tolerances that reflect the rendering variability you permit. A zero-size rectangle may indicate a hidden element or a selector that matched an unexpected node.
Clicking a calculated point
Prefer Selenium’s element interactions when possible because they account for WebDriver’s interactability checks. If a downstream tool needs a point, calculate it from the rectangle, commonly the center:
Rank #4
center_x = rect["x"] + rect["width"] / 2
center_y = rect["y"] + rect["height"] / 2
Re-measure immediately before using the point if animations or sticky UI can move the element.
Capturing a region
Viewport CSS pixels are not necessarily output-image pixels. Multiply or otherwise map coordinates according to the screenshot tool’s device scale, and account for browser zoom. Capture after the same scroll state used for measurement.
Do these 3 things before closing this tab:
1Clear out junk files and repair common Windows errors2Fix the driver behind crashes, sound loss and screen glitches3Repair Windows errors before they cause bigger problemsOr skip the browser setup
If your goal is a clean page image rather than Selenium-driven interaction, ScreenshotNeo accepts one request and returns a PNG, JPEG, WebP, or PDF. It accepts cookie and 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 or CAPTCHAs, blank pages, timeouts, failed loads, and cache hits are not billed, and the response identifies the page verdict and billing status in X-Page-Verdict and X-Billed headers. Its MCP server exposes take_screenshot, get_page_info, and capture_pdf for Claude, Cursor, and other MCP clients.
See the ScreenshotNeo API documentation for all options.
cURL
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://example.com -o shot.webp
Python
import requests
r = requests.get("https://api.screenshotneo.com/v1/shot", params={"access_key": "YOUR_API_KEY", "url": "https://example.com"}, timeout=90)
open("shot.webp", "wb").write(r.content)
Node.js
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://example.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
Options include full-page captures with lazy images loaded, CSS-selector element capture, dark mode, 12 device presets or custom viewports, retina scale, PDF paper and page controls, custom CSS/JavaScript, clicks, waits, ad/tracker/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, usage reporting, and an OpenAPI specification. Familiar parameter names from other screenshot APIs are accepted to ease migration.
Plans include 1,000 free screenshots each month with no card, then Starter at $5 for 3,000, Growth at $15 for 15,000, Pro at $39 for 60,000, Scale at $99 for 250,000, and Business at $249 for 1,000,000. Yearly billing gives two months free, and every feature is included on every plan. Create a free ScreenshotNeo account to start with 1,000 screenshots a month and no card.
Recommended Free Tools
Best Value
Troubleshooting checklist
“No such element”
- Check the selector and current URL.
- Wait for presence or visibility instead of measuring immediately after navigation.
- If the element is inside an iframe, switch to that frame before locating it.
Coordinates are zero or negative
- Confirm the element is rendered and not hidden.
- Remember that negative viewport values mean the element is above or left of the visible viewport.
- Scroll deliberately, then measure again.
Values change between runs
- Wait for images, fonts, client-side content, and animations to settle.
- Use a consistent viewport, zoom, device scale, and browser version.
- Capture the rectangle immediately before the action that consumes it.
The screenshot crop is misaligned
- Check CSS-pixel versus device-pixel scaling.
- Account for borders, padding, transforms, and clipping.
- Ensure the screenshot was taken at the same scroll position as the measurement.
Performance and reliability guidance
A JavaScript rectangle lookup is local to the already loaded page and normally cheaper than a second navigation. The expensive variables are page loading, synchronization, rendering, and repeated screenshots. Locate the element once, wait on meaningful state, measure only when needed, and avoid polling at a high frequency unless an animation is the subject of the test.
For repeatable visual checks, pin the browser viewport and device scale, disable or wait out motion, and record the coordinate frame with the result. Treat a rectangle as time-sensitive data: any scroll, resize, layout shift, transform, or DOM replacement invalidates it.
FAQ
Are x and left different?
No. For a DOMRect they represent the same horizontal position; likewise y and top.
Does getBoundingClientRect() return integers?
Not necessarily. Browsers can return fractional CSS-pixel values, so preserve them until an integer-only consumer requires rounding.
Which API should I use for the browser window’s position?
Use driver.get_window_rect(). It describes the outer window, not an element inside the page.
Why can a visible element have a rectangle that is not fully visible?
Clipping, nested scroll containers, fixed overlays, and transforms can leave only part of the DOM rectangle exposed. The rectangle describes geometry, not guaranteed painted visibility.
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.

