关闭

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

标签: javascript浏览器编辑器剪贴板clipboard
248人阅读 评论(0) 收藏 举报
分类:

引子

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

  • 跨浏览器获取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浏览器的编辑器中

参考

0
0

查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    个人资料
    • 访问:609292次
    • 积分:6999
    • 等级:
    • 排名:第3244名
    • 原创:119篇
    • 转载:140篇
    • 译文:0篇
    • 评论:36条
    文章分类
    最新评论