ScreenshotNeo

BlogHTML to image & PDF

How to Combine jQuery Functions to Save html2canvas Output as PDF

Use jQuery for the click and element lookup, then await html2canvas before creating and saving a jsPDF document. Includes multi-page exports, CORS fixes, and runnable code.

By the ScreenshotNeo team30 September 202611 min read

How to Combine jQuery Functions to Save html2canvas Output as PDF

To save an element as a PDF with jQuery, use jQuery to handle the button click and select the element, then pass its underlying DOM node to html2canvas. Wait for the returned Promise to resolve before converting the canvas to an image and calling jsPDF.save(). The essential detail is $('#content')[0]: html2canvas expects a DOM element, not a jQuery collection.

This approach creates a visual PDF: the page is reconstructed in a canvas and inserted as an image. It is useful when the exported page should look like the rendered section, but the PDF text typically will not be selectable as native text. For long documents, split the content across pages rather than shrinking an entire tall canvas onto one sheet.

1. Install and load the libraries

Include jQuery, html2canvas, and jsPDF in the page. The following example uses browser script tags; for a production app, pin versions through your package manager or asset pipeline and serve them from your own approved source. Check each library’s official installation guidance when choosing versions.

<script src="https://code.jquery.com/jquery-3.7.1.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/html2canvas/1.4.1/html2canvas.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jspdf/2.5.1/jspdf.umd.min.js"></script>

With the jsPDF UMD build, its constructor is exposed at window.jspdf.jsPDF. The example below assigns it to a local variable for readability. If your bundler exposes the package as an import, use its documented import syntax instead.

2. Add the button and export target

Keep the export target in the document and give it a stable ID. Add an ordinary button outside the target so it does not appear in the resulting PDF.

<button id="savePdf" type="button">Save as PDF</button>

<main id="content">
  <h1>Quarterly report</h1>
  <p>This section will be captured into a PDF.</p>
</main>

<p id="exportError" role="alert" hidden></p>

3. Capture the element and save after rendering

Attach the event after the libraries and markup are available. html2canvas returns a Promise that resolves with a canvas, so await it before calling toDataURL() or saving the PDF. Its documented purpose is to render a webpage or part of one in the user’s browser, reconstructing what it can from the DOM and styles.

The safe sequence is click, render to canvas, place the image in the PDF, then save after the Promise resolves.
The safe sequence is click, render to canvas, place the image in the PDF, then save after the Promise resolves.
<script>
  const { jsPDF } = window.jspdf;

  $('#savePdf').on('click', async function () {
    const button = this;
    const error = $('#exportError');
    const target = $('#content')[0];

    if (!target) {
      error.text('The content to export was not found.').prop('hidden', false);
      return;
    }

    button.disabled = true;
    error.prop('hidden', true).text('');

    try {
      const canvas = await html2canvas(target, {
        scale: window.devicePixelRatio || 1,
        useCORS: true,
        backgroundColor: '#ffffff'
      });

      if (!canvas.width || !canvas.height) {
        throw new Error('The rendered canvas is empty.');
      }

      const imgData = canvas.toDataURL('image/png');
      const pdf = new jsPDF({ unit: 'pt', format: 'a4', orientation: 'portrait' });
      const pageWidth = pdf.internal.pageSize.getWidth();
      const pageHeight = pdf.internal.pageSize.getHeight();
      const ratio = Math.min(pageWidth / canvas.width, pageHeight / canvas.height);
      const width = canvas.width * ratio;
      const height = canvas.height * ratio;
      const x = (pageWidth - width) / 2;
      const y = (pageHeight - height) / 2;

      pdf.addImage(imgData, 'PNG', x, y, width, height);
      pdf.save('export.pdf');
    } catch (err) {
      console.error('PDF export failed:', err);
      error.text('Could not create the PDF. Check the page assets and try again.').prop('hidden', false);
    } finally {
      button.disabled = false;
    }
  });
</script>

The fit calculation above preserves the captured aspect ratio and places the image on one A4 page. It fits a tall element by scaling it down, so a very long report may become unreadable. The PDF API’s addImage call accepts image data and placement dimensions; the official html2canvas examples also demonstrate serializing a canvas with toDataURL('image/png').

4. Why the jQuery collection must be unwrapped

$('#content') is a jQuery object that wraps zero or more matched elements. html2canvas needs the actual element. Use $('#content')[0] or document.querySelector('#content'). Check that the result exists before starting the capture; an empty selection otherwise leads to an invalid input or a confusing failure.

The click handler itself can be written with jQuery, while the rendering and PDF work use the libraries’ APIs. Keep those responsibilities separate: jQuery coordinates the user action, html2canvas renders, and jsPDF assembles and downloads.

5. Promise callback version

If the surrounding code does not use async functions, use .then(). The PDF creation and save must remain inside the callback so they run only after rendering finishes.

