Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

If absolutely positioned elements appear piled at the top of an html2canvas image, first determine whether the browser layout is wrong or whether html2canvas reconstructed it incorrectly. Log the live element rectangles, then test scroll coordinates, viewport dimensions, positioning ancestors, transforms, clipping, and SVG separately. There is no single, evidence-backed fix for every version and layout.

Why the canvas can differ from the page

html2canvas is not a native screenshot function. Its script traverses the page DOM, reads element information, and builds its own representation of the page. The browser has already performed layout and painting, but html2canvas must implement the CSS properties it needs to reproduce that result. The project documentation cautions that CSS support is incomplete; its FAQ says, “Every CSS property must be manually implemented to render correctly, so html2canvas will never have full CSS support.”

That distinction explains why a page can look correct in Chrome while the generated canvas places absolute children at one coordinate. It also means that increasing z-index, changing every child to position: relative, or always scrolling to the top is not a universal remedy.

Start with a layout-versus-render diagnosis

Capture the browser’s geometry

Run this immediately before calling html2canvas. Include the target, its positioning ancestor, and any scrolling wrapper:

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
function inspectBox(label, el) {
  const r = el.getBoundingClientRect();
  const s = getComputedStyle(el);
  console.log(label, {
    rect: { x: r.x, y: r.y, width: r.width, height: r.height },
    position: s.position,
    top: s.top,
    left: s.left,
    transform: s.transform,
    zIndex: s.zIndex,
    overflow: s.overflow,
    width: s.width,
    height: s.height
  });
}

const target = document.querySelector('#capture');
const child = target.querySelector('.absolute-child');
inspectBox('target', target);
inspectBox('child', child);
console.log('window scroll', window.scrollX, window.scrollY);
console.log('target scroll', target.scrollLeft, target.scrollTop);

If the rectangles are already identical or unexpectedly near the top, fix the application layout first. An absolutely positioned element is laid out relative to its containing block, normally the nearest positioned ancestor. Check that the intended ancestor has the expected position, dimensions, and coordinate system. If the rectangles are correct but the canvas is not, investigate capture coordinates and CSS coverage instead of rewriting production CSS.

Make a minimal reproduction

Reduce the case to one positioned ancestor, one absolute child, and the smallest stylesheet that still fails. Record the html2canvas version, browser and operating system, page and nested-scroll positions, capture options, and the live rectangles. A minimal reproduction makes it possible to distinguish a CSS-support gap from an application-specific containing-block problem.

Test scroll coordinates explicitly

The configuration reference documents scrollX and scrollY as the scroll positions used while rendering, including for fixed-position elements. A nested scrolling container and the window can therefore describe different coordinate frames.

  1. Capture while the page is at the top and save the result.
  2. Capture again at the problematic scroll position without changing any styles.
  3. Try explicit coordinates that match the frame you intend to render.
const canvas = await html2canvas(document.querySelector('#capture'), {
  scrollX: window.scrollX,
  scrollY: window.scrollY
});

A June 2019 report for html2canvas 1.0.0-rc.3, Chrome 75 on Windows, described a large blank offset when capturing after scrolling to the bottom; that reporter said window.scrollTo(0, 0) fixed that particular case and that rc.1 did not show it. Treat this as a version-specific reproduction clue, not a blanket prescription. If you need a top-of-page experiment:

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
const oldX = window.scrollX;
const oldY = window.scrollY;
window.scrollTo(0, 0);
try {
  const canvas = await html2canvas(document.querySelector('#capture'), {
    scrollX: 0,
    scrollY: 0
  });
  document.body.appendChild(canvas);
} finally {
  window.scrollTo(oldX, oldY);
}

For a target inside an independently scrolling element, also log that element’s scrollTop and scrollLeft. If only nested-scroll captures fail, reproduce with the target temporarily placed in the document flow and then isolate the wrapper’s overflow and positioning rules.

Rank #2
Sale
HTML and CSS: Design and Build Websites
  • HTML CSS Design and Build Web Sites
  • Comes with secure packaging
  • It can be a gift option

Match the rendering viewport for large or responsive targets

