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 решил проблему с буфером обмена?
Ответы
Ответ 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>