Async Clipboard AP
转自奇舞周刊,个人学习记录,侵权删
编者按:本文作者李松峰,资深技术图书译者,翻译出版过40余部技术及交互设计专著,现任360奇舞团高级前端开发工程师,360前端技术委员会委员、W3C AC代表
如果我问:你知道“剪贴板”(clipboard)吗?
恐怕没人不知道。我们每天都不知道自己要在电脑或手机上“复制”、“粘贴”多少回。每次“复制”、“粘贴”的背后,都会用到“剪贴板”。
根据“维基百科”:
The clipboard is a data buffer used for short-term data storage and/or data transfer between documents or applications used by cut, copy and paste operations and provided by the operating system.
翻译一下:
剪贴板是一种数据缓存,用于文档或应用间短期数据的存储/转移,在用户执行剪切、复制和粘贴操作时会用到,由操作系统提供。
这里最重要的一点在于,“剪贴板”是“由操作系统提供”的,因此它是系统级的一个软件特性。
对于前端开发者来说,如果我问:你知道怎么操作“剪贴板”吗?
很多人的第一反应可能是:使用clipboard.js吧……
clipboard.js的原理
clipboard.js(https://clipboardjs.com/)是在Github上有24000多个星,其流行程度可见一斑。关于这个库的用法,大家可以自己去看,我们这里主要分析其实现原理,以便了解目前操作剪贴板的主流技术。
简单来说,clipboard.js利用了两个已有的Web API(前者属于HTML5,后者属于HTML Editing API):
HTMLInputElement.select()
document.execCommand()
相应地,原理也只有两步。
第一步,创建临时的texterea元素、通过CSS隐藏起来,然后把要复制的文本赋值给这个文本区,再选择这个文本区中的所有内容:
// https://github.com/zenorocha/clipboard.js/blob/master/src/clipboard-action.js
/**
* Creates a fake textarea element, sets its value from `text` property,
* and makes a selection on it.
*/
// selectFake()
// 创建临时的texterea元素
this.fakeElem = document.createElement('textarea');
// 隐藏这个元素
this.fakeElem.style.position = 'absolute';
this.fakeElem.style[ isRTL ? 'right' : 'left' ] = '-9999px';
// 把要复制的文本赋给文本区并选择全部内容
this.fakeElem.value = this.text;
this.selectedText = select(this.fakeElem);
// 触发复制
this.copyText();
第二步,通过脚本触发copy操作,如果成功则文本会写入剪贴板,然后根据执行结果派发自定义事件:
// https://github.com/zenorocha/clipboard.js/blob/master/src/clipboard-action.js
/**
* Executes the copy operation based on the current selection.
*/
// copyText()
succeeded = document.execCommand(this.action);
this.handleResult(succeeded);
/**
* Fires an event based on the copy operation result.
* @param {Boolean} succeeded
*/
handleResult(succeeded) {
this.emitter.emit(succeeded ? 'success' : 'error', {
action: this.action,
text: this.selectedText,
trigger: this.trigger,
clearSelection: this.clearSelection.bind(this)
});
}
如前所述,剪贴板是由操作系统提供的,是系统级的。浏览器厂商为安全和用户体验考虑,只信任用户通过应用、文档或脚本触发的复制操作。而且,复制到剪贴板的内容来源还必须是已有的DOM元素。
以上复制操作的结果是将文本内容写入剪贴板。如果要读取剪贴板中的内容,必须给粘贴(paste)事件操作注册处理程序,通过e.clipboardData.getData()方法获取剪贴板中的内容。这样可以在用户把内容粘贴到目标输入框之前,对剪贴板上的内容进行一些预处理。但这不是本文的重点。
虽然clipboard.js使用的这种技术几乎所有浏览器都支持,但也存在诸多缺陷。
已有技术的缺陷
以前的技术存在什么失陷呢?除了前面需要通过编程方式通过execCommand模拟用户触发剪贴板的复制操作之外,还有:
execCommand的初衷是编辑DOM,而且浏览器间实现存在不少差异
复制粘贴操作是同步的,会阻塞主线程,导致页面无法响应
如果进而再弹窗申请授权,则可能会惹恼用户
相应地,妨碍对某些类型的数据(如图片)进行无害化处理或转码操作
而为避免外部利用,有时候转码又是必需的
为了克服这些问题,W3C开始着手制定相关标准:Clipboard API and events(https://www.w3.org/TR/clipboard-apis/)。
Clipboard API and events定义了Async Clipboard API,相应地增加了clipboardchange事件。
Async Clipboard API
换句话说,为避免阻塞主线程,这个新标准引入了基于Promise的异步剪贴板API。由于剪贴板是系统级软件特性,所以相应的API挂载到了navigator上面:
navigator.clipboard
这个clipboard对象有4个方法:
Promise<DataTransfer> read();
Promise<DOMString> readText();
Promise<void> write(DataTransfer data);
Promise<void> writeText(DOMString data);
两个读剪贴板,两个写剪贴板。
支持读写以下数据类型:
text/plain
text/uri-list
text/csv
text/css
text/html
application/xhtml+xml
image/png
image/jpg, image/jpeg
image/gif
image/svg+xml
application/xml, text/xml
application/javascript
application/json
application/octet-stream
read()
navigator.clipboard.read().then(function(data) {
for (var i = 0; i < data.items.length; i++) {
if (data.items[i].type == "text/plain") {
console.log("Your string: ", data.items[i].getAs("text/plain"));
} else {
console.error("No text/plain data on clipboard.");
}
}
});
readText()
navigator.clipboard.readText().then(function(data) {
console.log("Your string: ", data);
});
write(data)
var data = new DataTransfer();
data.items.add("text/plain", "Howdy, partner!");
navigator.clipboard.write(data).then(function() {
console.log("Copied to clipboard successfully!");
}, function() {
console.error("Unable to write to clipboard. :-(");
});
writeText(data)
navigator.clipboard.writeText("Howdy, partner!").then(function() {
console.log("Copied to clipboard successfully!");
}, function() {
console.error("Unable to write to clipboard. :-(");
});
注意事项
navigator.clipboard只能在“安全上下文”中使用。什么是“安全上下文”?简单说,就是localhost和HTTPS环境下。(可以通过window.isSecureContext属性取得。)
桌面浏览器中目前只有Chrome、Firefox和Opera支持,Safari和IE/Edge还不支持;而且,Chrome也只支持readText()和writeText()。
参考链接:
https://clipboardjs.com/
https://www.w3.org/TR/clipboard-ap
https://developers.google.com/web/updates/2018/03/clipboardapi
https://w3c.github.io/webappsec-secure-contexts
Async Clipboard AP的更多相关文章
- [React Native]访问操作系统剪贴板 Clipboard
我们之前学习了TextInput组件, 有时候我们需要在TextInput组件中复制或者粘贴一些文字. React Native为开发者提供了 Clipboard API,Clipboard 组件可以 ...
- [笔记]New in Chrome 66
原文 CSS Typed Object Model 使用CSS object model,返回的一切都是字符串 el.style.opacity = 0.3; console.log(typeof e ...
- Chrome 66 新增异步剪贴板 API
在过去的几年里我们只能使用 document.execCommand 来操作剪贴板.不过,这种操作剪贴板的操作是同步的,并且只能读取和写入 DOM. 现在 Chrome 66 已经支持了新的 Asyn ...
- C#学习日志 day8 -------------- async await 异步方法入门(引用博客)以及序列化和反序列化的XML及json实现
首先是异步方法的介绍,这里引用自http://www.cnblogs.com/LoveJenny/archive/2011/11/01/2230933.html async and await 简单的 ...
- Clipboard深度实践与采坑记录
1.css禁止选择导致IOS无法复制 body{ -webkit-touch-callout: none; -webkit-user-select: none; -khtml-user-select: ...
- clipboard异步复制文本(动态获取文本)
1.需求描述: 点击“分享”按钮的时候,发送 ajax 请求获得动态邀请连接,成功取得数据后复制到剪贴板 2.解决重点: > Clipboard 动态设置文本的使用 > Ajax请求设置为 ...
- clipboard.js 实现动态获取内容并复制到剪切板
使用clipboard.js分为以下几个步骤: 1.引入一个clipboard.js的文件: 2.新建一个clipboard对象: 3.点击按钮获取目标对象里面的内容,将其复制到剪切板. 注意:1.目 ...
- 2018-9-30-win10-UWP-剪贴板-Clipboard
原文:2018-9-30-win10-UWP-剪贴板-Clipboard title author date CreateTime categories win10 UWP 剪贴板 Clipboard ...
- async包 ES6 async/await的区别
最基本的async 包 ApCollection.find({}).toArray(function (err, aps) { var num = 0; async.whilst( function ...
随机推荐
- @PathVariable、@RequestParam、@RequestBody注解
讲解更加详细的参考资料 https://blog.csdn.net/u011410529/article/details/66974974 https://www.cnblogs.com/soul-w ...
- Java集合类框架的最佳实践有哪些
根据应用的需要正确选择要使用的集合的类型对性能非常重要,比如:元素的大小是固定的,而且能事先知道,我们就应该用Array而不是ArrayList. 有些集合类允许指定初始容量.因此,如果我们能估计出存 ...
- mongodb与java的整合
mongodb的相关命令我们这里不在赘述,因为其文档下写的非常清楚,也很容易懂.这里我们说一下其余java的整合,mongodb配置请查看官方文档 1.首先我们应该导入期相关依赖, org.mongo ...
- parse_str()函数怎么用?
php parse_str()函数 语法 parse_str()函数怎么用? php parse_str()函数表示将字符串解析成多个变量,语法是parse_str(string,array),如果未 ...
- RzPageControl(pagecontrol)
实现多标签的动态添加,切换,关闭 使用RzPageControl来实现多标签页使用菜单来打开标签页,通过标签页的caption来判断将标签页是否已经被打开过了.1.创建标签页,并判断是否是已经打开过的 ...
- 2019ccpc秦皇岛/Gym102361 I - Invoker dp
题意: 连续3个特定的按键(在这3个中不要求顺序)能使出某个技能,使出不同技能所需要的按键可以重叠,给你一个技能序列,问你最少花费多少次按键能按顺序使出这些招数. 题解: dp,dp[i][j]代表使 ...
- Linux C遇到的常见错误
此随笔主要记录一些Linux C遇到的常见错误,便于debug问题或自己编程时,避免发生类似的错误或问题,后续会持续更新.... 1.内存泄露问题 内存泄露是由于内存没有释放导致程序耗内存一直增大,引 ...
- (55)C# windows 消息
窗体捕获消息 namespace WindowsFormsApp1 { public partial class Form1 : Form { public Form1() { InitializeC ...
- sed 对文件进行操作
首先我们想不进入一个文件 对文件进行操作 那么久需要用到sed了 在某个变量之前添加内容: sed -i 's/原内容/要添加内容/g' 文件名 sed -i 's/原内容/要添加内容&/' ...
- mysql添加制定ip访问
GRANT ALL PRIVILEGES ON *.* TO 'root'@'120.244.114.45' IDENTIFIED BY 'Zhh722@7758521' WITH GRANT OPT ...