Avatar ·

Как скопировать текст в буфер обмена с помощью JavaScript?

Всем привет! Я начинающий фронтенд-разработчик и столкнулся с, казалось бы, простой задачей: нужно сделать кнопку, которая копирует какой-то текст (например, ссылку или код) в буфер обмена пользователя. Я перепробовал несколько способов из старых статей, но они либо работают криво, либо вообще не работают в современных браузерах. Вот что я пробовал: ```javascript function copyText() { const text = document.getElementById('myText').innerText; // Пытаюсь использовать execCommand const textarea = document.createElement('textarea'); textarea.value = text; document.body.appendChild(textarea); textarea.select(); document.execCommand('copy'); document.body.removeChild(textarea); alert('Скопировано!'); } ``` Этот код работает в старых браузерах, но в консоли Chrome я вижу предупреждение, что `execCommand` устарел. Кроме того, он создает лишние элементы в DOM, и это выглядит как костыль. Еще я слышал про Clipboard API, но не понимаю, как правильно его использовать, особенно с учетом поддержки старых браузеров. Подскажите, пожалуйста, современный и надежный способ копирования текста в буфер обмена на чистом JavaScript без jQuery? Нужно, чтобы работало в Chrome, Firefox, Safari и Edge. Заранее спасибо! <<>>

Современное решение: Clipboard API (асинхронный и безопасный)

Лучший способ на сегодняшний день — использовать Clipboard API, а именно метод navigator.clipboard.writeText(). Этот API работает асинхронно (возвращает Promise) и не требует создания временных элементов. Он поддерживается во всех современных браузерах (Chrome 66+, Firefox 63+, Safari 13.1+, Edge 79+).

Базовый пример

async function copyToClipboard(text) {
  try {
    await navigator.clipboard.writeText(text);
    console.log('Текст скопирован!');
  } catch (err) {
    console.error('Ошибка копирования: ', err);
  }
}

// Использование:
document.getElementById('copyBtn').addEventListener('click', () => {
  const text = document.getElementById('myText').innerText;
  copyToClipboard(text);
});

Важные нюансы

  • Контекст безопасности: Clipboard API работает только в защищенном контексте (HTTPS или localhost). На обычном HTTP он будет недоступен.
  • Разрешения: Браузер может запросить разрешение у пользователя при первом вызове. Лучше обрабатывать ошибку, если пользователь отказал.
  • Вызов из обработчика события: Метод должен вызываться в ответ на действие пользователя (клик, нажатие клавиши), иначе браузер может заблокировать его.

Универсальное решение с fallback (для поддержки старых браузеров)

Если нужно поддерживать старые браузеры (например, IE11 или старые версии Safari), используйте комбинацию Clipboard API и резервного метода с execCommand:

function fallbackCopyTextToClipboard(text) {
  const textArea = document.createElement("textarea");
  textArea.value = text;
  textArea.style.position = "fixed";  // Избегаем прокрутки
  textArea.style.opacity = 0;
  document.body.appendChild(textArea);
  textArea.focus();
  textArea.select();

  try {
    const successful = document.execCommand('copy');
    if (successful) {
      console.log('Скопировано через fallback');
    } else {
      console.error('Не удалось скопировать');
    }
  } catch (err) {
    console.error('Ошибка fallback: ', err);
  }

  document.body.removeChild(textArea);
}

async function copyToClipboardModern(text) {
  if (!navigator.clipboard) {
    // Clipboard API не поддерживается
    fallbackCopyTextToClipboard(text);
    return;
  }
  try {
    await navigator.clipboard.writeText(text);
    console.log('Скопировано через Clipboard API');
  } catch (err) {
    console.error('Ошибка Clipboard API, пробуем fallback: ', err);
    fallbackCopyTextToClipboard(text);
  }
}

Советы для лучшего UX

  • Не используйте alert() после копирования — это раздражает. Лучше покажите всплывающее уведомление (toast) или измените текст кнопки на "Скопировано!".
  • Для копирования из input или textarea можно использовать select() + execCommand как запасной вариант, но Clipboard API предпочтительнее.
  • Всегда проверяйте, что вы копируете строку. Если нужно скопировать HTML-содержимое (с форматированием), используйте navigator.clipboard.write() с объектом ClipboardItem, но это сложнее.

В итоге: для 95% современных проектов достаточно простого navigator.clipboard.writeText(). Добавьте fallback только если вам нужно поддерживать IE11 или очень старые версии браузеров.

<<>>

Альтернативный подход: копирование через выделение текста

Если вы не хотите использовать Clipboard API по каким-то причинам (например, из-за строгих политик безопасности), можно обойтись старым добрым способом с document.execCommand('copy'), но с небольшим улучшением — не создавать элемент в DOM, а использовать уже существующий элемент input или textarea на странице.

function copyFromInput(elementId) {
  const input = document.getElementById(elementId);
  if (!input) return;

  // Сохраняем текущее положение курсора
  const currentStart = input.selectionStart;
  const currentEnd = input.selectionEnd;

  input.select();
  input.setSelectionRange(0, 99999); // Для мобильных устройств

  try {
    document.execCommand('copy');
    console.log('Скопировано из поля ввода');
  } catch (err) {
    console.error('Ошибка копирования: ', err);
  }

  // Восстанавливаем выделение
  input.setSelectionRange(currentStart, currentEnd);
  input.blur();
}

Плюсы: не нужно создавать временные элементы, работает быстро.
Минусы: требует наличия видимого или скрытого textarea/input на странице, execCommand помечен как устаревший и может быть удален в будущих версиях браузеров.

Этот метод подходит для простых случаев, когда вы точно знаете, что пользователь работает в десктопном браузере, и не хотите заморачиваться с асинхронностью. Но для продакшена я рекомендую первый вариант с Clipboard API.