Три способа скопировать текст в буфер обмена на JavaScript: от древних до современных
Вы когда-нибудь задумывались, как работает магия кнопки "Скопировать"? Нажимаешь — и ссылка, код или промокод уже в буфере обмена. Без вспышек, без скролла, без лишних движений. Эта, казалось бы, простая задача — один из самых популярных вопросов на Stack Overflow. И неспроста: реализация копирования в буфер обмена на JavaScript таит в себе подводные камни, разницу браузеров и целых три эпохи развития веба.
В этой статье мы разберём все способы копирования текста в буфер обмена с помощью JavaScript: от старого доброго (и почти мёртвого) document.execCommand до современного, асинхронного navigator.clipboard. Вы узнаете не только как скопировать текст, но и как сделать это безопасно, красиво и с поддержкой старых браузеров. Поехали!
Почему копирование в буфер обмена — это не "просто"
На первый взгляд кажется: ну что там сложного? Взял текст, отправил в буфер. Но браузеры — параноики. Они не хотят, чтобы любой скрипт на сайте мог читать или писать в буфер обмена без вашего ведома. Представьте: вы открываете сайт, а он в фоне подменяет скопированный пароль на "SendBitcoinToMe". Ужас, правда?
Поэтому у JavaScript есть строгие правила: копирование возможно только при активном действии пользователя (клик, нажатие клавиши). И даже с этим условием — не все API работают одинаково. Давайте разбираться.
Способ №1: Классика — document.execCommand('copy')
Как это работает
Раньше, в эпоху динозавров (примерно до 2020 года), единственным способом скопировать текст в буфер обмена был трюк с execCommand. Идея простая:
- Создаёте временный
textareaилиinput. - Помещаете в него нужный текст.
- Выделяете его (через
select()). - Вызываете
document.execCommand('copy'). - Удаляете временный элемент.
Вот как это выглядит в коде:
function copyToClipboardLegacy(text) {
// Создаём временный элемент
const textarea = document.createElement('textarea');
textarea.value = text;
// Делаем его невидимым
textarea.style.position = 'fixed';
textarea.style.opacity = '0';
document.body.appendChild(textarea);
// Выделяем текст
textarea.select();
textarea.setSelectionRange(0, 99999); // Для мобильных устройств
// Копируем
try {
const successful = document.execCommand('copy');
if (successful) {
console.log('Текст скопирован!');
} else {
console.error('Копирование не удалось');
}
} catch (err) {
console.error('Ошибка копирования:', err);
}
// Убираем за собой
document.body.removeChild(textarea);
}
Плюсы и минусы
Плюсы: работает даже в Internet Explorer 9. Не требует разрешений.
Минусы: считается устаревшим (deprecated). Синхронный — блокирует главный поток, пока копирует. Неудобно копировать большой объём данных. И самое главное — в современных браузерах его могут выпилить в любой момент.
Совет: если вы пишете новый проект — не используйте execCommand. Но для поддержки совсем старых браузеров (IE11, старый Edge) это единственный вариант.
Способ №2: Современность — Clipboard API (navigator.clipboard)
Как это работает
На смену костылям пришёл Clipboard API — асинхронный, безопасный и гибкий. Он живёт в объекте navigator.clipboard и умеет не только писать, но и читать из буфера обмена (с разрешения пользователя).
Для копирования используется метод writeText():
async function copyToClipboardModern(text) {
try {
await navigator.clipboard.writeText(text);
console.log('Текст успешно скопирован!');
} catch (err) {
console.error('Не удалось скопировать текст:', err);
}
}
// Использование
copyToClipboardModern('Привет, мир!');
Всё! Никаких временных элементов, никаких выделений. Одна строчка — и дело в шляпе. Но есть нюансы.
Требования и ограничения
- HTTPS: Clipboard API работает только на защищённых страницах (или localhost). На HTTP — отказ.
- Разрешения: Браузер может запросить у пользователя разрешение на запись в буфер.
- Пользовательский жест: Вызов должен происходить в ответ на действие пользователя (клик, нажатие). Если вызвать
writeText()просто при загрузке страницы — браузер его проигнорирует. - Поддержка: Все современные браузеры (Chrome, Firefox, Safari, Edge) поддерживают Clipboard API с 2020 года. Полная таблица — на CanIUse.
Как запросить разрешение (на всякий случай)
Обычно writeText() работает без явного запроса, если вызван из обработчика клика. Но если вы хотите быть уверены на 100%, можно проверить разрешение:
async function copyWithPermission(text) {
// Проверяем, есть ли разрешение
let permission = await navigator.permissions.query({ name: 'clipboard-write' });
if (permission.state === 'granted' || permission.state === 'prompt') {
await navigator.clipboard.writeText(text);
} else {
console.error('Нет разрешения на запись в буфер');
}
}
Способ №3: Универсальный гибрид (лучшее из двух миров)
В реальных проектах редко используют только один метод. Вам нужна поддержка и старенького IE, и новенького Chrome. Поэтому пишут "полифилл" — функцию, которая пробует Clipboard API, а если не вышло — падает на execCommand.
Вот пример такой универсальной функции:
async function copyToClipboard(text) {
// Пробуем современный API
if (navigator.clipboard && window.isSecureContext) {
try {
await navigator.clipboard.writeText(text);
return true;
} catch (err) {
console.warn('Clipboard API не сработал, пробуем fallback:', err);
}
}
// Fallback: старый способ с textarea
try {
const textarea = document.createElement('textarea');
textarea.value = text;
textarea.style.position = 'fixed';
textarea.style.left = '-9999px';
textarea.style.top = '-9999px';
document.body.appendChild(textarea);
textarea.focus();
textarea.select();
const result = document.execCommand('copy');
document.body.removeChild(textarea);
if (result)