剪贴板自定义类型跨浏览器支持

原创 2017年01月03日 14:11:22

引子

在编辑器开发中, 遇到了需要设置内容到剪贴板和获取并剪贴板内容的情况. 有关以下问题

  • 跨浏览器获取Clipboard
  • 从剪贴板中获取不同类型数据

可以参考 THE DEFINITIVE GUIDE TO COPYING AND PASTING IN JAVASCRIPT

下面分享以下在 THE DEFINITIVE GUIDE TO COPYING AND PASTING IN JAVASCRIPT 基础上如何实现跨浏览器支持自定义类型.

自定类型浏览器支持情况

THE DEFINITIVE GUIDE TO COPYING AND PASTING IN JAVASCRIPT 一文我们可以了解
clipboardData.setData(type, value) API 中 type 在不同浏览器中的支持情况:

  • Chrome and Safari: They support any content type on the clipboardData, including custom types. So, we can call clipboardData.setData('application/lucidObjects', serializedObjects) for pasting, and then call var serialized = clipboardData.getData('application/lucidObjects')
  • Firefox: It currently only allows access to the data types described above. You can set custom types on copy, but when pasting, only the white-listed types are passed through.
  • Internet Explorer: In true IE fashion, it supports just two data types: Text and URL. Oh, and if you set one, you can’t set the other (it gets nulled out). There is a hack, however, that also allows us to indirectly get and set HTML.

Windows Edge 浏览器的情况与 FireFox 类似 ( Windows Edge 浏览器2016年才发布, THE DEFINITIVE GUIDE TO COPYING AND PASTING IN JAVASCRIPT 一文写于 2014 年, 故没有提到 Windows Edge ).

在编辑器(仅支持 Webkit 内核浏览器和 Windows Edge )中, 除了设置text/plain, text/html 到剪贴板外, 还会用到挺多自定义类型的数据 (如 text/yne-table-json, text/yne-json). 自定义类型数据的主要使用场景是:

  • 同一个端不同笔记间(同一个编辑器, 不同实例)相互拷贝粘贴
  • 不同端的编辑器(不同编辑器, 不同实例)相互拷贝粘贴, 如从PC端中的编辑器拷贝, 粘贴到Web浏览器的编辑器中

但 Windows Edge 浏览器的剪贴板又不支持自定义类型数据, 于是琢磨着如何应对 Windows Edge 浏览器.

  1. 在 Windows Edge 下 try ... catch ... 设置内容到剪贴板, 如果不能设置到剪贴板, 则不设置.
  2. 无论在 Webkit 内核浏览器还是 Windows Edge 中, 都能统一处理设置到剪贴板.

第一种解决方案肯定不太好, 而第二种方案又该如何实现呢?

思路

Clipboard API and events - Mandatory data types 可以发现, 剪贴板支持读取的数据类型:

  • 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

剪贴板支持写入的数据类型

  • text/plain
  • text/uri-list
  • text/csv
  • text/html
  • image/svg+xml
  • application/xml, text/xml
  • application/json

无论读取还是写入剪贴板, 都支持写入类型.

能否将自定义类型数据 JSON 序列化后写入标准写入类型中的一种呢? 要读取时,也可以
从剪贴板中读取该标准写入类型, JSON 处理后再提取出自定义类型.

参考代码

后来实验成功, 以下是参考代码:

DataTransfer.js


/**
 *
 * @see https://www.lucidchart.com/techblog/2014/12/02/definitive-guide-copying-pasting-javascript
 * @see https://w3c.github.io/clipboard-apis/#mandatory-data-types
 *
 * @author fudesign2008
 * @date  2016-12-29
 */
