Начиная с IE10 добавлена поддержка команд «Копировать» и «Вырезать» с помощью метода Document.execCommand(). Так же эти методы доступны в Chrome начиная с версии 43.

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

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

Примеры


Для примера, давайте добавим кнопку которая скопирует email адрес в буфер обмена.

Мы добавим email адрес, в наш HTML, с кнопкой клик по которой будет инициировать копирование email:
<p>Email me at <a class="js-emaillink" href="mailto:matt@example.co.uk">matt@example.co.uk</a></p>

<p><button class="js-emailcopybtn"><img src="./images/copy-icon.png" /></button></p>

Тогда, в наш JavaScript, мы добавим обработчик клика по кнопке, который выделит email из содержимого ссылки js-emaillink, выполнит команду копирования, так что бы адрес электронной почты оказался в буфере пользователя и после этого снять выделение с электронной почты, так что пользователь даже не увидит выделение.
var copyEmailBtn = document.querySelector('.js-emailcopybtn');  
copyEmailBtn.addEventListener('click', function(event) {  
  // Выборка ссылки с электронной почтой 
  var emailLink = document.querySelector('.js-emaillink');  
  var range = document.createRange();  
  range.selectNode(emailLink);  
  window.getSelection().addRange(range);  
    
  try {  
    // Теперь, когда мы выбрали текст ссылки, выполним команду копирования
    var successful = document.execCommand('copy');  
    var msg = successful ? 'successful' : 'unsuccessful';  
    console.log('Copy email command was ' + msg);  
  } catch(err) {  
    console.log('Oops, unable to copy');  
  }  
    
  // Снятие выделения - ВНИМАНИЕ: вы должны использовать
  // removeRange(range) когда это возможно
  window.getSelection().removeAllRanges();  
});

Здесь используется метод API выделения, window.getSelection(), что бы программно выделить текст внутри ссылки, который мы хотим скопировать в буфер обмена пользователя. После вызова document.execCommand() мы можем снять выделение с помощью window.getSelection().removeAllRanges().
Если вы хотите проверить что все прошло успешно, то вы можете рассмотреть результат возвращаемый функцией document.execCommand(). Результат будет false если функция не поддерживается или отключена. Мы «обвернули» execCommand() в try...catch, т.к. команды «вырезать» и «копировать» в некоторых случаях могут вернуть ошибку.

Команда «вырезать» может быть использована для текстовых полей ввода, там, где вы хотите удалить текст и поместить этот текст в буфер.

Использование textarea и кнопки:
<p><textarea class="js-cuttextarea">Hello I'm some text</textarea></p>
  
<p><button class="js-textareacutbtn" disable>Cut Textarea</button></p>

Мы можем сделать следующее, что бы «вырезать» содержимое:
var cutTextareaBtn = document.querySelector('.js-textareacutbtn');

