复制粘贴的高级玩法

im, 无色
im, 无色
im, 无色
919
文章
0
评论
2019年8月12日10:28:33 评论 1,900

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

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

复制粘贴的高级玩法

实现的原理也挺简单:

方法 1:监听键盘事件

(完)

 

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

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

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

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

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

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

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

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

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

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

var defaultText = 'iPresst,一个性感的网站';
document.addEventListener('paste', function(e){
    e.clipboardData.setData('text/plain', defaultText);
    e.clipboardData.setData('text/ipresst', 'ipresst');
    eventType = 'cut';
    // TODO: 获取要剪切的内部元素
}, false);
document.addEventListener('paste', function(e){
    e.clipboardData.setData('text/plain', defaultText);
    e.clipboardData.setData('text/ipresst', 'ipresst');
    eventType = 'copy';
    // TODO: 获取要复制的内部元素
}, false);
document.addEventListener('paste', function(e){
    var clipboard = e.clipboardData;
    // 有无内容
    if(!clipboard.items || !clipboard.items.length){
        clear();
        return;
    }
    // 先区分是内部粘贴还是外部粘贴
    if(clipboard.getData('text/ipresst') === 'ipresst'){
        if(!eventType || !elList.length){
            // TODO: 清空标志位
            return;
        }
        // 粘贴
        if(eventType === 'cut') {
            // TODO: 剪切粘贴
        } else {
            // TODO: 复制粘贴
        }
    } else {
        var temp;
        // ……
        // 此处略去N行前面贴过的代码
    }
}, false);

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

 

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

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

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

复制粘贴的高级玩法

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

(完)

im, 无色
  • 本文由 发表于 2019年8月12日10:28:33
JS粘贴事件paste简单解析及遇到的坑 JavaScript

JS粘贴事件paste简单解析及遇到的坑

在用户执行粘贴操作的时候,js能够获得剪切板的内容,本文讨论一下这个问题。 目前只有Chrome支持获取剪切板中的图片数据。还好需要这个功能的产品目前只支持Chrome和Safari,一些Chrome...
CoffeeScript 是一门让人上瘾的语言 JavaScript

CoffeeScript 是一门让人上瘾的语言

昨天偶然去逛 CoffeeScript 官网发现,这门陪伴我时间最短,但是我最喜欢的语言已经发布了 2.0 版。虽然目前还在 Alpha 阶段,但也足以让我高兴。 新版本主要是对 ES6 的支持加强,...
CoffeeScript介绍 JavaScript

CoffeeScript介绍

CoffeeScript 是一门编译到 JavaScript 的小巧语言. 在 Java 般笨拙的外表下, JavaScript 其实有着一颗华丽的心脏. CoffeeScript 尝试用简洁的方式展...
匿名

发表评论

匿名网友 填写信息

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen: