发布于 

js复制到剪切板

应用场景

在前端开发中经常遇到需要复制内容到剪切板的场景,比如点击一个按钮,把某一条数据的url字段写入剪切板。

那对于这个需求,总结用两种方式:

1、await navigator.clipboard.writeText('xxx') 通过clipboard API写入剪切板(api受限浏览器版本,能支持拷贝图片,未来越多用这个)
2、document.execCommand('copy') 通过document.execCommand(‘copy’)写入剪切板 (兼容性好,缺点不能拷贝图片,拷贝内容比较多,会卡一下)

clipboard API

Clipboard API 是下一代的剪贴板操作方法,比传统的document.execCommand()方法更强大、更合理;
它的所有操作都是异步的,返回 Promise 对象,不会造成页面卡顿。
而且,它可以将任意内容(比如图片)放入剪贴板;
navigator.clipboard属性返回 Clipboard 对象,所有操作都通过这个对象进行;

read()

从剪贴板读取数据(比如图片),返回一个 Promise 对象。When the data has been retrieved, the promise is resolved with a DataTransfer object that provides the data。

1
2
3
4
5
6
7
8
9
10
11
12
13
async function getClipboardContents() {
try {
const clipboardItems = await navigator.clipboard.read();
for (const clipboardItem of clipboardItems) {
for (const type of clipboardItem.types) {
const blob = await clipboardItem.getType(type);
console.log(URL.createObjectURL(blob));
}
}
} catch (err) {
console.error(err.name, err.message);
}
}

readText()

从操作系统读取文本;returns a Promise which is resolved with a DOMString containing the clipboard’s text once it’s available。

1
2
3
4
5
6
7
8
document.body.addEventListener(
'click',
async (e) => {
const text = await navigator.clipboard.readText();
console.log(text);
}
)

write()

写入任意数据至操作系统剪贴板。This asynchronous operation signals that it’s finished by resolving the returned Promise。

1
2
3
4
5
6
7
8
9
10
11
12
13
14

try {
const imgURL = '/images/photo-1676345338852-29fb1026c12f.webp';
const data = await fetch(imgURL);
const blob = await data.blob();
await navigator.clipboard.write([
new ClipboardItem({
[blob.type]: blob
})
]);
console.log('Image copied.');
} catch (err) {
console.error(err.name, err.message);
}

write写入图片的时候,可能会出现,格式等不正确的情况,可以通过将图片转成base64的方式,再转成blob的方式,再写入剪切板。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
//先解析成base64
function imageBase64(img) {
var canvas = document.createElement("canvas");
canvas.width = img.width;
canvas.height = img.height;
var ctx = canvas.getContext("2d");
ctx.drawImage(img, 0, 0, img.width, img.height);
var dataURL = canvas.toDataURL("image/png");
return dataURL;
}
// 再转成blod // 注意base64里面的特殊符号 在atob的时候不能识别“,”
function base64ToBlob(b64Data, contentType, sliceSize) {
contentType = contentType || '';
sliceSize = sliceSize || 512;
var byteCharacters = window.atob(b64Data);
// var byteCharacters = b64Data;
//该atob函数将base64编码的字符串解码为一个新字符串,其中包含二进制数据每个字节的字符。
var byteArrays = [];
for (var offset = 0; offset < byteCharacters.length; offset += sliceSize) {
var slice = byteCharacters.slice(offset, offset + sliceSize);
var byteNumbers = new Array(slice.length);
//通过使用.charCodeAt字符串中每个字符的方法应用它来创建一个字节值数组。
for (var i = 0; i < slice.length; i++) {
byteNumbers[i] = slice.charCodeAt(i);
}
//将此字节值数组转换为实际类型的字节数组,方法是将其传递给Uint8Array构造函数。
var byteArray = new Uint8Array(byteNumbers);
byteArrays.push(byteArray);
}
console.log(byteArrays)
//创建一个blob:包含此数据的URL,并将其显示给用户。
var blob = new Blob(byteArrays, { type: contentType });
return blob;
}

// 最后复制
copy_img.onclick = async _ => {
let base64 = imageBase64(img)
let blob = base64ToBlob(base64.replace('data:image/png;base64,', ''), 'image/png')
clipboardObj.write([
new ClipboardItem({
'image/png': blob
})
])
}

