# 监听浏览器的复制粘贴行为

浏览器提供了三个方法来监听复制粘贴行为,分别是copycutpaste,可以看到这些方法返回的类型都是ClipboardEvent,这个对象述了与修改剪切板相关信息的事件,其中 clipboardData 是我们需要关注的对象,这里面包含了剪切板的具体数据。先看看 notion 和一般页面复制的内容有什么不同,主要是 types 类型,我们给 body 加上contenteditable="true",这样就可以在页面中直接编辑内容了,然后在控制台监听 paste 事件,看看复制的内容是什么,

html
<!doctype html>
<html lang="ch">
  <body contenteditable="true">
    <div id="root">test</div>
  </body>
  <script>
    document.addEventListener("paste", function (event) {
      var clipboardData = event.clipboardData;
      console.log("types", clipboardData.types);
      for (const type of clipboardData.types) {
        console.log("data", clipboardData.getData(type));
      }
      for (const item of clipboardData.items) {
        console.log("item", item);
      }
    });
  </script>
</html>

从 notion 复制两条内容到浏览,实际输出的内容如下,可以看到输出了很多行,但如果如你看看下 html 结构,实际上浏览器只插入了text/html的 html 内容。

看到这里其实就可以给出结论了,浏览器会根据不同的输入元素读取不同的类型,比如input之类的元素会读取text/plain,而contenteditable的元素会读取text/html,这里类型其实就是 media-types,通常这些类型操作系统会自动处理。

而除了一般的 Media type,也可以自定义的类型,例如text/_notion-multi-text-production,这些自定义的类型浏览器默认是不会读取和操作的,需要手动监听和读取,而通过读取这些自定义类型,就可以实现一开始提到 notion 的复制粘贴操作了,也是可以看到复制粘贴操作携带的数据可能远比我们看到的多。顺便一提。我而在写文章时我发现 vscode 也有类似的自定义类型的。

写入自定义类型

写入自定义类型也很简单,我们需要劫持copy事件就好。

js
document.addEventListener("copy", function (event) {
  event.preventDefault();
  var clipboardData = event.clipboardData!;
  clipboardData.setData("text/plain", "Hello, world!");
  clipboardData.setData("text/html", "Hello, world!");
  clipboardData.setData("text/hello", "Hello, world!");
});

运行上面方法后,会发现复制的内容都成Hello, world!,同时也增加了一个text/hello的类型,这样就可以实现自定义类型的复制粘贴了。值得注意的是,因为直接修改复制事件内容是不会生效的,所以我们需要调用event.preventDefault()来阻止默认行为,然后通过clipboardData.setData来设置内容,要完全兼容原生的复制粘贴,可能需要考虑到各种细节。