javascriptjspdfhtml2canvashtml2pdf

html2pdf, html2canvas, jsPDF cannot export the html select option normally


When I select Saab and then export, I expect the output pdf screenshot showing the Saab in the html selection field, but it always shows the first value which is Volvo. It's very strange.

package version:

{
    "html2canvas": "^1.3.3",
    "html2pdf.js": "^0.10.1",
    "jspdf": "^2.4.0",
    "jspdf-html2canvas": "^1.4.9",
}
<select name="cars" id="cars">
  <option value="volvo">Volvo</option>
  <option value="saab">Saab</option>
  <option value="mercedes">Mercedes</option>
  <option value="audi">Audi</option>
</select>
const opt = {
          jsPDF: {
            unit: "in",
            format: "A4",
            orientation: "portrait",
            compress: true,
          },
          html2canvas: {
            scale: 2,
            useCORS: true,
          },
          image: { type: "png" },
          margin: [0, 0, 0, 0],
          filename: pdfFileName,
          pagebreak: { mode: ["avoid-all", "css"], after: afterBreak },
        };

        html2pdf()
          .set(opt)
          .from(elementToPrint)
          .save()
          .then(() => {
            $(".card").css(
              "box-shadow",
              "0 1px 3px 0 rgba(0,0,0,.2), 0 1px 1px 0 rgba(0,0,0,.14), 0 2px 1px -1px rgba(0,0,0,.12)"
            );
          });

Solution

  • This seems to be a bug in the package itself, even if I upgrade to the latest version still doesn't work. I found that there are two ways for your references.

    Method1: Create a hidden input tag to store the value that you have selected in the select tag. Then hide the select tag and show the input tag while printing the pdf. After finishing, show the select tag again and hide the input tag.

    <select name="cars" id="cars" style="display: block" onchange="onChange()">
      <option value="volvo">Volvo</option>
      <option value="saab">Saab</option>
      <option value="mercedes">Mercedes</option>
      <option value="audi">Audi</option>
    </select>
    <input type="text" value="..." style="display: none" />
    
      const onChange = () => {
        // store value to input
      }
    
      const elShowHide = (elementId: string): void => {
        const el = window.document.getElementById(elementId);
        if (el && el.style.display === "block") {
          el.style.display = "none";
        } else {
          el.style.display = "block";
        }
      }
    

    Method2: If you insist on using these packages, don't use the "html to the image" function that is built in. I recommend using a package like dom-to-image, it can work perfectly for every web page screenshot. First, you can create an image element in JavaScript, then set the source path of this image element after getting the image URL string (base64) from dom-to-image. Finally, add this image element to your elementToPrint container.

      const domToImage = (dom: HTMLElement): Promise<string> => {
        return new Promise((resolve, reject) => {
          const scale = window.devicePixelRatio;
          domtoimage
            .toPng(dom, {
              height: dom.offsetHeight * scale,
              width: dom.offsetWidth * scale,
              style: {
                transform: `scale(${scale})`,
                transformOrigin: "top left",
                width: `${dom.offsetWidth}px`,
                height: `${dom.offsetHeight}px`,
              },
            })
            .then((dataUrl) => {
              resolve(dataUrl);
            });
        });
      }
    
      const imgSrc = await domToImage(...element you want to print);
    
      const container = document.createElement("div");
      const imgEl= document.createElement("img");
      imgEl.src = imgSrc ;
      container.appendChild(imgEl);
    
      elementToPrint.appendChild(container);
      ...
      ...
      ...
    

    Hope it helps!