JavaScript. Работа с буфером, Ctrl+C Ctrl+V

от автора

Как копировать в буфер картинки. Какие типы данных можно класть в буфер. Поддержка кастомных типов. Как сделать свои кнопки копировать/вставить.

Рис 1. Копирование между вкладками редактора блок-схем и Google Docs

Рис 1. Копирование между вкладками редактора блок-схем и Google Docs

Задача

Сделать копирование для редактора блок-схем dgrm.net.

  • Копировать можно между вкладками.

  • Можно копировать в Word/Google docs, тогда схема вставляется как картинка.

  • Должны работать горячие клавиши Ctrl+C, Ctrl+V, Ctrl+X.

  • Свои кнопки копировать/вставить, чтобы можно было копировать мышкой.

  • Поддержка мобильных.

Варианты API работы с буфером

document.execCommand(‘copy’) не работает

Это API устарело и не работает. Не пытайтесь с его помощью обойти ограничения других API.

Работа с буфером в обработчиках событий ‘copy’, ‘paste’, ‘cut’

document.addEventListener('copy', evt => { evt.preventDefault(); evt.clipboardData.setData('text/plain', 'text to clipboard'); });

Листинг 1. Пишем в буфер на событии “вставить”

В этом варианте нет возможности сделать свою кнопку “копировать”, пользователь должен нажать Ctrl+C.

navigator.clipboard.read/wright

btn.addEventListener('click', async _ =>  await navigator.clipboard.writeText('text to clipboard') );

Листинг 2. Пишем в буфер по клику на кнопке

Можно сделать свою кнопку “копировать”. Работает во всех браузерах и на мобильных.
В Safari есть ограничение: нельзя использовать в callback-ах. Так в Safari не работает:

btn.addEventListener('click', async _ =>  // create png from svg svgToPng( svg, // callback async png => await navigator.clipboard.write(...)) );

Листинг 3. Не работает в Safari. Нельзя использовать navigator.clipboard в callback-ах. Как писать картинку в буфер в следующем разделе.

Это ограничение безопасности: когда придет callback не понятно, пользователь может уже и передумал копировать.

Можно сделать так: строку гарантированно пишем в буфер, если браузер разрешит запишем картинку.

// guaranteed clipboard write await navigator.clipboard.writeText('text to clipboard');  // try to write img // if ok -> clipboard will be overwritten try { // create png from svg svgToPng( svg, // callback async png => await navigator.clipboard.write(...)); } catch { }

Листинг 4. Запись текста в буфер гарантирована, если браузер разрешит запишем картинку

Как записать картинку в буфер

await navigator.clipboard.write([new window.ClipboardItem({ 'image/png': Promise.resolve(png), // png is Blob 'text/plain': Promise.resolve(new Blob(['text to clipboard'], { type: 'text/plain' })) })]);

Листинг 5. Запись картинки и альтернативного текста в буфер.

В буфер можно записать одновременно несколько типов данных. Например, картинку и текст. Если вставить в Word — вставится картинка, если в блокнот — текст.

Можно записывать такие типы:

  • text/plain

  • text/html

  • image/png

Некоторые браузеры поддерживают больше типов. Эти три работают везде.

Копировать в буфер можно только png. Другие форматы картинок не поддерживаются.
При копировании браузер “очищает” png. Для безопасности браузер удаляет из png метаданные.

Неприятность для dgrm.net. Dgrm.net хранит в метаданных описание схемы. Благодаря метаданным можно открыть картинку на редактирование — рис 2.

Рис 2. dgrm.net умеет открывать схемы из PNG картинок

Рис 2. dgrm.net умеет открывать схемы из PNG картинок

Кастомные типы данных в буфере

‘text/plain’ из буфера можно вставить куда угодно. Хорошо использовать свой тип данных, который читает только ваше приложение. Например ‘text/dgrm’.

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

Не все браузеры поддерживают самодельные типы. В Chrome можно использовать кастомные типы, они обязательно должны иметь префикс ‘web ’ -> ‘web text/dgrm’.

Не все браузеры позволяют записать не ‘text/plain’ в буфер с помощью navigator.clipboard.write. Разрешают только в обработчиках событий ‘copy’, ‘cut’. Т.е. свою кнопку “копировать” нельзя сделать.

В итоге остановился на navigator.clipboard.writeText который везде работает.

Чтение из буфера

Чтение буфера еще опаснее чем запись. Еще больше ограничений. В FireFox читать можно только в обработчике события ‘paste’. Свою кнопку “вставить” сделать нельзя, нужно чтобы пользователь нажал Ctrl V.

document.addEventListener('paste', evt => { const txt = evt.clipboardData.getData('text/plain'); });

Листинг 6. Чтение из буфера на событии ‘paste’

В других браузерах можно использовать navigator.clipboard API.

btn.addEventListener('click', async _ =>  await navigator.clipboard.readText() );

Листинг 7. Чтение из буфера по кнопке

Браузер запросит разрешение пользователя. iOS Safari будет спрашивать при каждом вызове navigator.clipboard.readText.

О редакторе dgrm.net

Dgrm.net — быстрый редактор без лишних кнопок.

Развиваю редактор. Анонсы обновлений начал писать в телеграм.

Попробуйте, напишите что нравится/не нравится. Все читаю, веду список предложений.


ссылка на оригинал статьи https://habr.com/ru/articles/724904/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *