One‑Click Web Area Screenshot: Using Clipboard API & DOM‑to‑Image
This article explains how to implement a one‑click precise screenshot feature in a web application by leveraging the Clipboard API, converting DOM elements to images via SVG foreignObject, handling styles and resources, and finally writing the image to the system clipboard.
Introduction
When colleagues need to share a screenshot of a specific area of the company's middle‑platform web page, the front‑end developer wants to create a one‑click precise screenshot feature to improve communication efficiency.
The demo effect of the feature is shown below:
Next, let’s see how to achieve this functionality.
Clipboard API in the Browser
The Clipboard interface implements the Clipboard API, which, after the user grants permission, provides read/write access to the system clipboard. It replaces the less secure document.execCommand method and works asynchronously, returning Promise objects.
Compatibility is still evolving; key points to note: navigator.clipboard must be used in a secure HTTPS context.
Read/write operations require user authorization, which is now part of the Permissions API.
The page must be in an active tab to invoke the API.
All Clipboard operations are asynchronous and return Promise objects, allowing arbitrary content to be placed on the clipboard.
writeText
navigator.clipboard.writeTextwrites a string (a DOMString) to the operating system clipboard.
await navigator.clipboard.writeText('hello, world!');write
navigator.clipboard.writesupports writing any data (text or binary) to the clipboard. In practice, a ClipboardItem is constructed with an object whose keys are MIME types and values are Blob objects.
Example: copy an image to the clipboard.
const blob = await fetch('image-remote-url').then(r => r.blob());
await navigator.clipboard.write([
new ClipboardItem({
[blob.type]: blob,
}),
]);Now the image is on the clipboard. If we first convert a DOM element to an image, we can achieve a one‑click copy feature.
Converting DOM to Image
Popular npm libraries such as html2canvas and dom-to-image render a DOM element onto a canvas and then export it as an Image or SVG. Using the SVG foreignObject element simplifies the process: embed the HTML inside foreignObject to generate an image.
Node Cloning
First, clone the node. Because each node may need special handling (styles, resources), we cannot simply call Node.cloneNode; instead we manually traverse and clone.
Clone Node
If the node is an HTMLCanvasElement, convert it to a DataURL and return an Image object; otherwise use cloneNode(false).
function copyNode(original) {
if (original instanceof HTMLCanvasElement) {
return new Promise((resolve, reject) => {
const image = new Image();
image.src = original.toDataURL();
image.onload = resolve(image);
image.onerror = reject;
});
}
return original.cloneNode(false);
}Clone Child Nodes
Recursively clone all child nodes.
/**
* @param {*} original 原始节点
* @param {*} clone copyNode()返回的克隆节点
* @returns 已克隆子节点的克隆节点
*/
function copyChildren(original, clone) {
const children = original.childNodes;
if (children.length === 0) return Promise.resolve(clone);
return cloneChildrenInOrder(clone, Array.from(children)).then(clone);
}Style Handling
After cloning, the original styles are lost, so we need to inline them.
Clone Style
Use getComputedStyle to obtain computed styles and apply them as inline styles on the clone.
function cloneStyle(original, clone) {
const source = window.getComputedStyle(original);
Array.from(source).forEach(name => {
clone.style.setProperty(
name,
source.getPropertyValue(name),
source.getPropertyPriority(name)
);
});
}Clone Pseudo‑Elements
Pseudo‑elements like :before and :after cannot be cloned directly; we extract their styles via getComputedStyle (with the pseudo‑element argument) and add equivalent CSS rules.
function clonePseudoElements(original, clone) {
[':before', ':after'].forEach(pseudo => {
const style = window.getComputedStyle(original, pseudo);
const content = style.getPropertyValue('content');
generateCssRules(style, pseudo);
});
}Image and Background URLs
For img tags, convert the src to a DataURL. For CSS background images, replace the URL with a DataURL as well.
<img src="data:image/svg+xml;base64,PD94b...XXXXX"> background: url('data:image/png;base64,iVBOR...')Watermark
If the DOM image may contain sensitive data, add a watermark using a canvas.
function createWaterMark(node, config) {
return new Promise(resolve => {
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
ctx.fillText(content, 50, 50);
const backgroundUrl = canvas.toDataURL();
// ...
});
}Serialization and Rendering
Serialize Node to XML
After deep cloning and inlining styles, embed the node in an SVG foreignObject and serialize it.
const xml = new XMLSerializer().serializeToString(node);
const svg = `<svg ...><foreignObject x="0" y="0" width="100%" height="100%">${xml}</foreignObject></svg>`;
const dataUrl = `data:image/svg+xml;charset=utf-8,${svg}`;The resulting DataURL can be drawn onto a canvas.
Canvas Drawing
Draw the SVG image onto a canvas, convert it to a Blob, and write the Blob to the clipboard.
const img = new Image();
img.src = dataUrl;
canvas.getContext('2d').drawImage(img, 0, 0);
canvas.toBlob(writeBlobToClipboard);Conclusion
This article analyzed the process of copying a DOM region to the clipboard, introduced the features of the Clipboard API, and demonstrated how to convert DOM elements into images. The techniques can also be applied to other scenarios such as popup screenshots, poster generation, business cards, and QR‑code capture.
References
[1]Clipboard API: https://developer.mozilla.org/zh-CN/docs/Web/API/Clipboard [2] ClipboardItem: https://developer.mozilla.org/zh-CN/docs/Web/API/ClipboardItem [3] Full demo code: https://codepen.io/YUKCHING/pen/OJZzgXa
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