最终写法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
// 先解析成base64
imageBase64(img) {
var canvas = document.createElement("canvas");
canvas.width = img.width;
canvas.height = img.height;
var ctx = canvas.getContext("2d");
ctx.drawImage(img, 0, 0, img.width, img.height);
var dataURL = canvas.toDataURL("image/png");
return dataURL;
},
// 再转成blod // 注意base64里面的特殊符号 在atob的时候不能识别“,”
base64ToBlob(b64Data, contentType, sliceSize) {
contentType = contentType || "";
sliceSize = sliceSize || 512;
var byteCharacters = window.atob(b64Data);
// var byteCharacters = b64Data;
// 该atob函数将base64编码的字符串解码为一个新字符串,其中包含二进制数据每个字节的字符。
var byteArrays = [];
for (var offset = 0; offset < byteCharacters.length; offset += sliceSize) {
var slice = byteCharacters.slice(offset, offset + sliceSize);
var byteNumbers = new Array(slice.length);
// 通过使用.charCodeAt字符串中每个字符的方法应用它来创建一个字节值数组。
for (var i = 0; i < slice.length; i++) {
byteNumbers[i] = slice.charCodeAt(i);
}
// 将此字节值数组转换为实际类型的字节数组,方法是将其传递给Uint8Array构造函数。
var byteArray = new Uint8Array(byteNumbers);
byteArrays.push(byteArray);
}
// 创建一个blob:包含此数据的URL,并将其显示给用户。
var blob = new Blob(byteArrays, {type: contentType});
return blob;
},

// 复制图片到剪切板
copy(file) {
const reader = new FileReader();
reader.onload = (e) => {
const img = new Image();
img.src = e.target.result;
img.onload = () => {
let base64 = this.imageBase64(img);
let blob = this.base64ToBlob(base64.replace("data:image/png;base64,", ""), "image/png");
navigator.clipboard.write([
new ClipboardItem({
"image/png": blob,
}),
]);
};
};
reader.readAsDataURL(file);
},

writeText()

写入文本至操作系统剪贴板。returning a Promise which is resolved once the text is fully copied into the clipboard。

1
await navigator.clipboard.writeText('xxx')

Document.execCommand() 方法

Document.execCommand()是操作剪贴板的传统方法,
各种浏览器都支持。
它支持复制、剪切和粘贴这三个操作。

document.execCommand(‘copy’)(复制)

1
2
3
const inputElement = document.querySelector('#input');
inputElement.select();
document.execCommand('copy');

document.execCommand(‘cut’)(剪切)

1
2
3
const inputElement = document.querySelector('#input');
inputElement.select();
document.execCommand('cut');

document.execCommand(‘paste’)(粘贴)

1
2
3
const pasteText = document.querySelector('#output');
pasteText.focus();
document.execCommand('paste');

实际操作:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
//定义函数
/**
* 复制到剪切板
* @param {String} text 文本
*/
function copyToClipboard(text) {
var textArea = document.createElement("textarea");
textArea.value = text;
document.body.appendChild(textArea);
textArea.select();
try {
var successful = document.execCommand("copy");
var msg = successful ? "successful" : "unsuccessful";
console.log("Copying text command was " + msg);
} catch (err) {
console.log("Oops, unable to copy");
}
document.body.removeChild(textArea);
}

//调用
var text = $("#txt").val();
if (text.length == 0) return;
copyToClipboard(text);

缺点

Document.execCommand()方法虽然方便,但是有一些缺点。
首先,它只能将选中的内容复制到剪贴板,无法向剪贴板任意写入内容。
其次,它是同步操作,如果复制/粘贴大量数据,页面会出现卡顿。有些浏览器还会跳出提示框,要求用户许可,这时在用户做出选择前,页面会失去响应。

扩展

比如在一些平台复制一些内容的时候,人家会在后面加一段自己站点的介绍啥的,这个还可以用paste事件来搞:

1
2
3
4
5
document.addEventListener('paste', async (e) => {
e.preventDefault();
const text = await navigator.clipboard.readText();
console.log('Pasted text: ', text);
});

原理: 用户使用剪贴板数据,进行粘贴操作时,会触发paste事件。

另外比如要实现,例如钉钉、飞书里面那种拷贝图片,然后能去别的平台粘贴的功能,也可以借助:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
const clipboardItems = [];

document.addEventListener('copy', async (e) => {
e.preventDefault();
try {
let clipboardItems = [];
for (const item of e.clipboardData.items) {
if (!item.type.startsWith('image/')) {
continue;
}
clipboardItems.push(
new ClipboardItem({
[item.type]: item,
})
);
await navigator.clipboard.write(clipboardItems);
console.log('Image copied.');
}
} catch (err) {
console.error(err.name, err.message);
}
});

相当于我们截获一下复制了类似图片,然后我们从原有的自定义的格式啥的,转换成了image/png这种格式,然后再写入到剪贴板里面去。

其他文章


如果你有什么意见和建议,可以点击: 反馈地址 进行反馈。