TAT.老教授 复制粘贴的高级玩法
In 未分类 on 2015年04月26日 by view: 13,044
5

想做一个好用的在线编辑器,不管是地图编辑器、PPT 创作平台还是通过拖拽快速创建活动页面的编辑器等等,必然要给用户提供各种快捷的操作方法。如非常常用的复制粘贴功能。

举个例子,在 iPresst 创作平台,我们的作品在好几页都要用到同一张图片,总不能每次都点击上传一次图片吧?右键复制粘贴或者直接按快捷键无疑是最符合用户预期的操作方式,然而我们编辑器用到的元素一般比较特别,而且我们复制粘贴的时候经常要做一些特殊处理,此时我们就需要覆盖浏览器给我们提供的复制粘贴功能了。

QQ20150426-1@2x

实现的原理也挺简单:

方法 1:监听键盘事件

此时我们要覆盖掉浏览器的默认右键菜单,不然快捷键和右键菜单的复制粘贴操作效果不一致。这并不奇怪,一般的稍复杂的编辑器都有定制自己的右键菜单。

方法 2:直接覆盖剪切复制粘贴事件

如此我们就可以定制我们编辑器的特色复制粘贴功能。

(完)

 

开玩笑,如果就这样结束那也太水了,前面那些只是铺垫,铺垫,咳咳。

上面的代码只是实现了编辑器的内部元素复制粘贴的闭环,那来自外部的元素呢?如别的地方拷贝的一段文本,如用 QQ 截了一张图,能否直接粘贴在我们的编辑器生成特有的文本元素、图片元素?

这就是接下去要讲的高级玩法,Clipboard API。

其实访问剪贴板的数据这并不新鲜,早在多年前 IE 就支持了,我们可以通过下面的方式访问:

但这种接口注定沦为历史的尘埃。为什么?不安全!如果用户打开一个网页,在他不知不觉中 JavaScript 就访问了系统剪贴板的数据,然后上传到服务器或者做各种猥琐的操作,那用户会泄露多少的隐私。所以在新的浏览器如 chrome 是不支持这种接口的,一般情况下 js 代码是访问不到系统的剪贴板,我们在网上看到的点击复制网址之类的功能,基本都是用 Flash 来实现。

那如果用户点击了浏览器右键菜单的复制粘贴或按下相应快捷键,此时访问剪贴板就合理了,而浏览器确实是这么做的。前面提到的方法 1 监听键盘事件是不行的,此时必须使用方法 2,我们可以通过下面代码获取到剪贴板里的图片或者文本:

要注意两个小点,第一,我们通过上面获取到的图片文件是一个 file 对象,这跟我们从一个 type=file 的上传文件节点监听 change 事件,通过 e.target.files[0] 拿到的 file 对象是一样的(之所以监听 change 是为了实现选择文件即时上传的效果不用额外点击上传按钮)。从 file 对象中可以获取到图片的 base64 编码:

file 对象中还可以获取到文件类型等信息,大家想更深入了解可以搜索 e.target.files 。

第二个要注意的点是从剪贴板获取到的文本是系统格式的,如果我们不做处理直接通过类似 innerHTML 的方法使用,会导致换行丢失等显示问题。

Ok,大家可以在 iPresst 的编辑创作页面体验效果,QQ 截完图可以直接粘贴进来的感觉就是爽!

但是这时候有另外一个问题,怎么保持内部元素的复制和外部元素复制的统一?简单讲,我在编辑器里面复制了我的特有元素,此时系统的剪贴板不管有什么都应该被覆盖,反之亦然,我在编辑器里面复制一个特有元素,然后在别的地方复制了一段文本,那此时我在编辑器里面粘贴应该是粘贴这段文本而不是粘贴之前的特有元素。

要做到这一点,只要处理好两个事情:在编辑器里剪切复制的时候覆盖剪贴板、在编辑器里粘贴时区分要粘贴的是内部元素还是外部元素。程序员嘛,直接上代码:

我们在剪贴板里面设置了我们的特色数据 text/ipresst ,如果用户在其他地方剪切复制了东西,剪贴板会被清空这个标志位就不存在,所以可以用来区分内部粘贴和外部粘贴。而这行代码

则让我们复制了内部元素然后在外面如 QQ 聊天窗口粘贴时(显然在聊天窗口没法粘贴我们编辑器的内部特有元素),贴出文本:iPresst,一个性感的网站。so cool!

此时我们内部和外部的闭环就打通了。只是很遗憾地,为了保持交互逻辑的一致性,我不得不把 iPresst 的自定义右键菜单中剪切、复制、粘贴这几项去掉,因为点击事件没法访问到剪贴板对象(只有 cut\copy\paste 可以访问到),也就说没法粘贴外部元素,和按下快捷键的表现是不一致的。这一点没有更好的解决方案,当然你放弃自定义右键菜单就不会有这个问题。

或许有人会说:那我们可以点击右键菜单的复制粘贴时,通过 execCommand 或者模拟键盘事件来触发 cut、copy、paste 事件,那不就可以访问到剪贴板了?我只能说:朋友,你想多了。那样会跟前面讨论的 IE 的接口一样,有安全风险的,我自测过在 chrome 是行不通的。在 caniuse.com 上面也是这样写:

QQ20150426-5@2x

至此,复制粘贴的高级玩法讲完了,虽说还有点小不满意的点,但还是一个比较推荐的实用性挺高的实践。

(完)

 

(真的完了)

原创文章转载请注明:

转载自AlloyTeam:http://www.alloyteam.com/2015/04/how-to-paste-zhuangbility/

  1. 老表 2017 年 9 月 8 日

    【修正后的评论】

    写的不错,满满的干货!

    不过有点小小的纠正

    在 paste 事件中,有句代码
    temp = clipboard.items[0]

    这句不太严谨,有时候 file 不一定能通过第 0 个元素的 getAsFile() 方法拿到,例如在网页上直接对图片右键复制后,回到楼主的粘贴事件中,则 clipboard.items[0].kind 不是 file,而是 string,而 clipboard.items[1].kind 才是 file.

    所以判断方式建议改为:循环一下 items,碰到 kind=file 且 type 为 image 开头的,才断定为图数据

    • refn 2018 年 3 月 20 日

      赞,确实不太严谨。算一个 bug

  2. TAT.joeyguo

    TAT.joeyguo 2016 年 5 月 24 日

    好文

  3. xyz 2015 年 10 月 22 日

    赞赞赞赞

  4. TAT.dnt

    dntzhang 2015 年 4 月 26 日

    赞一个!

发表评论