The FAQ demonstrates setting the rendering window to the element’s full scroll dimensions:

const element = document.querySelector('#capture');
const canvas = await html2canvas(element, {
  windowWidth: element.scrollWidth,
  windowHeight: element.scrollHeight
});

This is primarily a remedy for clipping, empty output, and responsive layout changes, not a guaranteed fix for absolute children stacking at the top. Configuration notes that windowWidth and windowHeight can affect media queries. A wider virtual viewport may therefore select a different breakpoint and legitimately move elements.

Very large canvases can also exceed browser- or platform-specific area and dimension limits, producing blank or partial output. Test a smaller subtree first, then capture sections or reduce the requested viewport when limits are reached.

Free tools Windows power users keep installed

One-click scans. No signup required.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Check positioning, transforms, and clipping one factor at a time

Containing blocks

Confirm that the ancestor intended to contain the absolute child actually establishes that context. Compare a temporary test with position: relative on that ancestor, but apply it only to the reproduction or clone until you know it is the real fix. Also verify that the ancestor has a nonzero size when its children are absolutely positioned.

Transforms and stacking contexts

Transforms, opacity, positioned elements, and z-index can create stacking contexts. html2canvas processes positioned descendants in separate internal buckets for negative z-index, zero/auto or transformed/opacity content, and positive z-index content. That implementation detail helps explain paint-order differences, but it does not prove that z-index causes every top-position symptom. Test geometry and paint order separately:

  • Remove a transform from the ancestor and capture again.
  • Temporarily remove nonessential opacity and filters.
  • Set explicit dimensions on the containing block.
  • Test with a simple, explicit z-index only after geometry is correct.

Overflow and clipping

Temporarily change overflow: hidden, clip-path, masks, and nested scroll wrappers to visible, then compare. A child may be correctly positioned but clipped in the cloned render. Restore each rule after testing; do not leave broad overflow changes as a production workaround without checking the visual and accessibility consequences.

Use onclone for capture-only experiments

The onclone option receives the cloned document used for rendering. You can make a narrowly scoped change without modifying the live page:

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
const source = document.querySelector('#capture');
const canvas = await html2canvas(source, {
  onclone: (clonedDocument) => {
    const clone = clonedDocument.querySelector('#capture');
    const child = clone?.querySelector('.absolute-child');
    if (!clone || !child) return;

    // Experiment only: use the values discovered during diagnosis.
    clone.style.position = 'relative';
    child.style.transform = 'none';
  }
});

Do not copy this exact override blindly. If the browser’s rectangles are correct, changing the clone can hide the real cause. Use one change per capture, compare the output, and remove the change if it does not identify a specific lost or altered layout rule. You can also inject a temporary stylesheet in onclone when several related selectors must be changed.

Determine whether the failing node is SVG

A separate report for html2canvas 1.4.1, Chrome 111 on Windows 10, described incomplete rendering when an SVG was absolutely positioned away from its parent’s upper-left corner. The report associated the symptom with SVG serialization by XMLSerializer. It is a narrow SVG case, not proof that ordinary absolutely positioned div elements are affected in the same way.

Capture the SVG alone, then run a controlled comparison with a temporary in-flow or top-left version in the clone. If only the original SVG placement fails, preserve the reproduction details and test an updated package or a different rendering path rather than changing every absolute element on the page.

Rank #4
Sale
Web Design with HTML, CSS, JavaScript and jQuery Set
  • Brand: Wiley
  • Set of 2 Volumes
  • A handy two-book set that uniquely combines related technologies Highly visual format and accessible language makes these books highly effective learning tools Perfect for beginning web designers and front-end developers

A repeatable capture procedure

  1. Save the package version, browser, operating system, viewport, and scroll positions.
  2. Log getBoundingClientRect() and computed styles for the target, child, and positioning ancestors.
  3. Capture at scroll position zero and at the failing position.
  4. Try explicit scrollX, scrollY, windowWidth, and windowHeight values.
  5. Reduce the DOM to a minimal subtree, then test transforms, overflow, and stacking contexts individually.
  6. Use onclone for temporary, capture-only overrides.
  7. Test SVG separately if present.
  8. Compare the resulting canvas with the logged browser geometry and keep the smallest successful change.

