JavaScript 剪贴板 Clipboard 的那些事儿!

1,252 阅读3分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第19天,点击查看活动详情


减轻阅读负担,启发创作心智,轻松学习 JavaScript 技巧,日拱一卒,jym,冲~

take-it-easy-relax.gif

本篇带来 —— JavaScript 剪贴板 Clipboard 的那些事儿!


复制粘贴就是互联网最伟大的发明,无论是使用快捷键 ctrl c 、ctrl v,还是鼠标选中右键复制再粘贴,Copy 操作都是非常简单的。

然而要注意的是,你的剪贴板 Clipboard 或许在被有些软件无时无刻的监听着,有时候在知乎上搜的东西,最终出现在了京东商品的推荐页里,让人不禁打个冷战,疑问道:它怎么知道我想买这个东西的?

在 JavaScript 中 navigator.clipboard 全局对象就是来处理剪贴板的,它有以下方法:

  • readText: 读文本
  • read: 读任何类型的数据,除文本外还有图片这些;
  • writeText: 写文本
  • write: 写任何数据;

接下来我们实战来看看 navigator.clipboard 的代码应用:

写入

原生 JS 实现将数据写入剪贴板:

<html>
    <head>
        <script >
            async function copyText() {
                let textArea = document.getElementById("myText")

                const permission = await navigator.permissions.query({ name: 'clipboard-write' });
                if (permission.state === 'denied') {
                    return console.error("Damn, we don't have permissions to do this")
                }
                try {
                    await navigator.clipboard.writeText(textArea.value) // 写入文本
                    console.log("done!")
                } catch (e) {
                    console.error("Error while copying text!")
                    console.error(e.message)
                }

            }
        </script>
    </head>
    <body>
        <textarea rows="5" cols="89" id="myText"></textarea>
        <button onclick="copyText()">Share!</button>
    </body>
</html>

这里要注意的是,在我们写入之前要访问 navigator.permissions.query({ name: 'clipboard-write' }); 用于提前判断我们是否有权限去做写入操作。

除了写入文本,还可以写入图片:

<html>
    <head>
        <script >
            async function copyImg() {
                const type = "image/png";

                let myImg = document.getElementById("myImage")

                let img = await fetch(myImg.getAttribute("src"))

                var blob = new Blob([await img.blob()], { type });
                var data = [new ClipboardItem({ [type]: blob })];

                try {
                    await navigator.clipboard.write(data)
                    console.log("The image was copied")
                } catch (e) {
                    console.error("There was a problem copying the imagge")
                    console.error(e)
                }
            }
        </script>
    </head>
    <body>
        <img src="./img.png" width="300" id="myImage" />
        <button onclick="copyImg()">Share!</button>
    </body>
</html>

与文本稍有不同的是我们要包装一个 ClipboardItem 数组;

读取

与写入数据对应的就是读取数据:

<html>
<head>
    <script >
        async function pasteImage() {
            let destinationImage = document.getElementById("myImage")
            const permission = await navigator.permissions.query({ name: 'clipboard-read' });
            if (permission.state === 'denied') {
                throw new Error('Not allowed to read clipboard.');
            }
            const clipboardContents = await navigator.clipboard.read();
            for (const item of clipboardContents) {
                if (!item.types.includes('image/png')) {
                    throw new Error('Clipboard contains non-image data.');
                }
                const blob = await item.getType('image/png');
                destinationImage.src = URL.createObjectURL(blob);
            }
        }
        
    </script>
</head>
<body>
    <img  width="300" id="myImage" />
    <button onclick="pasteImage()">Get Image!</button>
</body>
</html>

同样需要先确认是否有权限读取:navigator.permissions.query({ name: 'clipboard-read' })

读取图片要用到:URL.createObjectURL 方法。

混淆剪贴内容

这里再提供一个小技巧:假设我们不想自己的文本内容被复制,除了禁止复制以外,还可以用 JS 脚本设置给它赋值一个内容混淆过的内容。

代码如下:

<html>
<head>
    <script >
        function setUpEvent() {
            let mainContent = document.getElementById("main-content")

            mainContent.addEventListener('copy', function(evt) {
                let selectedText = document.getSelection()
                selectedText = selectedText
                                .toString()
                                .split("") //turn it into an array of characters
                                .sort((a,b) => Math.random() > 0.5?1:-1) //shuffle the array
                                .join("") //join it back into a single string
                evt.clipboardData.setData("text/plain", selectedText) //set the clipboard's data
                evt.preventDefault()
            })
        }
        
    </script>
</head>
<body onload="setUpEvent()">
    <p id="main-content">
    For this example, I'm using the HTML IMG tag just to get the src atttribute, since I can't really use the element directly. The API needs me to turn the image into a Blob (which is essentially a way of saying "It's a big chunk of I don't know exactly"). And since the DOM API doesn't have any methods I can use, I'm fetching the actual image and the calling the blob method on the response object (line 11).
The rest is as expected, I create a one-element array with a ClipboardItem containing the blob, and the I write the array into the clipboard.
You can use the above example to extrapolate how you would go about copying something else, like an XLS file. You need to fetch it, blob it, and make sure you used the right mime-type, which is the string I have on the type variable. If you don't know what's the mime-type for your file, just Google "mime-type for <your file's extension here>" and you'll have it.
    </p>
</body>
</html>

我们可以在 www.runoob.com/runcode 即时运行这段代码看看混淆后的剪贴板的内容:

粘贴出来是这样的:

image.png

主要用到了随机数去打乱内容。

挺有意思的,不是让人不能复制,而是让人复制一个乱序的结果,Clipboard 也能这样玩儿~~

OK,以上便是本篇分享。点赞关注评论,为好文助力👍

我是掘金安东尼 🤠 100 万人气前端技术博主 💥 INFP 写作人格坚持 1000 日更文 ✍ 关注我,安东尼陪你一起度过漫长编程岁月 🌏