html2canvas Option `onclone` Not Working as Expected with jsPDF
When passing a function to the html2canvas option onclone via jsPDF, via the html module's html2canvas property, the generated PDF does not reflect the changes in the cloned document.
Some self-contained html is below to copy+paste, which demonstrates what seems to be happening. From what I can tell, jsPDF is using an overlay and container for its document source, while html2canvas creates its own container for its cloned document, which jsPDF never seems to reference.
I didn't see any existing issues around this, so not sure if I'm missing something, using incompatible versions of something, etc. Thanks for any help!
<html>
<head>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jspdf/2.1.1/jspdf.umd.js"></script>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/html2canvas.js"></script>
<style type="text/css">
.html2pdf__overlay {
margin: 12px 0;
visibility: visible !important;
position: unset !important;
border: solid 1px red !important;
height: 250px !important;
width: 500px !important;
}
.html2canvas-container {
visibility: visible !important;
position: unset !important;
border: solid 1px blue !important;
height: 250px !important;
width: 500px !important;
}
</style>
</head>
<body>
<div id="mainContainer">
<span id="mainText">Original Document</span>
</div>
<button id="jsPdfButton">Generate PDF</button>
<div>
<span style="color: red;">jsPDF / html2pdf overlay is red</span><br />
<span style="color: blue;">html2canvas container for cloned document in blue</span>
</div>
<script type="text/javascript">
function delay(t, v) {
return new Promise(function (resolve) {
setTimeout(resolve.bind(null, v), t)
});
}
const { jsPDF } = window.jspdf;
const doc = new jsPDF("portrait", "pt", "a4");
document.getElementById("jsPdfButton").addEventListener("click", (event) => {
doc.html(document.getElementById("mainContainer"), {
html2canvas: {
removeContainer: false,
onclone: async function (doc) {
// Change text in cloned document, which should reflect in generated PDF
// Slowing it down to see what's going on...
await delay(2000);
doc.getElementById("mainText").innerHTML = "Cloned Document";
await delay(2000);
}
},
callback: function (doc) {
window.open(doc.output("bloburl"));
}
});
});
</script>
</body>
</html>
The html2canvas option is currently not for passing options to html2canvas, but for injecting the html2canvas module. We should probably improve this situation, though.
The
html2canvasoption is currently not for passing options to html2canvas, but for injecting the html2canvas option.
Thanks - not sure I understand what the difference is? The Html2CanvasOptions type supports the onclone option, and other options are respected by html2canvas.
In lieu of improving the situation, is there some way we can improve the documentation maybe? Not entirely sure what we'd say, but hoping to figure it out :)
Ah sorry, I wrote "html2canvas option" instead of "html2canvas module". The typings suggest that you can pass html2canvas options to html2canvas, but that's unfortunately not true/not implemented. I'm willing to accept a PR that will improve this situation. Passing options to html2canvas should really be possible. For backwards compatibility, we should probably check if the argument is the html2canvas function or the options object and treat it respectively.
Try use it like this, i didn't found problem with that.
const PDFisLoadingEl = document.getElementById('PDFisLoading');
const removeLoadingModal = () => PDFisLoadingEl.remove();
pdfDoc.html(document.body, {
callback: function (JsPDFinstance) {
JsPDFinstance.save();
},
filename,
html2canvas: {scale: 0.1, onclone: removeLoadingModal},
x: 0,
y: 0
});
This issue is stale because it has been open 90 days with no activity. It will be closed soon. Please comment/reopen if this issue is still relevant.
there is any update about that? i have a similar problem, my changes in the onclone function are reflected in the cloned document but only few are showed when the premises is solved
It seems that the issue is due to the fact that the styles of the clone element used by jsPDF keep the same properties as the source html element. It would be necessary to be able to apply to them the same modifications made in the onclone option for that to work.
html2canvas onclone is necessary in order to intercept and modify elements in the finished document before final output. The current workaround is to chain it by directly installing and using html2canvas and using the final canvas object to create the jsPDF.
Has this changed? @HackbrettXXX stated previously:
The typings suggest that you can pass html2canvas options to html2canvas, but that's unfortunately not true/not implemented. I'm willing to accept a PR that will improve this situation. Passing options to html2canvas should really be possible.
However, the current docs show the html2canvas param to .html() as type Html2CanvasOptions, and other params appear to be passed to html2canvas correctly now. However, I'm still not seeing changes made in the onclone callback reflected in the pdf. With html2canvas logging enabled (by passing an options object to html2canvas in fact), I see the clone start, the onclone callback called, and the clone end, but my pdf if still not getting the changes.
@HackbrettXXX Apparently on my rendered canvas, l have noticed that some css properties such as left are not maintained and as such the rendered image is out of the actually required viewport.
I have tried the onclone option to add the style back to my document, but its not being fulfilled unfortunately. I have also noticed that as a matter of fact, the required section had already been turned a canvas regardless of the callback function implement. Simple console.logging has ascertained this.
Is there a fix or get-around to the onclone method. Please assist, its a bit important...thanks
Hi there, I was looking for a solution for my similar issue and found this thread, for me these steps bellow make the original post works:
- Upgrade the html2canvas to version 1.4.1
- Replace
onclone: async function(doc)byonclone: async function(doc, element)anddoc.getElementById("mainText")byelement.querySelector("#mainText")
I also created a fiddle to test https://jsfiddle.net/renatobl/emzjufr2/ Thanks