$('#savePdf').on('click', function () {
  const target = $('#content')[0];
  if (!target) return;

  html2canvas(target, { useCORS: true }).then(function (canvas) {
    const pdf = new window.jspdf.jsPDF('p', 'pt', 'a4');
    const pageWidth = pdf.internal.pageSize.getWidth();
    const ratio = pageWidth / canvas.width;
    pdf.addImage(
      canvas.toDataURL('image/png'),
      'PNG',
      0,
      0,
      pageWidth,
      canvas.height * ratio
    );
    pdf.save('export.pdf');
  }).catch(function (err) {
    console.error('PDF export failed:', err);
  });
});

This compact version scales to the page width and may extend below the first page if the content is tall. Use explicit page slicing or a page-aware wrapper for multi-page output.

6. Configure the capture for your page

html2canvas options change the rendering conditions. They do not make the browser capture a pixel-perfect screenshot: the library rebuilds the image from DOM information, and some CSS or browser-rendered content may differ.

Option or technique When to use it Limit
scale Increase sharpness, commonly using window.devicePixelRatio or a fixed value such as 2. Higher scale increases canvas dimensions and memory use. Reduce it for large pages or constrained devices.
useCORS: true Allow eligible cross-origin images to be requested with CORS. The remote host must send permissive CORS headers. This setting cannot bypass browser security.
backgroundColor Set a predictable page background, for example white. Use null where supported when transparency is desired, and verify the PDF appearance.
windowWidth and windowHeight Control the virtual window dimensions used for layout-sensitive captures. Choose dimensions that match the intended responsive breakpoint.
scrollX and scrollY Control the scroll position used during rendering. Sticky and fixed elements can render differently depending on the capture viewport.
ignoreElements Skip unwanted elements by predicate when configuring the render. For a simple element exclusion, mark it with data-html2canvas-ignore.
onclone Adjust the cloned document before rendering, such as hiding controls or changing print styles. Keep changes scoped to the clone so the live page is unaffected.
allowTaint Understand this option before changing it for cross-origin assets. A tainted canvas cannot be serialized with toDataURL(); setting this does not grant access to protected image bytes.

For example, hide interactive controls using the supported ignore marker:

<button data-html2canvas-ignore>Edit report</button>

const canvas = await html2canvas($('#content')[0], {
  scale: 2,
  useCORS: true,
  backgroundColor: '#fff'
});

Wait for content to finish loading before invoking html2canvas. If the target includes images or data loaded asynchronously by your application, trigger export only after that work completes. A delay can mask timing problems, but an application-level ready signal is more reliable.

7. Create a multi-page PDF

One tall canvas added to a single page either gets scaled down or extends beyond the page. A straightforward manual approach is to render the element once, then add vertical slices to pages. The following helper assumes the canvas width is scaled to the PDF page width; each page takes a slice corresponding to one PDF page’s height.

function addCanvasAsPages(pdf, canvas) {
  const pageWidth = pdf.internal.pageSize.getWidth();
  const pageHeight = pdf.internal.pageSize.getHeight();
  const scale = pageWidth / canvas.width;
  const sliceHeight = Math.floor(pageHeight / scale);

  if (sliceHeight < 1) throw new Error('Canvas is too wide for the page.');

  const pageCanvas = document.createElement('canvas');
  const ctx = pageCanvas.getContext('2d');
  pageCanvas.width = canvas.width;
  pageCanvas.height = Math.min(sliceHeight, canvas.height);

  let offset = 0;
  while (offset < canvas.height) {
    const height = Math.min(sliceHeight, canvas.height - offset);
    pageCanvas.height = height;
    ctx.clearRect(0, 0, pageCanvas.width, height);
    ctx.drawImage(canvas, 0, offset, canvas.width, height, 0, 0, canvas.width, height);

    if (offset > 0) pdf.addPage();
    pdf.addImage(pageCanvas.toDataURL('image/png'), 'PNG', 0, 0, pageWidth, height * scale);
    offset += height;
  }
}

$('#savePdf').on('click', async function () {
  const canvas = await html2canvas($('#content')[0], { scale: 1, useCORS: true });
  const pdf = new window.jspdf.jsPDF({ unit: 'pt', format: 'a4' });
  addCanvasAsPages(pdf, canvas);
  pdf.save('report.pdf');
});

Canvas slicing can split a line, table row, or image at a page boundary. For polished reports, render known sections separately, add page breaks in the source layout, or use a page-aware HTML-to-PDF workflow and test the actual content. html2pdf.js documents a chained pipeline—.from().toContainer().toCanvas().toImg().toPdf().save()—and provides options around html2canvas and jsPDF. A wrapper can simplify orchestration, but it does not remove canvas security limits or eliminate layout testing.

