Как мне скопировать в буфер обмена в JavaScript? - PullRequest
2958 голосов
/ 30 декабря 2008

Как лучше всего скопировать текст в буфер обмена? (Мульти-браузер)

Я пробовал:

function copyToClipboard(text) {
    if (window.clipboardData) { // Internet Explorer
        window.clipboardData.setData("Text", text);
    } else {  
        unsafeWindow.netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");  
        const clipboardHelper = Components.classes["@mozilla.org/widget/clipboardhelper;1"].getService(Components.interfaces.nsIClipboardHelper);  
        clipboardHelper.copyString(text);
    }
}

но в Internet Explorer выдает синтаксическую ошибку. В Firefox написано unsafeWindow is not defined.

Хороший трюк без вспышки: Как Trello получает доступ к буферу обмена пользователя?

Ответы [ 51 ]

1869 голосов
/ 12 июня 2015

Обзор

Существует три основных API браузера для копирования в буфер обмена:

  1. API асинхронного буфера обмена [navigator.clipboard.writeText]
    • Текстовая часть доступна в Chrome 66 (март 2018)
    • Доступ асинхронный и использует Обещания JavaScript , могут быть написаны так, чтобы запросы безопасности (если отображаются) не прерывали JavaScript на странице.
    • Текст можно скопировать в буфер обмена непосредственно из переменной.
    • Поддерживается только на страницах, обслуживаемых по HTTPS.
    • В Chrome 66 страницы в активных вкладках могут записываться в буфер обмена без запроса прав доступа.
  2. document.execCommand('copy')
    • Большинство браузеров поддерживают это по состоянию на апрель 2015 года (см. «Поддержка браузеров» ниже).
    • Доступ является синхронным, т.е. останавливает JavaScript на странице до завершения, включая отображение и взаимодействие пользователя с любыми запросами безопасности.
    • Текст читается из DOM и помещается в буфер обмена.
    • Во время тестирования ~ апрель 2015 г. только Internet Explorer был отмечен как отображающий запросы разрешений при записи в буфер обмена.
  3. Переопределение события копирования
    • См. Документацию API буфера обмена по Переопределение события копирования .
    • Позволяет изменить то, что появляется в буфере обмена, из любого события копирования, может включать в себя другие форматы данных, кроме обычного текста.
    • Здесь не рассматривается, так как это не дает прямого ответа на вопрос.

Общие замечания по разработке

Не ожидайте, что команды, связанные с буфером обмена, будут работать, пока вы тестируете код в консоли. Обычно страница должна быть активной (Async Clipboard API) или требует взаимодействия с пользователем (например, щелчок пользователя), чтобы разрешить (document.execCommand('copy')) доступ к буферу обмена, подробнее см. Ниже.

Async + Fallback

Из-за уровня поддержки браузером нового Async Clipboard API вы, вероятно, захотите вернуться к методу document.execCommand('copy'), чтобы получить хорошее покрытие браузера.

Вот простой пример:

function fallbackCopyTextToClipboard(text) {
  var textArea = document.createElement("textarea");
  textArea.value = text;
  document.body.appendChild(textArea);
  textArea.focus();
  textArea.select();

  try {
    var successful = document.execCommand('copy');
    var msg = successful ? 'successful' : 'unsuccessful';
    console.log('Fallback: Copying text command was ' + msg);
  } catch (err) {
    console.error('Fallback: Oops, unable to copy', err);
  }

  document.body.removeChild(textArea);
}
function copyTextToClipboard(text) {
  if (!navigator.clipboard) {
    fallbackCopyTextToClipboard(text);
    return;
  }
  navigator.clipboard.writeText(text).then(function() {
    console.log('Async: Copying to clipboard was successful!');
  }, function(err) {
    console.error('Async: Could not copy text: ', err);
  });
}

var copyBobBtn = document.querySelector('.js-copy-bob-btn'),
  copyJaneBtn = document.querySelector('.js-copy-jane-btn');

