Копирование в буфер обмена на JavaScript: 3 рабочих способа

Онлайн-тренажер Python для начинающих

Изучайте Python легко и без перегрузки теорией. Решайте практические задачи с автоматической проверкой, получайте подсказки на русском языке и пишите код прямо в браузере — без необходимости что-либо устанавливать.

Начать курс

Три способа скопировать текст в буфер обмена на 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)                

Блоги

Рекомендаций по книгам