8. Image and layout edge cases

  • Cross-origin images: A remote image without suitable CORS response headers can taint the canvas. Prefer same-origin assets, configure the asset server to allow your origin, or use a controlled proxy that is permitted to retrieve that content. useCORS helps only when the server cooperates.
  • Background images and web fonts: Wait for fonts and assets to load before capture. If a font is still loading, the measured layout can shift or fall back.
  • Unsupported CSS: Since html2canvas reconstructs the page from DOM and styles, unsupported or unusual effects may not match the live browser view. Simplify the export styles and validate representative pages.
  • Canvas and video elements: Browser security and rendering rules can limit what content is available. Verify the actual output and avoid assuming protected cross-origin media can be copied.
  • Fixed and sticky elements: Headers can appear at unexpected positions when the capture viewport or scroll offsets differ from the visible page. Use a dedicated export layout or adjust the cloned document.
  • Large targets: A high-resolution, long canvas consumes substantial memory. Reduce scale, split the document into sections, and avoid simultaneous exports on mobile devices.
  • Popup or control content: Exclude buttons, floating chat controls, and other transient elements with data-html2canvas-ignore or clone-specific styling.
Cross-origin images appear in the export only when browser security and the asset server's CORS policy allow them.
Cross-origin images appear in the export only when browser security and the asset server's CORS policy allow them.

9. Troubleshooting

Symptom Likely cause Fix
PDF downloads before the capture is ready, or is blank save() runs outside the html2canvas Promise. Put all canvas-dependent work after await html2canvas(...) or inside .then().
“Invalid element” or no captured content A jQuery collection was passed directly, or the selector matched nothing. Pass $('#content')[0]; check for a missing target before rendering.
SecurityError from toDataURL() A cross-origin asset tainted the canvas. Use same-origin content or configure correct CORS headers on the asset host. A client option cannot override browser policy.
Images are missing They have not loaded, the server disallows CORS, or the resource is inaccessible. Wait for the page’s asset-ready state, inspect image requests, and correct the asset server’s CORS configuration.
Text or CSS differs from the page The renderer does not support or reproduce every browser feature. Use export-specific styles, simplify effects, and compare the result at the intended viewport.
PDF is tiny or too tall The full canvas was fitted to one page or placed without scaling. Use page slicing or separate sections; choose a suitable scale and page format.
Browser tab freezes or runs out of memory The target and scale create an oversized canvas, possibly alongside other large assets. Lower scale, capture sections, and avoid exporting several canvases concurrently.
Nothing happens when clicking The handler ran before jQuery loaded, the selector is wrong, or an exception is hidden. Load scripts in order, bind after the DOM is ready, check the browser console, and show a visible error state.

10. Performance, reliability, and cost

The DIY method runs in the visitor’s browser and uses that browser’s CPU and memory. Capture time depends on DOM size, image dimensions, fonts, effects, and device capacity; there is no fixed duration that applies to every page. High-DPI rendering improves sharpness but grows both canvas width and height, so its memory footprint can increase quickly. Keep the target focused, lower the scale for long pages, and disable repeated clicks while a capture is in progress.

For reliability, export a stable, self-contained section, wait for your own app’s data and assets, catch Promise rejections, and tell the user when an export fails. Do not treat a generated canvas PDF as an archival or accessible document without checking its text, links, and reading order; it is primarily a visual image. If searchable text, selectable paragraphs, or precise pagination is required, use a PDF generation path designed to preserve document structure.

The browser libraries have no ScreenshotNeo API charge, but development and support costs include handling browser differences, asset hosting, CORS, page layout, and large-document memory. If using a hosted screenshot service, compare its billing rules, output controls, and failure handling against your requirements.

Or skip the browser setup

If you need a screenshot or PDF of a live website rather than a client-side export of your app’s DOM, ScreenshotNeo provides a website screenshot API and MCP server. One GET request captures a URL; the result can be PNG, JPEG, WebP, or PDF. See the API documentation for request options.

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 removes cookie banners, newsletter popups, and chat widgets before the shot. Bot checks, blank pages, failed loads, timeouts, and cache hits are not billed; response headers identify the page verdict and billing status. Its MCP server gives AI agents tools to take screenshots, get page information, and capture PDFs. The free plan includes 1,000 screenshots a month with no card; paid plans start at $5 for 3,000. Those captures are of URLs in a browser service, so this is a different fit from exporting a private, unsaved DOM state in the visitor’s page.

Sign up free for 1,000 screenshots a month, no card required.

FAQ

Can I make the PDF text selectable?

Not with the image-based flow shown here: it embeds the rendered canvas. Use a PDF workflow that writes text and layout as PDF elements when selection and search matter.

Can jQuery itself create the PDF?

No. In this pattern jQuery handles events and selection. html2canvas renders the target, and jsPDF builds and saves the PDF.

Why does the output look different from the page?

html2canvas reconstructs a rendering from DOM and CSS information. Some browser features and styles do not reproduce exactly, so a dedicated export layout may be needed.

Should I use html2pdf.js instead?

It can reduce manual pipeline code and offers page-break options. The underlying canvas workflow still has asset security and rendering constraints, and you should test page breaks with your content.

References