define(function (require) {
    var _ = require('underscore'),
        Class = require('jtk/core/Class'),
        L = require('jtk/core/L'),
        USER_AGENT = require('../util/userAgent'),
        ONLY_SUPPORT_STANDARD_TYPES = USER_AGENT.isEdge(),
        REGISTERED_TYPES = {
            /**
             * @type {Boolean} is standard type or not
             * @see https://w3c.github.io/clipboard-apis/#mandatory-data-types
             */
            'text/html': true,
            'text/plain': true,
            'text/uri-list': true,
            'text/yne-image-json': false,
            'text/yne-json': false,
            'text/yne-note-id': false,
            'text/yne-table-json': false,
        },
        ALT_STANDARD_TYPE = 'application/json',
        DataTransfer;

    DataTransfer = Class.extend({

        /**
         * @param {Event} options.event
         */
        initialize: function (options) {
            var that = this,
                event = options.event,
                rawEvent = event.originalEvent || event;

            that._dataTransfer = rawEvent.dataTransfer;

        },

        _getCustomData: function (type) {
            var that = this,
                dataStr,
                dataObj;

            if (!that._altData) {
                dataStr = that._dataTransfer.getData(ALT_STANDARD_TYPE);
                try {
                    dataObj = JSON.parse(dataStr);
                    that._altData = dataObj;
                } catch (ex) {
                    L.error(ex);
                    that._altData = {};
                }
            }

            return that._altData[type];
        },

        /**
         * @param {String} type
         * @return {Any}
         */
        getData: function (type) {
            var that = this,
                dataTransfer = that._dataTransfer,
                isStardard,
                value;

            if (ONLY_SUPPORT_STANDARD_TYPES) {
                isStardard = REGISTERED_TYPES[type];
                if (isStardard === true) {
                    value = dataTransfer.getData(type);
                    L.log('get standard type', type, value);
                } else if (isStardard === false) {
                    value = that._getCustomData(type);
                    L.log('get custom type', type, value);
                } else {
                    L.error('type should be registered!', type);
                }
                return value;
            } else {
                return dataTransfer.getData(type);
            }
        },

        /**
         * @param {Object} data
         */
        setDataMap: function (dataMap) {
            var dataTransfer = this._dataTransfer,
                setData = function (value, type) {
                    L.log('set data to data transfer', type, value);
                    try {
                        dataTransfer.setData(type, value);
                    } catch (ex) {
                        L.error(ex);
                    }
                },
                customData = {},
                customCounter = 0,
                str;

            if (ONLY_SUPPORT_STANDARD_TYPES) {
                _.each(dataMap, function (value, type) {
                    var isStardard = REGISTERED_TYPES[type];

                    if (isStardard === true) {
                        setData(value, type);
                    } else if (isStardard === false){
                        L.log('set custom type', type);
                        customData[type] = value;
                        customCounter++;
                    } else {
                        L.error('type should be registered!', type);
                    }
                });
                if (customCounter > 0) {
                    try {
                        str = JSON.stringify(customData);
                        setData(str, ALT_STANDARD_TYPE);
                    } catch (ex) {
                        L.error(ex);
                    }
                }
            } else {
                _.each(dataMap, setData);
            }
        }

    });

    return DataTransfer;
});


ClipboardData.js


/**
 *
 * @see https://www.lucidchart.com/techblog/2014/12/02/definitive-guide-copying-pasting-javascript
 * @see https://w3c.github.io/clipboard-apis/#mandatory-data-types
 *
 * @author fudesign2008
 * @date  2016-12-29
 */
define(function (require) {

    var DataTransfer = require('./DataTransfer'),
        ClipboardData = DataTransfer.extend({
            /**
             * @param {Event} options.event
             * @override
             */
            initialize: function (options) {
                var that = this,
                    event = options.event,
                    rawEvent = event.originalEvent || event;

                that._dataTransfer = rawEvent.clipboardData;
            }
        });

    return ClipboardData;
});

无论在 Webkit 内核浏览器和 Windows Edge 浏览器都能如此使用:


var ClipboardData = require('./ClipboardData');

$(el).on('copy', function (event) {
    var clipboardData = new ClipboardData({
            event: event
        });

    clipboardData.setDataMap({
        'text/plain': 'plain xxxx',
        'text/html': 'html xxxx',
        'yne-json': 'xxxx',
        'yne-table-json': 'yyyy'
    });
}).on('paste', function (event) {
    var clipboardData = new ClipboardData({
            event: event
        }),
        html = clipboardData.get('text/html'),
        yneTableJSON = clipboardData.get('yne-table-json');

    console.log('clipboard data text/html', html);
    console.log('clipboard data yne-table-json', yneTableJSON);

});

以上方案能够解决以下场景

  • 同一个端不同笔记间(同一个编辑器, 不同实例)相互拷贝粘贴

无法解决以下场景

  • 不同端的编辑器(不同编辑器, 不同实例)相互拷贝粘贴, 如从PC端中的编辑器拷贝, 粘贴到Web浏览器的编辑器中

参考

版权声明:本文为博主原创文章,未经博主允许不得转载。

相关文章推荐

工作学习笔记——C#自定义类型剪贴板操作、自定义鼠标光标定位问题

C#剪贴板操作,序列化;自定义光标定位准确度问题
  • wtyqm
  • wtyqm
  • 2011-02-16 00:28
  • 1622

使用剪贴板保存自定义对象

技术要点: 放置到剪贴板上的对象必须是可序列化的。 剪贴板接收一个实现了IDataObject接口的对象,可以用此对象“包装”具体的数据对象。 可以多次调用IDataObject. SetD...

使用ZeroClipboard解决跨浏览器复制到剪贴板的问题

Zero Clipboard的实现原理 Zero Clipboard 利用透明的Flash让其漂浮在复制按钮之上,这样其实点击的不是按钮而是 Flash ,这样将需要的内容传入Flash,再通过Fl...

关于使用ZeroClipboard解决跨浏览器复制到剪贴板的问题

最近在做点击按钮把文本框的内容复制到粘贴板的功能,遇到各种浏览器兼容问题,真是要抓狂了,几经周折终于是成功解决了目前比较主流的浏览器(IE、Firefox、Chrome、360)兼容问题。
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:深度学习:神经网络中的前向传播和反向传播算法推导
举报原因:
原因补充:

(最多只允许输入30个字)