Troubleshooting common symptoms

Symptom Likely area Next check
All absolute children appear at the top, but the live page is correct Clone layout or unsupported CSS Minimal subtree, computed rectangles, then an onclone experiment
Large blank strip when capturing after scrolling Scroll coordinate mismatch Compare top-of-page and explicit scrollX/scrollY; record package and browser versions
Content is cut off or canvas is blank Viewport or canvas limits Try element scroll dimensions, smaller sections, and a reduced viewport
Only one responsive breakpoint fails Virtual viewport changed media queries Log windowWidth/windowHeight and capture at a matching viewport
Only SVG placement fails SVG serialization path Capture the SVG alone and create a minimal SVG reproduction
Changing z-index does nothing Geometry, not paint order Inspect containing blocks, transforms, and rectangles before changing stacking

When to escalate or choose a real-browser screenshot

If a minimal reproduction still differs from the browser, open an issue with the package version, browser and OS, DOM and CSS, computed rectangles, scroll state, options, and before/after images. The project FAQ recommends this process when a CSS property is missing or incomplete.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

For server-side screenshots where faithful browser painting is essential, the FAQ points to Puppeteer or Playwright, which drive a real headless browser. That is an architectural choice: it avoids asking a DOM-to-canvas implementation to reproduce every browser painting detail, but it introduces browser-process setup, resource use, and operational maintenance.

Or skip the browser setup

For an API-based capture, ScreenshotNeo returns a PNG, JPEG, WebP, or PDF from one GET request. Before capture it accepts cookie or consent banners 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 status.

Use the same URL with your own access key:

curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp

Python:

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)

Node.js:

const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);

See the complete parameter reference and options in the ScreenshotNeo documentation. It supports full-page and selector captures, dark mode, device presets or custom viewports, retina scale, PDF paper and page controls, custom CSS and JavaScript, clicks, selector or network-idle waits, request blocking, headers, cookies, user agents, authorization, timezone and geolocation, transparent backgrounds, resizing, chosen cache TTLs, signed image links, asynchronous jobs with signed webhooks, bulk capture for up to 100 URLs per call, a usage API, and an OpenAPI specification. Existing parameter names used by other screenshot APIs also work.

An MCP server provides take_screenshot, get_page_info, and capture_pdf tools for Claude, Cursor, and other MCP clients. The Free plan includes 1,000 screenshots per month with no card; paid plans start at $5 for 3,000 shots. Create a free ScreenshotNeo account to try it.

What’s actually slowing this PC down?

Pick the symptom - the matching free tool is one click away.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

FAQ

Does html2canvas capture the browser’s pixels exactly?

No. It reconstructs a representation from DOM information, so unsupported or differently implemented CSS can produce a different result from the already-painted page.

Should I always call window.scrollTo(0, 0) first?

No. Use it as a controlled test for scroll-dependent failures. Keep it only when your coordinate model requires it and your reproduction confirms the result.

Is this problem specific to z-index?

Not necessarily. Verify geometry, containing blocks, transforms, overflow, viewport settings, and SVG serialization before treating paint order as the cause.

What information should an issue report contain?

Include the html2canvas version, browser and operating system, minimal DOM/CSS, viewport and scroll positions, capture options, computed rectangles, and the smallest output that demonstrates the discrepancy.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Frequently Asked Questions

Can I fix the issue by replacing absolute positioning with relative positioning everywhere?

No. That changes the page’s layout semantics and may hide the actual cause. Test the containing block and use a narrowly scoped clone override first.

Why did changing windowWidth move my elements?

The rendering viewport can activate different media queries, so a changed width may produce a legitimate responsive layout rather than a positioning repair.

The Bottom Line

Measure the live layout first, then isolate scroll coordinates, viewport size, containing blocks, transforms, clipping, and SVG. Use onclone for controlled experiments; if faithful browser pixels are mandatory, move to a real-browser or API screenshot path.

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.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.