
1. Copy the DOM and serialize
const cloneNode = document.body.cloneNode(true);
const xmlSerializer = new XMLSerializer();
const html = xmlSerializer.serializeToString(cloneNode);
Code language: JavaScript (javascript)
2. Embed svg foreignObject
const svg = `
<svg
xmlns='http://www.w3.org/2000/svg'
width='${document.body.clientWidth}'
height='${document.body.clientHeight}'
>
<foreignObject
x='0'
y='0'
width='100%'
height='100%'
>
${html}
</foreignObject>
</svg>
`;
Code language: HTML, XML (xml)
3. Generate images through canvas
const canvas = document.createElement('canvas');
canvas.width = document.body.clientWidth;
canvas.height = document.body.clientHeight;
const ctx = canvas.getContext('2d');
const img = new Image();
img.onload = () => {
ctx.drawImage(img, 0, 0);
canvas.toBlob((blob) => {
const blobURL = URL.createObjectURL(blob);
window.open(blobURL);
URL.revokeObjectURL(blobURL);
});
};
img.src = `data:image/svg+xml;charset=utf-8,${svg}`;
Code language: JavaScript (javascript)
Problem: Image content has no style
The svg is loaded as a string via img src data and does not share styles with the current page.
The screenshot plug-ins html2canvas
and dom-to-image
solve this problem through inline styles. Deeply traverses each source DOM element, each DOM element passes all style properties and values of the current element through window.getComputedStyle
method
Find the corresponding cloned DOM element and reassign it via getPropertyValue
and setProperty


DOM operations are slow !!!
A simple calculation, each DOM has 300+ style attributes, assuming that each DOM has 5 valid styles, 100 DOMs need to copy styles 500 times. Assuming 0.5 milliseconds per copy, that would take 250 milliseconds.
If conditions permit, you can prepare style content in advance, embed svg, remove this link, and improve the efficiency of screenshots.
const svg = `
<svg
xmlns='http://www.w3.org/2000/svg'
width='${document.body.clientWidth}'
height='${document.body.clientHeight}'
>
+ <style>
+ ${svgStyle}
+ </style>
<foreignObject
x='0'
y='0'
width='100%'
height='100%'
>
${html}
</foreignObject>
</svg>
`;
Code language: HTML, XML (xml)

After the style synchronization link is removed, the speed is improved.
Problem: The font of the image content is missing
Like the reason for the loss of image content styles, fonts also need to be embedded in svg. Because it does not share resources with the current page, the font resource reference cannot be in URL form and needs to be converted into base64 format.
const response = await fetch(url, { headers: { responseType: 'blob' } });
const blob = await response.blob();
const fileReader = new FileReader();
fileReader.onload = () => {
const b64 = fileReader.result;
};
fileReader.readAsDataURL(blob);
Code language: JavaScript (javascript)
svgStyle adds font declarations.
svgStyle += `
@font-face {
font-family: "${name}";
src: local("${name}"), url("${b64}");
}
`;
Code language: JavaScript (javascript)
Other way
Refer to
render-html-to-an-image
rendering-web-content-to-image-with-svg-foreign-object