copyBobBtn.addEventListener('click', function(event) {
  copyTextToClipboard('Bob');
});


copyJaneBtn.addEventListener('click', function(event) {
  copyTextToClipboard('Jane');
});
<div style="display:inline-block; vertical-align:top;">
  <button class="js-copy-bob-btn">Set clipboard to BOB</button><br /><br />
  <button class="js-copy-jane-btn">Set clipboard to JANE</button>
</div>
<div style="display:inline-block;">
  <textarea class="js-test-textarea" cols="35" rows="4">Try pasting into here to see what you have on your clipboard:

  </textarea>
</div>

Обратите внимание, что этот фрагмент не работает должным образом во встроенном предварительном просмотре Stack Overflow, вы можете попробовать его здесь: https://codepen.io/DeanMarkTaylor/pen/RMRaJX?editors=1011

Async Clipboard API

Обратите внимание, что в Chrome 66 имеется возможность «запросить разрешение» и проверить доступ к буферу обмена через API разрешений.

var text = "Example text to appear on clipboard";
navigator.clipboard.writeText(text).then(function() {
  console.log('Async: Copying to clipboard was successful!');
}, function(err) {
  console.error('Async: Could not copy text: ', err);
});

document.execCommand ( 'копия')

Остальная часть этого поста посвящена нюансам и деталям document.execCommand('copy') API.

Поддержка браузера

Поддержка JavaScript document.execCommand('copy') увеличилась, см. Ссылки ниже для обновлений браузера:

Простой пример

var copyTextareaBtn = document.querySelector('.js-textareacopybtn');

copyTextareaBtn.addEventListener('click', function(event) {
  var copyTextarea = document.querySelector('.js-copytextarea');
  copyTextarea.focus();
  copyTextarea.select();

  try {
    var successful = document.execCommand('copy');
    var msg = successful ? 'successful' : 'unsuccessful';
    console.log('Copying text command was ' + msg);
  } catch (err) {
    console.log('Oops, unable to copy');
  }
});
<p>
  <button class="js-textareacopybtn" style="vertical-align:top;">Copy Textarea</button>
  <textarea class="js-copytextarea">Hello I'm some text</textarea>
</p>

Сложный пример: копирование в буфер обмена без отображения ввода

Приведенный выше простой пример отлично работает, если на экране виден элемент textarea или input.

В некоторых случаях вы можете скопировать текст в буфер обмена без отображения элемента input / textarea. Это один из примеров способа обойти это (в основном вставка элемента, копирование в буфер обмена, удаление элемента):

Протестировано с Google Chrome 44, Firefox 42.0a1 и Internet Explorer 11.0.8600.17814.

function copyTextToClipboard(text) {
  var textArea = document.createElement("textarea");

  //
  // *** This styling is an extra step which is likely not required. ***
  //
  // Why is it here? To ensure:
  // 1. the element is able to have focus and selection.
  // 2. if element was to flash render it has minimal visual impact.
  // 3. less flakyness with selection and copying which **might** occur if
  //    the textarea element is not visible.
  //
  // The likelihood is the element won't even render, not even a
  // flash, so some of these are just precautions. However in
  // Internet Explorer the element is visible whilst the popup
  // box asking the user for permission for the web page to
  // copy to the clipboard.
  //

  // Place in top-left corner of screen regardless of scroll position.
  textArea.style.position = 'fixed';
  textArea.style.top = 0;
  textArea.style.left = 0;

  // Ensure it has a small width and height. Setting to 1px / 1em
  // doesn't work as this gives a negative w/h on some browsers.
  textArea.style.width = '2em';
  textArea.style.height = '2em';

  // We don't need padding, reducing the size if it does flash render.
  textArea.style.padding = 0;

  // Clean up any borders.
  textArea.style.border = 'none';
  textArea.style.outline = 'none';
  textArea.style.boxShadow = 'none';

  // Avoid flash of white box if rendered for any reason.
  textArea.style.background = 'transparent';


  textArea.value = text;

  document.body.appendChild(textArea);
  textArea.focus();
  textArea.select();

  try {
    var successful = document.execCommand('copy');
    var msg = successful ? 'successful' : 'unsuccessful';
    console.log('Copying text command was ' + msg);
  } catch (err) {
    console.log('Oops, unable to copy');
  }

  document.body.removeChild(textArea);
}


