Подтвердить что ты не робот

Javascript cut/copy/paste в буфер обмена: как Google решил его?

Да, этот вопрос задавался снова и снова: как скопировать и вставить из и в системный буфер обмена с помощью javascript? До сих пор я нашел только частичные решения и хаки. Причина, по которой его часто спрашивали в прошлом, заключается в том, что до сих пор нет рабочего решения. Тем не менее, я видел, что Google Docs фактически имеет рабочее решение теперь как для клавиатурных событий, так и для кнопок. Таким образом, это возможно, но как они это делают? Software Salad article, Доступ к системному буферу с JavaScript - Святой Грааль? дает хороший обзор проблемы (но это несколько лет).

Короче:

  • вы можете использовать события клавиатуры ctrl + x, ctrl + c, ctrl + v, чтобы либо скопировать текст из скрытого текстового поля с подготовленными данными, либо вставить скрытый текст в скрытое поле, а затем сделать что-то с ним

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

  • вы можете использовать "реальное" решение с помощью clipboardData.setData для IE и execCommand для других браузеров, что зависит от одобрения пользователя.

Любая идея, как Google решил проблему с буфером обмена?

4b9b3361

Ответ 1

[ Примечание: Этот ответ был точным в момент его написания и правильно ответил на вопрос OP. Однако с тех пор технология развивается; если вы заинтересованы в поддержке копирования и вставки в своем веб-приложении, см. другие, более свежие ответы на этой странице. — ruakh]


Однако, я видел, что Google Docs на самом деле имеет рабочее решение теперь как для клавиатурных событий, так и для кнопок.

Нет, это не так. На самом деле, нет. Для событий клавиатуры Google Docs ничего не делает; он просто не блокирует функцию копирования и вставки браузера по умолчанию; поэтому пользователи могут свободно копировать и вставлять файлы, не закрывая Google Docs. Для кнопок Google Docs не поддерживает системный буфер обмена, а его собственный "веб-буфер обмена", который полностью находится в Документах Google. Вы не можете использовать кнопки панели инструментов для копирования текста для вставки в другую программу на своем компьютере или для вставки текста, который был скопирован из другой программы на вашем компьютере.

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

Ответ 2

Я знаю, что вопрос был опубликован давно, но мне нужно было проверить, как это делает Google, поэтому, возможно, кто-то найдет это полезным.

На самом деле google использует также системный буфер обмена, но это немного сложно. В случае, если вы используете сочетание клавиш, вы можете поймать событие copy/paste/cut, например. окна:

window.addEventListener('copy', function (ev) {
    console.log('copy event');
    // you can set clipboard data here, e.g.
    ev.clipboardData.setData('text/plain', 'some text pushed to clipboard');
    // you need to prevent default behaviour here, otherwise browser will overwrite your content with currently selected 
    ev.preventDefault();
});

живой пример для сочетания клавиш: http://jsfiddle.net/tyk9U/

К сожалению, это только решение для сочетания клавиш, и есть проблема с контекстным меню, потому что вы не можете получить доступ к данным буфера обмена без собственного (доверенного) экземпляра copy/cut/paste. Но Google делает интересный трюк. Существует API document.execCommand(), который позволяет запускать команды для контент-контента, и есть команда "copy", которую вы можете запустить с помощью document.execCommand('copy'). Но когда вы попробуете это на консоли в Chrome, он вернет false. Я потратил немного времени на это, и выяснилось, что у них установлено расширение Chrome, называемое "Google Диск" (перейдите в chrome://apps/, и вы увидите его там), который позволяет использовать буфер обмена для доменов drive.google. com и docs.google.com. Откройте какой-либо документ или таблицу и введите консоль document.execCommand('copy') - она ​​вернет true. Когда вы удаляете расширение, вы не сможете использовать операции буфера обмена из контекстного меню.

Вы можете создать такое приложение для себя с очень простым файлом манифеста (подробности здесь https://developer.chrome.com/apps/first_app):

{
    "manifest_version": 2,
    "name": "App name",
    "description": "App description",
    "version": "1.0",
    "app": {
        "urls": [
            "http://your.app.url.here/"
        ],
        "launch": {
            "web_url": "http://your.app.url.here/"
        }
    },
    "icons": {
        "128": "x-128.png"
    },
    "permissions": [
        "clipboardRead",
        "clipboardWrite"
    ]
}

"Разрешения" здесь позволяет выполнять операции с буфером обмена.

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

Конечно, это описание действительно только для Chrome.

Ответ 3

В дополнение к тому, что другие уже опубликовали в этом потоке, я создал полностью рабочий пример, демонстрирующий подход к сокращению клавиатуры (CTRL + C или CMD + C в Mac OS X), а также пользовательский кнопочный подход, который запускает копировать действие.

Полную демонстрационную версию можно найти здесь: http://jsfiddle.net/rve7d/

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

if(window.clipboardData) {
    // use just 'Text' or 'Url' as a first param otherwise strange exception is thrown
    window.clipboardData.setData('Text', 'Text that will be copied to CB');        
} else if(ev.originalEvent.clipboardData) {
    ev.originalEvent.clipboardData.setData('text/plain', 'Text that will be copied to CB');      
} else {
    alert('Clipboard Data are not supported in this browser. Sorry.');
}

PS: Мне нужна эта функциональность для нашего пользовательского компонента "Просмотр электронных таблиц" и способа анализа исходного кода электронных таблиц Google, поэтому мое решение в основном соответствует их решению.

Ответ 4

Google использует очень простой, но классный метод. Используя firebug, вы узнаете, что загруженный html-код имеет текстовую область в начале размера 1. Что такое google doc, так это то, что когда пользователь выбирает текст и нажимает ctrl + c, он захватывает событие и по какой-либо методике получает текст, который выбран в контейнере doc и устанавливает значение текстовой области для этого содержимого. Затем он фокусирует и выбирает текстовую область. Теперь он освобождает событие ctrl + c. Но теперь текст выбирается в текстовой области, поэтому при повторном событии браузер копирует текст в текстовую область и, таким образом, мы получаем скопированный текст

Ответ 5

<p>COPY : </p>
<p>Email me at <a class="js-emaillink" href="mailto:[email protected]">[email protected]</a></p>
<p><button class="js-emailcopybtn" value="clipboard" >clipboard</button></p>
<textarea rows="10" cols = "12"></textarea>
<p>CUT: </p>
<p><textarea class="js-cuttextarea">Hello I'm some text</textarea></p>
<p><button class="js-textareacutbtn" disable>Cut Textarea</button></p>
<script>
//copy clipboard
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();
});
//cut
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');
  }
});
</script>