Html2canvas Live Dom Mutation
What Happened

We wanted screenshots with debug overlays visible and tooltips hidden. The natural approach was to modify the live DOM before calling html2canvas : add the overlay class, remove the tooltip. Users saw those elements flash on screen and immediately disappear. One visible frame of garbage before every screenshot.
Root Cause
See definitions/root-cause-analysis for the analytical framework. Specific cause: html2canvas snapshots the live DOM first, then operates on a deep clone. Any mutations to the live DOM to “prepare” the shot are already visible to the user during the async capture window. The clone happens after the visual damage is done.
How to Avoid
Always operate on the cloned DOM only. html2canvas exposes an onclone callback that provides the deep copy before rendering. Mutations here are invisible to the user:
html2canvas(element, {
onclone: (clonedDoc) => {
// Safe: mutations here are invisible to the user
clonedDoc.querySelector('.debug-overlay')?.classList.add('visible');
clonedDoc.querySelector('.tooltip')?.remove();
}
});
The onclone callback is the correct place for all screenshot preparation: hiding elements, adding debug overlays, force-expanding collapsed panels, and injecting measurement scaffolding. The live DOM should be treated as read-only from the moment html2canvas is called.
Related
- projects/dakka/_index : parent project
- skills/debug-capture-system : skill where this was discovered
- xterm-webgl-canvas-tainted : companion RENDER pitfall: another html2canvas limitation in the same pipeline