var copyBobBtn = document.querySelector('.js-copy-bob-btn'),
  copyJaneBtn = document.querySelector('.js-copy-jane-btn');

copyBobBtn.addEventListener('click', function(event) {
  copyTextToClipboard('Bob');
});


copyJaneBtn.addEventListener('click', function(event) {
  copyTextToClipboard('Jane');
});
<div style="display:inline-block; vertical-align:top;">
  <button class="js-copy-bob-btn">Set clipboard to BOB</button><br /><br />
  <button class="js-copy-jane-btn">Set clipboard to JANE</button>
</div>
<div style="display:inline-block;">
  <textarea class="js-test-textarea" cols="35" rows="4">Try pasting into here to see what you have on your clipboard:

  </textarea>
</div>

Дополнительные примечания

Работает только в том случае, если пользователь выполняет действие

Все document.execCommand('copy') вызовы должны выполняться как прямой результат действий пользователя, например, щелкните обработчик события. Это мера, позволяющая избежать путаницы с буфером обмена пользователя, когда он этого не ожидает.

См. сообщение для разработчиков Google здесь для получения дополнительной информации.

API буфера обмена

Обратите внимание, что полный проект спецификации API буфера обмена можно найти здесь: https://w3c.github.io/clipboard-apis/

Это поддерживается?

  • document.queryCommandSupported('copy') должен вернуть true, если команда "поддерживается браузером".
  • и document.queryCommandEnabled('copy') возвращают true, если document.execCommand('copy') будет успешным, если вызывается сейчас. Проверка того, что команда была вызвана из потока, инициированного пользователем, и соблюдены другие требования.

Однако в качестве примера проблем совместимости браузера Google Chrome с ~ апреля по ~ октябрь 2015 года возвратил true из document.queryCommandSupported('copy'), только если команда была вызвана из потока, инициированного пользователем.

Обратите внимание на подробности совместимости ниже.

Сведения о совместимости браузера

Хотя простой вызов document.execCommand('copy'), заключенный в блок try / catch, вызываемый в результате щелчка пользователя, обеспечит вам наибольшую совместимость, использование следующих условий имеет некоторые условия:

Любой вызов document.execCommand, document.queryCommandSupported или document.queryCommandEnabled должен быть заключен в блок try / catch.

Различные реализации браузера и версии браузера выдают различные типы исключений при вызове вместо возврата false.

Различные реализации браузера все еще находятся в движении, а API буфера обмена все еще находится в стадии разработки, поэтому не забудьте провести тестирование.

1229 голосов
/ 19 мая 2011

Автоматическое копирование в буфер обмена может быть опасным, поэтому большинство браузеров (кроме IE) делают это очень сложно. Лично я использую следующий простой трюк:

function copyToClipboard(text) {
  window.prompt("Copy to clipboard: Ctrl+C, Enter", text);
}

Пользователю предоставляется окно приглашения, в котором текст для копирования уже выделен. Теперь достаточно нажать Ctrl + C и Enter (чтобы закрыть окно) - и вуаля!

Теперь операция копирования в буфер обмена БЕЗОПАСНА, потому что пользователь делает это вручную (но довольно простым способом). Конечно, работает во всех браузерах.

<button id="demo" onclick="copyToClipboard(document.getElementById('demo').innerHTML)">This is what I want to copy</button>

<script>
  function copyToClipboard(text) {
    window.prompt("Copy to clipboard: Ctrl+C, Enter", text);
  }
</script>
230 голосов
/ 26 ноября 2015

Следующий подход работает в Chrome, Firefox, Internet Explorer и Edge, а также в последних версиях Safari (поддержка копирования была добавлена ​​в версии 10, выпущенной в октябре 2016 года).

  • Создайте текстовую область и установите для ее содержимого текст, который вы хотите скопировать в буфер обмена.
  • Добавьте текстовое поле к DOM.
  • Выберите текст в текстовой области.
  • Вызовите document.execCommand ("copy")
  • Удалите текстовое поле из домена.

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

Некоторые вещи, на которые стоит обратить внимание, если вы реализуете это самостоятельно:

  • По соображениям безопасности, его можно вызывать только из обработчика событий, такого как щелчок (как и при открытии окон).
  • IE покажет диалоговое окно с разрешениями при первом обновлении буфера обмена.
  • IE и Edge будут прокручиваться, когда текстовая область сфокусирована.
  • execCommand () может выдавать в некоторых случаях.
  • Новые строки и вкладки могут быть проглочены, если вы не используете текстовую область. (В большинстве статей рекомендуется использовать div)
  • Текстовая область будет видна, пока отображается диалоговое окно IE, вам нужно либо скрыть ее, либо использовать специфический для IE clipboardData api.
  • В IE системные администраторы могут отключить API буфера обмена.

Приведенная ниже функция должна обрабатывать все следующие проблемы как можно более чисто. Пожалуйста, оставьте комментарий, если вы обнаружите какие-либо проблемы или у вас есть предложения по его улучшению.

// Copies a string to the clipboard. Must be called from within an 
// event handler such as click. May return false if it failed, but
// this is not always possible. Browser support for Chrome 43+, 
// Firefox 42+, Safari 10+, Edge and IE 10+.
// IE: The clipboard feature may be disabled by an administrator. By
// default a prompt is shown the first time the clipboard is 
// used (per session).
function copyToClipboard(text) {
    if (window.clipboardData && window.clipboardData.setData) {
        // IE specific code path to prevent textarea being shown while dialog is visible.
        return clipboardData.setData("Text", text); 

    } else if (document.queryCommandSupported && document.queryCommandSupported("copy")) {
        var textarea = document.createElement("textarea");
        textarea.textContent = text;
        textarea.style.position = "fixed";  // Prevent scrolling to bottom of page in MS Edge.
        document.body.appendChild(textarea);
        textarea.select();
        try {
            return document.execCommand("copy");  // Security exception may be thrown by some browsers.
        } catch (ex) {
            console.warn("Copy to clipboard failed.", ex);
            return false;
        } finally {
            document.body.removeChild(textarea);
        }
    }
}

https://jsfiddle.net/fx6a6n6x/

93 голосов
/ 17 октября 2010

Если вы хотите действительно простое решение (занимает меньше 5 минут для интеграции) и хорошо выглядят прямо из коробки, тогда Clippy - хорошая альтернатива некоторым из более сложных решений. *

Он был написан соучредителем GitHub. Пример встроенного кода Flash ниже:

<object
   classid="clsid:d27cdb6e-ae6d-11cf-96b8-444553540000"
   width="110"
   height="14"
   id="clippy">
  <param name="movie" value="/flash/clippy.swf"/>
  <param name="allowScriptAccess" value="always"/>
  <param name="quality" value="high"/>
  <param name="scale" value="noscale"/>
  <param NAME="FlashVars" value="text=#{text}"/>
  <param name="bgcolor" value="#{bgcolor}"/>
  <embed
     src="/flash/clippy.swf"
     width="110"
     height="14"
     name="clippy"
     quality="high"
     allowScriptAccess="always"
     type="application/x-shockwave-flash"
     pluginspage="http://www.macromedia.com/go/getflashplayer"
     FlashVars="text=#{text}"
     bgcolor="#{bgcolor}"/>
</object>

Не забудьте заменить #{text} текстом, который вам нужно скопировать, а #{bgcolor} цветом.

80 голосов
/ 30 декабря 2008