cutTextareaBtn.addEventListener('click', function(event) {  
  var cutTextarea = document.querySelector('.js-cuttextarea');  
  cutTextarea.select();

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

queryCommandSupported и queryCommandEnabled


Перед вызовом document.execCommand() вы должны убедится что эти API поддерживаются с помощью document.queryCommandSupported(). В примере выше, мы могли бы заблокировать кнопку, по результатам проверки совместимости, например, так:
copyEmailBtn.disabled = !document.queryCommandSupported('copy');

Отличие между queryCommandSupported() и queryCommandEnabled() в том, что команды «копировать» и «вырезать» могут поддерживаться браузером, но если текст не выделен, то они не будут доступны. Это удобно в том случае если вы не выбрали фрагмент текста программно и хотите что бы команда отработала ожидаемо, в противном случае показать сообщение пользователю.

Совместимость с браузерами


IE 10+, Chrome 43+ и Opera 29+

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

Safari не поддерживает данные команды.

Известные проблемы


Комментарии (25)


  1. Demetros
    19.04.2015 19:00
    +3

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


    1. edejin Автор
      19.04.2015 19:19

      Просто по тому, что не было подобных API. Точнее были, но, по-моему, только в Firefox.
      Вот тут на помощь и приходят Flash и Java, и начинаются «приседания» с версией…
      И в каждом браузере свои настройки Java, Flash, да и безопасности в целом.
      Как вы понимаете, давать доступ к буферу обмена пользователя, всем подряд, довольно смело.


      1. Keyten
        20.04.2015 01:43
        +1

        Мне почему-то кажется, что какой-то API на эту тему был ещё в IE6.
        Не исключено, что какой-нибудь встроенный ActiveX.


        1. PastorGL
          20.04.2015 13:50
          +2

          С пятой версии IE поддерживал работу с буфером обмена через dataTransfer. msdn.microsoft.com/en-us/library/ms537658.aspx

          Обычная история с IE… Собственный стандарт, сильно опередивший время, а теперь W3C спека делается с оглядкой на.


    1. forgotten
      19.04.2015 19:45
      +7

      Потому что говносайты будут бесконечно забивать буфер обмена своими «познакомься с горячими девушками в своём городе»


      1. edejin Автор
        19.04.2015 19:51
        +2

        Молотком можно гвозди забивать, а можно и человека убить.

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


        1. forgotten
          19.04.2015 19:57
          +6

          По-моему, разнообразных разрешений в браузере и так многовато.



          github.com/w3ctag/spec-reviews/issues/45


          1. edejin Автор
            19.04.2015 20:13
            -3

            Мне, как «тыжпрограммисту», стала немного проще жить после скандала с Амиго.
            Пользователи стали внимательнее при установке программ.
            Может после пары-тройки прецедентов с буфером люди еще поумнеют.


            1. forgotten
              19.04.2015 20:16
              +14

              > люди еще поумнеют

              Кто-кто поумнеет? Люди???


              1. edejin Автор
                19.04.2015 20:21

                Я надеюсь.
                В крайнем случае естественный отбор расставит все по своим местам.


                1. Houston
                  19.04.2015 20:48
                  +4

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


                  1. forgotten
                    19.04.2015 21:17
                    +1

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


            1. AlexP11223
              19.04.2015 22:38
              +1

              Что за скандал?


              1. edejin Автор
                20.04.2015 09:13

                Первая попавшаяся ссылка привела меня сюда


      1. 23rd
        20.04.2015 17:45

        Во флеше это решено так:

        In the application sandbox of Adobe AIR, setData() can be called anytime. In other contexts, setData() can only be called in response to a user-generated event such as a key press or mouse click.


    1. Isopropil
      19.04.2015 21:14
      +1

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


  1. ghaiklor
    19.04.2015 21:04
    +2

    А как же copy()?

    copy('Hello');
    


    1. justboris
      20.04.2015 00:15

      эта функция работает только в консоли. В статье речь о том, как добавить кнопку копирования на веб-страницу.
      Например, как эта кнопка на Github (там работает через flash для поддержки всех браузеров):


  1. hssergey
    20.04.2015 04:39
    +1

    Если бы это было бы еще совместимо с мобильными браузерами, где в большинстве своем не поддерживается Flash…


  1. JeStoneDev
    20.04.2015 07:10
    +1

    Выглядит не менее ужасно, чем flash.
    Видимо я не доживу до подобного API:

    clipboard.copy();       // копировать текущее выделение
    clipboard.cut();        // вырезать текущее выделение, если в поле textarea, input, иначе скопировать
    clipboard.paste();      // вставить текст из буфера обмена по текущему положению курсора
    clipboard.put('text');  // сохранить переданный текст
    myVar = clipboard.get();// получить содержимое буфера в переменную
    


    1. Sayonji
      20.04.2015 08:05
      -1

      Paste точно никогда не будет.


      1. Ualde
        20.04.2015 09:11

        Вы не совсем правы.
        Уже дали ссылку ниже, но поставлю акцент — w3c.github.io/clipboard-apis/#the-paste-action


    1. ReklatsMasters
      20.04.2015 08:55

      Уже дожили, Clipboard API. В Огнелисе работает достаточно давно, в самых новых Хром и, кажется, в старой Опере.


  1. mavrin
    21.04.2015 01:06

    В chrome, скрипт сработал даже в версии 42.0.2311.90 jsfiddle.net/541sbywh, а вот в IE досадно, если даже запретить выделение, операция возвращает true. А так можно было бы сделать для пользователей, у которых не сработал этот метод, предложить скопировать вручную.


    1. mavrin
      21.04.2015 01:23

      Хотя, если после операции проверить результат, то можно все таки этого добиться jsfiddle.net/541sbywh/2.