Javascript - Копировать строку в буфер обмена как text/html

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

Существуют решения для копирования в буфер обмена как текст (текст/обычный), например qaru.site/info/314/..., но не как text/html

Мне нужно решение non flash, не jquery, которое будет работать хотя бы на IE11 FF42 и Chrome.

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

Ответ 1

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

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

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

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

  • попробуйте/поймайте execCommand

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

В OSX это не сработает. Safari не поддерживает execCommand, а хром 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.

Ответ 2

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


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

Могу ли я использовать 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).

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

Ответ 3

Прошло почти два года, и в настоящее время существует более простой способ достичь этого, используя 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
  }
);