Чтение и изменение буфера обмена с веб-страницы поднимает вопросы безопасности и конфиденциальности. Однако в Internet Explorer это можно сделать. Я нашел этот пример фрагмента :

    <script type="text/javascript">
        function select_all(obj) {
            var text_val=eval(obj);
            text_val.focus();
            text_val.select();
            r = text_val.createTextRange();
            if (!r.execCommand) return; // feature detection
            r.execCommand('copy');
        }
    </script>
    <input value="http://www.sajithmr.com"
     onclick="select_all(this)" name="url" type="text" />
67 голосов
/ 03 декабря 2014

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

Копировать простой текст в буфер обмена относительно просто, если вы хотите сделать это во время события копирования системы (пользователь нажимает Ctrl C или использует меню браузера).

var isIe = (navigator.userAgent.toLowerCase().indexOf("msie") != -1 
           || navigator.userAgent.toLowerCase().indexOf("trident") != -1);

document.addEventListener('copy', function(e) {
    var textToPutOnClipboard = "This is some text";
    if (isIe) {
        window.clipboardData.setData('Text', textToPutOnClipboard);    
    } else {
        e.clipboardData.setData('text/plain', textToPutOnClipboard);
    }
    e.preventDefault();
});

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

Кроме этого, есть несколько опций для каждого браузера.

Это самый простой в IE, где вы можете получить доступ к объекту clipboardData в любое время из JavaScript через:

window.clipboardData

(Однако, если вы попытаетесь сделать это вне системного события вырезания, копирования или вставки, IE предложит пользователю предоставить разрешение буфера обмена веб-приложения.)

В Chrome вы можете создать расширение Chrome, которое даст вам разрешения на буфер обмена (это то, что мы делаем для Lucidchart). Тогда для пользователей с установленным расширением вам просто нужно самостоятельно запустить системное событие:

document.execCommand('copy');

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

47 голосов
/ 14 сентября 2017

Вот мой взгляд на это ..

function copy(text) {
    var input = document.createElement('input');
    input.setAttribute('value', text);
    document.body.appendChild(input);
    input.select();
    var result = document.execCommand('copy');
    document.body.removeChild(input)
    return result;
 }
45 голосов
/ 11 августа 2015

clipboard.js - небольшая, не Flash, утилита, позволяющая копировать текстовые или HTML-данные в буфер обмена. Это очень легко использовать, просто включите .js и используйте что-то вроде этого:

<button id='markup-copy'>Copy Button</button>

<script>
document.getElementById('markup-copy').addEventListener('click', function() {
  clipboard.copy({
    'text/plain': 'Markup text. Paste me into a rich text editor.',
    'text/html': '<i>here</i> is some <b>rich text</b>'
  }).then(
    function(){console.log('success'); },
    function(err){console.log('failure', err);
  });

});
</script>

clipboard.js также включен GitHub .

Примечание: Это устарело. Миграция на здесь .

34 голосов
/ 22 ноября 2013

ZeroClipboard - лучшее кросс-браузерное решение, которое я нашел:

<div id="copy" data-clipboard-text="Copy Me!">Click to copy</div>    
<script src="ZeroClipboard.js"></script>
<script>
  var clip = new ZeroClipboard( document.getElementById('copy') );
</script>

Если вам нужна поддержка не-flash для iOS, просто добавьте запасной вариант:

clip.on( 'noflash', function ( client, args ) {
    $("#copy").click(function(){            
        var txt = $(this).attr('data-clipboard-text');
        prompt ("Copy link, then click OK.", txt);
    });
});  

http://zeroclipboard.org/

https://github.com/zeroclipboard/ZeroClipboard

25 голосов
/ 04 мая 2011

В одном из проектов, над которым я работал, плагин jQuery для копирования в буфер обмена, использующий библиотеку Zero Clipboard .

Его легче использовать, чем нативный плагин Zero Clipboard, если вы большой пользователь jQuery.

...