Javascript - Скопировать строку в буфер обмена в виде текста / HTML

Есть ли способ в javascript, чтобы скопировать строку html (т.е. <b>xx<b>) в буфер обмена в виде text / html, чтобы затем его можно было вставить, например, в сообщение gmail с форматированием (т. е. xx выделено жирным шрифтом)

Существуют решения для копирования в буфер обмена в виде текста (text / plain), например, /questions/14172016/kak-mne-skopirovat-v-bufer-obmena-v-javascript/14172024#14172024 но не в виде text / html.

Мне нужно решение без flash, без jquery, которое будет работать по крайней мере на IE11 FF42 и Chrome.

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

6 ответов

Решение

Я сделал несколько изменений в ответе Лойло выше:

  • установка (и последующее восстановление) фокуса на скрытый div предотвращает бесконечную рекурсию FF при копировании из текстовой области

  • установка диапазона для внутренних дочерних элементов div предотвращает добавление хрома <br> в начале

  • removeAllRanges в getSelection() предотвращает добавление к существующему выбору (возможно, не требуется)

  • попробуйте / поймать вокруг execCommand

  • лучше скрывать копию div

На OSX это не будет работать. Safari не поддерживает execCommand, а в chrome OSX есть известная ошибка https://bugs.chromium.org/p/chromium/issues/detail?id=552975

код:

clipboardDiv = document.createElement('div');
clipboardDiv.style.fontSize = '12pt'; // Prevent zooming on iOS
// Reset box model
clipboardDiv.style.border = '0';
clipboardDiv.style.padding = '0';
clipboardDiv.style.margin = '0';
// Move element out of screen 
clipboardDiv.style.position = 'fixed';
clipboardDiv.style['right'] = '-9999px';
clipboardDiv.style.top = (window.pageYOffset || document.documentElement.scrollTop) + 'px';
// more hiding
clipboardDiv.setAttribute('readonly', '');
clipboardDiv.style.opacity = 0;
clipboardDiv.style.pointerEvents = 'none';
clipboardDiv.style.zIndex = -1;
clipboardDiv.setAttribute('tabindex', '0'); // so it can be focused
clipboardDiv.innerHTML = '';
document.body.appendChild(clipboardDiv);

function copyHtmlToClipboard(html) {
  clipboardDiv.innerHTML=html;

  var focused=document.activeElement;
  clipboardDiv.focus();

  window.getSelection().removeAllRanges();  
  var range = document.createRange(); 
  range.setStartBefore(clipboardDiv.firstChild);
  range.setEndAfter(clipboardDiv.lastChild);
  window.getSelection().addRange(range);  

  var ok=false;
  try {
     if (document.execCommand('copy')) ok=true; else utils.log('execCommand returned false !');
  } catch (err) {
     utils.log('execCommand failed ! exception '+err);
  }

  focused.focus();
}

см. jsfiddle, где вы можете ввести html-сегмент в текстовую область и скопировать в буфер обмена с помощью ctrl + c.

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


Сложенный вопрос:

Могу ли я использовать JavaScript, чтобы скопировать форматированный вывод некоторого HTML-кода в буфер обмена пользователя?


Ответ:

Да, с некоторыми ограничениями, вы можете.


Решение:

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

Объяснение того, как это работает, можно найти ниже, вы можете в интерактивном режиме протестировать функцию в этом jsFiddle.

// This function expects an HTML string and copies it as rich text.

function copyFormatted (html) {
  // Create container for the HTML
  // [1]
  var container = document.createElement('div')
  container.innerHTML = html

  // Hide element
  // [2]
  container.style.position = 'fixed'
  container.style.pointerEvents = 'none'
  container.style.opacity = 0

  // Detect all style sheets of the page
  var activeSheets = Array.prototype.slice.call(document.styleSheets)
    .filter(function (sheet) {
      return !sheet.disabled
    })

  // Mount the container to the DOM to make `contentWindow` available
  // [3]
  document.body.appendChild(container)

  // Copy to clipboard
  // [4]
  window.getSelection().removeAllRanges()

  var range = document.createRange()
  range.selectNode(container)
  window.getSelection().addRange(range)

  // [5.1]
  document.execCommand('copy')

  // [5.2]
  for (var i = 0; i < activeSheets.length; i++) activeSheets[i].disabled = true

  // [5.3]
  document.execCommand('copy')

  // [5.4]
  for (var i = 0; i < activeSheets.length; i++) activeSheets[i].disabled = false

  // Remove the container
  // [6]
  document.body.removeChild(container)
}

Объяснение:

Посмотрите на комментарии в коде выше, чтобы увидеть, где вы находитесь в следующем процессе:

  1. Мы создаем контейнер для размещения нашего HTML-кода.
  2. Мы делаем стиль контейнера скрытым и определяем активные таблицы стилей страницы. Причина будет объяснена в ближайшее время.
  3. Мы помещаем контейнер в DOM страницы.
  4. Мы удаляем, возможно, существующие выборы и выбираем содержимое нашего контейнера.
  5. Мы делаем само копирование. На самом деле это многошаговый процесс: Chrome будет копировать текст так, как он его видит, с применением стилей CSS, в то время как другие браузеры будут копировать его со стилями браузера по умолчанию. Поэтому мы отключим все пользовательские стили перед копированием, чтобы получить максимально согласованный результат.

    1. Прежде чем мы сделаем это, мы преждевременно выполняем copy команда. Это взлом для IE11: в этом браузере копирование должно быть подтверждено вручную один раз. Пока пользователь не нажмет кнопку "Подтвердить", пользователи IE будут видеть страницу без каких-либо стилей. Чтобы избежать этого, мы сначала копируем, ждем подтверждения, затем отключаем стили и снова копируем. На этот раз мы не получим диалоговое окно подтверждения, поскольку IE запоминает наш последний выбор.
    2. Мы фактически отключаем стили страницы.
    3. Теперь мы снова выполним copy команда.
    4. Мы снова включаем таблицы стилей.
  6. Мы удаляем контейнер из DOM страницы.

И мы сделали.


Предостережения:

  • Отформатированный контент не будет идеально согласован в разных браузерах.

    Как объяснено выше, Chrome (т. Е. Механизм Blink) будет использовать другую стратегию, нежели Firefox и IE: Chrome будет копировать содержимое с использованием своих стилей CSS, но исключая любые стили, которые не определены.

    Firefox и IE, с другой стороны, не будут применять CSS для конкретной страницы, они будут применять стили браузера по умолчанию. Это также означает, что к ним будут применены некоторые странные стили, например, шрифт по умолчанию (обычно это Times New Roman).

  • Из соображений безопасности браузеры разрешают выполнение функции только в результате взаимодействия с пользователем (например, щелчок, нажатие клавиши и т. Д.)

Есть гораздо более простое решение. Скопируйте раздел своей страницы (элемент), чем скопируйте HTML.

С помощью этой простой функции вы можете скопировать все, что хотите (текст, изображения, таблицы и т. Д.) На свою страницу или весь документ в буфер обмена. Функция получает идентификатор элемента или сам элемент.

function copyElementToClipboard(element) {
  window.getSelection().removeAllRanges();
  let range = document.createRange();
  range.selectNode(typeof element === 'string' ? document.getElementById(element) : element);
  window.getSelection().addRange(range);
  document.execCommand('copy');
  window.getSelection().removeAllRanges();
}

Как пользоваться:

copyElementToClipboard(document.body);
copyElementToClipboard('myImageId');

Для тех, кто ищет способ сделать это с помощью ClipboardItem и не может заставить его работать, даже если для dom.events.asyncClipboard.clipboardItem установлено значение true, ответ можно найти на сайте nikouusitalo.com.

А вот мой рабочий код (проверено на Firefox 102).

          const clipboardItem = new
        ClipboardItem({'text/html':  new Blob([html],
                                              {type: 'text/html'}),
                       'text/plain': new Blob([html],
                                              {type: 'text/plain'})});
    navigator.clipboard.write([clipboardItem]).
            then(_ => console.log("clipboard.write() Ok"),
                 error => alert(error));

Убедитесь, что вы пытаетесь вставить его в редактор форматированного текста, такой как gmail, а не в редактор простого текста/уценки, такой как stackoverflow.

Если вы хотите использовать новый API буфера обмена , используйтеwriteметод, как показано ниже:

      var type = "text/html";
var blob = new Blob([text], { type });
var data = [new ClipboardItem({ [type]: blob })];

navigator.clipboard.write(data).then(
    function () {
    /* success */
    },
    function () {
    /* failure */
    }
);

В настоящее время (сентябрь 2021 г.) проблема в том, что Firefox не поддерживает этот метод .

Прошло почти два года, и в настоящее время существует более простой способ добиться этого с помощью clipboard.js.

Для этого требуется современный браузер (Chrome 42+, Firefox 41+, Opera 29+, Internet Explorer 9+, Safari 10+), но он скрывает сложность вставки копий форматированного текста (намного меньше кода для записи):

1) Ссылка clipboard.js

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

clipboard.copy({
  "text/plain": "Normal text comes here",
  "text/html": "<b>Normal text</b> comes <div style="color: blue;">here</div>"
})
.then(
  function() {
     // success code here
  },
  function(err) {
     // failure code here
  }
);
Другие вопросы по тегам