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

Use jsPDF’s doc.html() method with a renderable export source. For a div hidden in your interface, either reveal it only while exporting or, preferably, let html2canvas modify a cloned document through onclone. The clone approach keeps the live page unchanged while making hidden content visible to the renderer.

Choose how the hidden content becomes renderable

A browser cannot lay out an element whose own display, visibility, dimensions, or hidden ancestor prevent rendering. jsPDF does not read a hidden div as a special PDF object; its HTML path renders the element through html2canvas and then places the result into the PDF. Your export code therefore needs a source that has usable layout at capture time.

# Preview Product Price
1 Building Scalable Web Sites Building Scalable Web Sites $18.03
Approach Live interface changes? Best use Trade-off
Temporarily reveal the original Yes, briefly A small export with simple state Requires careful cleanup if rendering fails
Use html2canvas.onclone No The UI must remain untouched Ancestor styles, dimensions, and asynchronous content still need checking
Build a dedicated export clone No Reports needing their own CSS, width, and controls More DOM and styling work

The official html2canvas configuration documents onclone as a hook that receives the cloned document. Changes made there affect the clone used for rendering, not the original page. The exact CSS needed depends on your app, so inspect hidden ancestors and the element’s dimensions rather than assuming one style change fixes every case.

Minimal export when the div can be shown briefly

This is the shortest implementation. It records the existing inline styles, makes the target renderable, exports it, and restores the styles in a finally block so an exception does not leave the interface exposed.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
#1 Best Overall
Sale
Building Scalable Web Sites
  • Used Book in Good Condition
async function exportVisibleDiv() {
  const element = document.getElementById('report');
  if (!element) throw new Error('Missing #report');

  const oldDisplay = element.style.display;
  const oldVisibility = element.style.visibility;

  try {
    element.style.display = 'block';
    element.style.visibility = 'visible';

    const doc = new jsPDF();
    await new Promise((resolve, reject) => {
      doc.html(element, {
        callback: pdf => {
          pdf.save('report.pdf');
          resolve();
        },
        x: 10,
        y: 10,
        autoPaging: 'text'
      });
    });
  } finally {
    element.style.display = oldDisplay;
    element.style.visibility = oldVisibility;
  }
}

Call exportVisibleDiv() from your button handler after jsPDF and its HTML dependencies are loaded. If the div is hidden by a parent, changing the child alone may not help. In that case, reveal the relevant ancestor as well, or use an export-only clone with explicit dimensions.

Preferred method: make the cloned document visible

With this pattern, the page the user sees never changes. The renderer clones the document, finds the target in that clone, and changes its display and visibility before capture.

function exportHiddenDiv() {
  const element = document.getElementById('report');
  if (!element) throw new Error('Missing #report');

  const doc = new jsPDF();
  doc.html(element, {
    html2canvas: {
      onclone(clonedDocument) {
        const exportElement = clonedDocument.getElementById('report');
        if (!exportElement) return;

        exportElement.style.display = 'block';
        exportElement.style.visibility = 'visible';
        exportElement.style.position = 'static';
        exportElement.style.width = '900px';
      }
    },
    callback(pdf) {
      pdf.save('report.pdf');
    },
    x: 10,
    y: 10,
    autoPaging: 'text'
  });
}

Keep the selector identity stable in the clone. If the element is inside a hidden parent, make that parent renderable in onclone too. A dedicated export clone is more predictable when the screen version relies on tabs, collapsed panels, fixed positioning, or responsive widths.

Creating a dedicated export clone

Clone the report, remove interface-only controls, append it to an off-screen container with a known width, and pass that clone to doc.html(). Do not use display:none on the container, because that prevents layout.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
function exportReportClone() {
  const source = document.getElementById('report');
  if (!source) throw new Error('Missing #report');

  const wrapper = document.createElement('div');
  wrapper.style.position = 'fixed';
  wrapper.style.left = '-100000px';
  wrapper.style.top = '0';
  wrapper.style.width = '900px';
  wrapper.style.background = '#fff';

  const clone = source.cloneNode(true);
  clone.style.display = 'block';
  clone.style.visibility = 'visible';
  clone.querySelectorAll('[data-html2canvas-ignore]').forEach(node => node.remove());
  wrapper.appendChild(clone);
  document.body.appendChild(wrapper);

  const doc = new jsPDF();
  doc.html(clone, {
    callback(pdf) {
      pdf.save('report.pdf');
      wrapper.remove();
    },
    x: 10,
    y: 10,
    width: 190,
    windowWidth: 900,
    autoPaging: 'text'
  });
}

For production code, also remove the wrapper when rendering reports an error. If your report contains data populated asynchronously, wait until that data and its fonts or images are ready before calling jsPDF.

Configure page layout, scaling, and pagination

jsPDF HTML options

  • callback receives the PDF instance; call save() there.
  • x and y set the starting position in the PDF.
  • width controls the rendered HTML width in the PDF coordinate system.
  • windowWidth influences the layout viewport used for the HTML render. It is separate from html2canvas’s similarly named media-query setting.
  • autoPaging: 'text' attempts to avoid cutting text and is most suitable for mostly single-column text. true or 'slice' can split text or shapes at page boundaries.
  • Set a filename in save(); jsPDF’s HTML API also supports documented margin, filename, and font-face settings.

html2canvas capture options

  • scale controls raster density. A higher value can improve small text but increases memory and rendering time.
  • width, height, x, y, scrollX, and scrollY define the capture region.
  • windowWidth and windowHeight affect the cloned browser viewport and responsive CSS. Do not confuse this with jsPDF’s sizing option.
  • useCORS requests cross-origin images that permit CORS. It is not a bypass for servers that omit the required headers.
  • A proxy can be used for cross-origin assets when your deployment provides an appropriate proxy.
  • onclone is the place to adjust visibility, add export-only styles, or remove controls without mutating the live document.
  • data-html2canvas-ignore excludes an element from the render. Add it to buttons, menus, or other controls that should not appear in the PDF.
  • onError can report resources that fail to load or render.
doc.html(element, {
  html2canvas: {
    scale: 2,
    useCORS: true,
    onclone(clonedDocument) {
      clonedDocument.querySelectorAll('.screen-only').forEach(node => {
        node.style.display = 'none';
      });
    },
    onError(error) {
      console.error('html2canvas resource error', error);
    }
  },
  width: 190,
  windowWidth: 900,
  autoPaging: 'text',
  callback: pdf => pdf.save('report.pdf')
});

Make hidden reports print correctly

Give the export a real size

Responsive rules can produce a different report when the clone has an unexpected viewport. Set a stable clone width and coordinate it with jsPDF’s width and html2canvas’s viewport options. Check the rendered element’s getBoundingClientRect() in the clone workflow if the PDF is blank or only a narrow strip.

Wait for content and fonts

Charts, images, and data inserted after the click may not exist when rendering begins. Resolve the request that supplies the data first, wait for images to complete, and then export. For web fonts, wait for document.fonts.ready where supported. Missing fonts can change line wrapping and page breaks.

Exclude interface controls

Mark a print button or close icon with data-html2canvas-ignore, or remove it in the cloned document. This avoids exporting controls that only make sense on screen.

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

Troubleshooting blank, clipped, or incomplete PDFs

The PDF is blank

  • Confirm the target ID exists and is passed to doc.html().
  • Check whether the target or an ancestor still has display:none, visibility:hidden, zero dimensions, or a collapsed panel state.
  • In onclone, make every hidden ancestor needed for layout visible and give the export a width.
  • Ensure asynchronous data has finished loading before starting capture.

Only some images appear

Cross-origin images require server permission through CORS, or a suitable proxy. Setting useCORS: true cannot override a server that does not send the required headers. Use onError to identify failed resources.

Text or graphics are cut at page breaks

Try autoPaging: 'text' for text-heavy, single-column content. The slice modes may divide a shape or text chunk. For complex dashboards, insert deliberate page sections in the export clone and test at the final width.

The layout is too small, too large, or wraps differently

Separate the problems: adjust the export width and jsPDF width for sizing; adjust html2canvas scale for pixel density; adjust viewport settings for responsive breakpoints. A larger scale does not correct an incorrect CSS width.

The page looks unlike the browser

HTML rendering is not identical to the browser’s print engine. CSS effects, external fonts, images, fixed elements, and page breaks may need export-specific styles. Treat the PDF as a separately tested output rather than promising a perfect visual clone.

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

Performance and reliability considerations

  • Large, long pages consume more memory because html2canvas rasterizes content. Capture only the report area and avoid unnecessarily high scale.
  • Use a fixed export width to make wrapping and pagination repeatable across devices.
  • Keep the clone cleanup in both success and failure paths.
  • Cache or preload assets that must appear, but verify that the final URLs permit CORS.
  • Test long text, empty states, slow images, narrow mobile layouts, and reports with hidden ancestors.
  • For a sensitive report, remember that client-side rendering exposes the report data to the browser; enforce your application’s normal authorization before generating it.

Or skip the browser setup

ScreenshotNeo provides a website screenshot and PDF API when the source is a URL rather than an in-page hidden DOM node. 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 the response identifies the result with X-Page-Verdict and X-Billed headers. Its MCP server exposes take_screenshot, get_page_info, and capture_pdf to Claude, Cursor, and other MCP clients. Every plan includes the features, with 1,000 free shots per month without a card and paid plans starting at $5 for 3,000 shots.

See the ScreenshotNeo documentation for the complete option list, including PDF paper size, margins, landscape mode, page ranges, custom CSS and JavaScript, selectors, waits, cookies, headers, device presets, geolocation, caching, signed links, asynchronous jobs, bulk capture, and usage reporting.

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}`);

If your report is available at a protected or otherwise configurable URL, ScreenshotNeo can supply custom headers, cookies, authorization, waits, and JavaScript through its documented options. Start with a free account at ScreenshotNeo (1,000 screenshots a month, no card).

Frequently Asked Questions

Can jsPDF export an element that is hidden with a closed tab or accordion?

Yes, if the export source is made renderable in the cloned document or in a dedicated export clone. Reveal required ancestors and assign a usable width before calling doc.html().

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

Does onclone change my live page?

No. The hook runs against the cloned document used by html2canvas. Changes to the clone do not alter the original DOM, although you still need to account for styles inherited from hidden ancestors.

Why are external images missing even with useCORS?

The image server must grant CORS access. The option requests CORS loading; it cannot bypass missing response headers. Use an allowed proxy or same-origin assets.

Quick Recap

SaleBestseller No. 1
Building Scalable Web Sites
Building Scalable Web Sites
Used Book in Good Condition
$18.03

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.