Статьи

Полное руководство по копированию и вставке в JavaScript

В Lucid Software мы стремимся создавать качественное программное обеспечение для настольных компьютеров, которое работает в браузере. Однако до недавнего времени мы предпочитали использовать собственную реализацию внутреннего буфера обмена, а не системный буфер обмена, ограничивая копирование и вставку из внешних приложений простым текстом. Клиенты требуют возможности вставлять изображения, снятые со снимков экрана или скопированные из других окон браузера, используя исключительно системный буфер обмена, открывая тем самым двери для добавления богатого контента.

По пути мы столкнулись с тремя основными проблемами: ограниченный доступ к буферу обмена, несовместимые события в браузерах и

Internet Explorer
ограниченная поддержка нескольких типов данных. В этом посте будет рассказано о том, что мы узнали, и как вы можете использовать наши идеи. Продолжайте читать, чтобы увидеть фрагменты кода и подробные ответы на эти проблемы. Если у вас есть вопросы, просто дайте нам знать. Мы будем рады добавить больше деталей.

Шаг № 1 — Доступ к событиям буфера обмена в любом браузере

Запрос доступа к буферу обмена в Internet Explorer

Существует несколько проблем безопасности, связанных с доступом веб-страницы к системному буферу обмена. Из-за этого браузеры ограничивают доступ к буферу обмена. В общем, вы можете получить доступ к буферу обмена только во время системного вырезания, копирования или вставки. Они запускаются, когда пользователь нажимает сочетания клавиш или использует меню браузера. Это ограничение по двум причинам:

Браузеры (за исключением Chrome) запускают события буфера обмена только при наличии правильного выделения и фокусируются на элементах HTML. Чтобы убедиться в этом, используйте Chrome, чтобы поиграть со следующим кодом.

['cut', 'copy', 'paste'].forEach(function(event) {
    document.addEventListener(event, function(e) {
        console.log(event);   
    });
}); 

  1. Теперь попробуйте это в другом браузере. Ничего такого. События не стреляют. Теперь попробуйте добавить поле ввода текста . Если ваш текстовый курсор находится в поле ввода, и у вас есть некоторый текст в буфере обмена, он вставляется просто отлично. Чтобы получить копию или вырезать, вам нужно выделить какой-то текст в области ввода. В Lucidchart мы не используем элементы HTML для обработки или визуализации нашего текста или фигур. Чтобы получить доступ к буферу обмена, нам нужно событие буфера обмена. Чтобы получить событие буфера обмена, нам нужны сфокусированные элементы HTML, которые ожидает браузер. Но мы не используем эти элементы HTML.
  2. Мы хотели бы поддерживать копирование и вставку из нашего контекстного меню, когда пользователь щелкает правой кнопкой мыши, но поскольку мы используем собственное контекстное меню, а не браузер, событие системного буфера обмена не запускается. А это значит, что мы не получаем доступ к системному буферу обмена.

Последовательное получение событий буфера обмена

Наше решение выглядит примерно следующий код :

var hiddenInput = $('#hidden-input');

var focusHiddenArea = function() {
    hiddenInput.val(' ');
hiddenInput.focus().select();
};

$(document).mouseup(focusHiddenArea);

['cut', 'copy', 'paste'].forEach(function(event) {
    document.addEventListener(event, function(e) {
        console.log(event);
        focusHiddenArea();
        e.preventDefault();
    });
});

У нас есть скрытая текстовая область, в которой всегда есть выделенный текст. Таким образом, события вырезания, копирования и вставки всегда запускаются в любом браузере. Однако вы заметите, что эта Fiddle ничего не делает, когда пользователь печатает, а после того, как пользователь печатает, события буфера обмена не будут срабатывать, пока область скрытого текста не будет перефокусирована нажатием. В случае такого приложения, как Lucidchart, это недостаточное решение. Мы не можем просто выбросить ввод с клавиатуры пользователя.

Я не буду вдаваться во все детали передачи нашего ввода, потому что оказывается, что реализация вашего собственного редактирования текста является довольно подробной проблемой. Но просто чтобы дать вам представление, эта скрипка способна успешно захватить ввод и оставаться готовой к вырезанию, копированию или вставке события.

Копирование и вставка контекстного меню

Пока что мы поддерживаем только копирование и вставку контекстного меню для Chrome и IE. Пользователи других браузеров ограничены сочетаниями клавиш. Chrome может предоставить нам доступ к буферу обмена через наше расширение Chrome , а Internet Explorer позволяет получить доступ к буферу обмена в любое время (через window.clipboardData), но сообщит пользователю, находится ли он вне системного события. Кроме этих двух случаев, мы просто не можем поддерживать копирование и вставку меню (хотя вы найдете множество людей, пытающихся обойти это , единственный реальный способ, которым я знаю, это использование Flash ). Наше единственное утешение в том, что Google тоже не может этого сделать. Попробуйте и попробуйте это с Google Docs. Работает только в Chrome и IE. Используйте его в Firefox или Safari, и вы заметите небольшой диалог, в котором вам предложат использовать сочетания клавиш.

Ограниченное уведомление о доступе к буферу обмена

Шаг № 2 — Получить данные в нескольких форматах в и из буфера обмена

Теперь у нас есть надежный способ получения событий буфера обмена. Теперь мы рассмотрим проблему перемещения данных в нее и из нее.

Давайте начнем с описания того, что мы хотим. Настольные приложения могут читать и записывать данные любого типа в буфер обмена. Если это может быть представлено как поток байтов, это может пойти в буфер обмена. Например, если я выберу несколько фигур и текстовых областей в Microsoft PowerPoint, а затем вставлю их в Microsoft Word, все фигуры и текст будут правильно вставлены с использованием некоторого представления объектов в виде форм и текста внутри MS Office. Более того, приложение может поместить несколько форм данных в буфер обмена. Таким образом, эти формы из PowerPoint также могут быть вставлены в Photoshop или Gimp в качестве изображения. Когда вы копируете в PowerPoint, что-то подобное может произойти.

clipboard.setData('text/plain', selection.getText());
clipboard.setData('application/officeObj’, selection.serialize());
clipboard.setData('image/bmp', draw(selection));
clipboard.setData('text/html', ...);
...

PowerPoint попытается скопировать все возможные полезные типы данных в буфер обмена, чтобы другие приложения могли использовать любой тип данных, который они распознают.

В Интернете мы не обладаем такой же гибкостью. В то время как стандарты описывают минимальную поддержку большого количества типов данных , мы, как правило, рады, если можем просто постоянно получать доступ к простому тексту в буфере обмена. Прямо сейчас, вот что на самом деле поддерживают браузеры:

  • Chrome и Safari : они поддерживают любой тип содержимого clipboardData, включая пользовательские. Итак, мы можем позвонить clipboardData.setData('application/lucidObjects', serializedObjects)для вставки, а затем позвонитьvar serialized = clipboardData.getData('application/lucidObjects')
  • Firefox : в настоящее время он разрешает доступ только к тем типам данных, которые описаны выше . Вы можете установить пользовательские типы при копировании, но при вставке пропускаются только типы из белого списка.
  • Internet Explorer : в истинном смысле IE он поддерживает только два типа данных: Textи URL. О, и если вы установите один, вы не можете установить другой (он обнуляется). Однако существует хак, который также позволяет нам косвенно получать и устанавливать HTML.

Объект буфера обмена в Internet Explorer не открывается text/htmlчерез JavaScript. Однако он поддерживает копирование и вставку HTML в contenteditableэлементы. Мы можем использовать это, если позволим браузеру выполнить копирование и вставку по умолчанию, но «перехватить» события, чтобы получить / поместить нужные нам данные HTML. Это будет выглядеть примерно следующим кодом .

if (isIe) {
    document.addEventListener('beforepaste', function() {
        if (hiddenInput.is(':focus')) {
            focusIeClipboardDiv();
        }
    }, true);
}

var ieClipboardEvent = function(clipboardEvent) {
    var clipboardData = window.clipboardData;
    if (clipboardEvent == 'cut' || clipboardEvent == 'copy') {
        clipboardData.setData('Text', textToCopy);
        ieClipboardDiv.html(htmlToCopy);
        focusIeClipboardDiv();
        setTimeout(function() {
            focusHiddenArea();
            ieClipboardDiv.empty();
        }, 0);
    }
    if (clipboardEvent == 'paste') {
        var clipboardText = clipboardData.getData('Text');
        ieClipboardDiv.empty();
        setTimeout(function() {
            console.log('Clipboard Plain Text: ' + clipboardText);
            console.log('Clipboard HTML: ' + ieClipboardDiv.html());
            ieClipboardDiv.empty();
            focusHiddenArea();
        }, 0);
    }
};

Ниже приведена схема, иллюстрирующая весь процесс:

Диаграмма, иллюстрирующая функциональность буфера обмена в Lucidchart

Вот что происходит:

  • У нас есть скрытый contenteditablediv на странице только для копирования и вставки.
  • Во время copyсобытия (до того, как система выполнит действие по умолчанию), мы устанавливаем HTML-код div на то, что мы хотим поместить в буфер обмена, и программно выбираем весь контент.
  • Затем, когда система выполнит копирование, она получит нужный HTML и будет помещена в системный буфер обмена.
  • Чтобы вставить HTML, мы перемещаем фокус на contenteditableэлемент во время beforepasteсобытия.
  • Сразу после того, как система выполнила вставку по умолчанию, мы просто извлекаем и очищаем вставленный HTML.

Этот последний фрагмент дает рабочий пример того, как мы копируем и вставляем пользовательский текст и HTML в браузеры, используя только JavaScript. Этого на самом деле достаточно, чтобы поддерживать все, что делала наша старая реализация буфера обмена. Мы кодируем / декодируем наши фигуры в / из HTML, чтобы мы могли копировать и вставлять фигуры в редакторе, и мы получаем / устанавливаем простой текст в буфере обмена, чтобы мы могли копировать и вставлять текст в другие приложения и из них.

Шаг № 3 — Веселитесь

Очевидно, что мы не прошли через этот беспорядок исключительно для того, чтобы сохранить нашу прежнюю функциональность. Теперь, когда у нас есть постоянный доступ к системному буферу обмена, мы больше не ограничены тем, чтобы жить исключительно в нашем собственном приложении. Скорее, пользователи Lucidchart могут обмениваться и получать данные из других приложений с помощью системного буфера обмена. Например, было легко добавить функциональность для вставки изображений из скриншота рабочего стола или из другого окна браузера. И теперь мы также поддерживаем вставку форматированного текста из Gmail, Google Docs или других веб-страниц.


Копирование и вставка расширенного текста из Документов Google в Lucidchart

Мы бы очень хотели услышать, что вы думаете об этой функции. Попробуйте сами!
Если у вас есть какие-либо идеи по его улучшению — или вы просто хотите поболтать о копировании и вставке в Интернет — не стесняйтесь оставлять отзывы на наших форумах поддержки .

Полезные ресурсы