JavaScript navigator.clipboard API

10 сентября 2026
Автор

Антон Ларичев

Что такое Clipboard API

Clipboard API — это современный браузерный интерфейс, позволяющий программно читать и записывать данные в системный буфер обмена. Он доступен через объект navigator.clipboard и предоставляет асинхронные методы для работы с текстом, изображениями и произвольными данными.

В отличие от устаревшего document.execCommand('copy'), новый API:

  • Работает асинхронно и возвращает промисы
  • Поддерживает не только текст, но и изображения и другие форматы
  • Требует явного предоставления разрешений пользователем
  • Доступен только в защищённых контекстах (HTTPS или localhost)
// Проверка поддержки API
if (navigator.clipboard) {
  console.log('Clipboard API поддерживается');
} else {
  console.log('Clipboard API не поддерживается');
}

Курс по теме

Основы JavaScript — курс

40 000+ студентов · рейтинг 4.8 · гарантия возврата 30 дней

Запись в буфер обмена

Наиболее часто используемый метод — запись текста в буфер обмена. Принимает строку и возвращает промис, который разрешается после успешной записи.

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

copyText('Привет, мир!');

Пример кнопки копирования с визуальной обратной связью:

const button = document.querySelector('#copy-button');
const textToCopy = 'npm install purpleschool-utils';

button.addEventListener('click', async () => {
  try {
    await navigator.clipboard.writeText(textToCopy);
    button.textContent = 'Скопировано!';
    setTimeout(() => {
      button.textContent = 'Копировать';
    }, 2000);
  } catch (error) {
    console.error('Не удалось скопировать текст:', error);
  }
});

Для записи нетекстовых данных — изображений, HTML и других форматов — используется метод write(). Он принимает массив объектов ClipboardItem.

async function copyImage(imageUrl) {
  try {
    const response = await fetch(imageUrl);
    const blob = await response.blob();

    const clipboardItem = new ClipboardItem({
      [blob.type]: blob
    });

    await navigator.clipboard.write([clipboardItem]);
    console.log('Изображение скопировано в буфер обмена');
  } catch (error) {
    console.error('Ошибка при копировании изображения:', error);
  }
}

Запись HTML вместе с текстовым fallback:

async function copyAsHtml(htmlString, plainText) {
  try {
    const htmlBlob = new Blob([htmlString], { type: 'text/html' });
    const textBlob = new Blob([plainText], { type: 'text/plain' });

    const clipboardItem = new ClipboardItem({
      'text/html': htmlBlob,
      'text/plain': textBlob
    });

    await navigator.clipboard.write([clipboardItem]);
    console.log('HTML скопирован в буфер обмена');
  } catch (error) {
    console.error('Ошибка:', error);
  }
}

copyAsHtml(
  '<strong>Жирный текст</strong>',
  'Жирный текст'
);

Чтение из буфера обмена

Метод readText() возвращает промис со строкой — текущим текстовым содержимым буфера обмена.

async function pasteText() {
  try {
    const text = await navigator.clipboard.readText();
    console.log('Содержимое буфера:', text);
    return text;
  } catch (error) {
    console.error('Ошибка при чтении буфера обмена:', error);
  }
}

// Вставить содержимое в поле ввода по кнопке
const input = document.querySelector('#text-input');
const pasteButton = document.querySelector('#paste-button');

pasteButton.addEventListener('click', async () => {
  const text = await pasteText();
  if (text !== undefined) {
    input.value = text;
  }
});

Метод read() позволяет читать данные любого типа из буфера обмена. Возвращает массив объектов ClipboardItem, каждый из которых содержит данные в одном или нескольких форматах.

async function readClipboard() {
  try {
    const items = await navigator.clipboard.read();

    for (const item of items) {
      console.log('Доступные типы:', item.types);

      for (const type of item.types) {
        const blob = await item.getType(type);

        if (type === 'text/plain') {
          const text = await blob.text();
          console.log('Текст:', text);
        } else if (type.startsWith('image/')) {
          const url = URL.createObjectURL(blob);
          const img = document.createElement('img');
          img.src = url;
          document.body.appendChild(img);
        }
      }
    }
  } catch (error) {
    console.error('Ошибка при чтении буфера обмена:', error);
  }
}

Практический пример — загрузка вставленного изображения на сервер:

async function handleImagePaste() {
  try {
    const items = await navigator.clipboard.read();

    for (const item of items) {
      const imageType = item.types.find(type => type.startsWith('image/'));
      if (!imageType) continue;

      const blob = await item.getType(imageType);

      const formData = new FormData();
      formData.append('image', blob, 'clipboard-image.png');

      const response = await fetch('/api/upload', {
        method: 'POST',
        body: formData
      });

      const result = await response.json();
      console.log('Изображение загружено:', result.url);
    }
  } catch (error) {
    console.error('Ошибка при вставке изображения:', error);
  }
}

Разрешения и безопасность

Clipboard API тесно связан с Permissions API. Браузер автоматически разрешает запись, если действие произошло в ответ на пользовательское взаимодействие (клик, нажатие клавиши). Чтение, как правило, требует явного разрешения.

Проверка разрешений

async function checkClipboardPermissions() {
  try {
    const readPermission = await navigator.permissions.query({
      name: 'clipboard-read'
    });
    console.log('Разрешение на чтение:', readPermission.state);
    // 'granted', 'denied' или 'prompt'

    const writePermission = await navigator.permissions.query({
      name: 'clipboard-write'
    });
    console.log('Разрешение на запись:', writePermission.state);
  } catch (error) {
    console.error('Ошибка проверки разрешений:', error);
  }
}

Отслеживание изменений разрешений

async function watchClipboardPermission() {
  const permission = await navigator.permissions.query({
    name: 'clipboard-read'
  });

  permission.addEventListener('change', () => {
    if (permission.state === 'granted') {
      console.log('Теперь можно читать из буфера обмена');
    } else if (permission.state === 'denied') {
      console.log('Доступ к буферу обмена запрещён');
    }
  });
}

Ключевые ограничения безопасности

  • API доступен только в защищённых контекстах (HTTPS или localhost)
  • Чтение из буфера обмена требует разрешения пользователя или фокуса на странице (зависит от браузера)
  • В ряде браузеров запись разрешена только при активном взаимодействии пользователя
  • Web Workers и расширения не имеют доступа к этому API
if (!window.isSecureContext) {
  console.warn('Clipboard API доступен только в защищённых контекстах (HTTPS)');
}

Обработка ошибок

При работе с Clipboard API необходимо обрабатывать несколько типов ошибок и реализовывать fallback для старых браузеров.

async function safeCopyText(text) {
  if (!navigator.clipboard) {
    return fallbackCopyText(text);
  }

  try {
    await navigator.clipboard.writeText(text);
    return true;
  } catch (error) {
    if (error.name === 'NotAllowedError') {
      console.error('Доступ к буферу обмена запрещён пользователем');
    } else if (error.name === 'SecurityError') {
      console.error('Ошибка безопасности: требуется HTTPS');
    } else {
      console.error('Неизвестная ошибка:', error.message);
    }
    return fallbackCopyText(text);
  }
}

function fallbackCopyText(text) {
  const textArea = document.createElement('textarea');
  textArea.value = text;
  textArea.style.cssText = 'position:fixed;opacity:0;pointer-events:none';
  document.body.appendChild(textArea);
  textArea.focus();
  textArea.select();

  try {
    return document.execCommand('copy');
  } catch (error) {
    console.error('Fallback не сработал:', error);
    return false;
  } finally {
    document.body.removeChild(textArea);
  }
}

Практические примеры

Менеджер буфера обмена с fallback

class ClipboardManager {
  constructor(feedbackElement) {
    this.feedbackEl = feedbackElement;
  }

  async copy(text) {
    const success = await this.#writeToClipboard(text);
    this.#showFeedback(success ? 'Скопировано!' : 'Ошибка копирования');
    return success;
  }

  async paste() {
    try {
      return await navigator.clipboard.readText();
    } catch {
      return null;
    }
  }

  async #writeToClipboard(text) {
    if (!navigator.clipboard) return this.#fallbackCopy(text);
    try {
      await navigator.clipboard.writeText(text);
      return true;
    } catch {
      return this.#fallbackCopy(text);
    }
  }

  #fallbackCopy(text) {
    const textarea = Object.assign(document.createElement('textarea'), {
      value: text,
      style: 'position:fixed;opacity:0'
    });
    document.body.appendChild(textarea);
    textarea.select();
    const ok = document.execCommand('copy');
    document.body.removeChild(textarea);
    return ok;
  }

  #showFeedback(message) {
    if (!this.feedbackEl) return;
    this.feedbackEl.textContent = message;
    setTimeout(() => { this.feedbackEl.textContent = ''; }, 2000);
  }
}

// Инициализация и навешивание обработчиков
const clipboard = new ClipboardManager(document.querySelector('#feedback'));

document.querySelectorAll('[data-copy]').forEach(button => {
  button.addEventListener('click', () => clipboard.copy(button.dataset.copy));
});

Кнопки копирования для блоков кода

function initCodeCopyButtons() {
  document.querySelectorAll('pre code').forEach(codeBlock => {
    const pre = codeBlock.parentElement;
    const button = document.createElement('button');
    button.className = 'copy-code-btn';
    button.textContent = 'Копировать';
    pre.style.position = 'relative';
    pre.appendChild(button);

    button.addEventListener('click', async () => {
      try {
        await navigator.clipboard.writeText(codeBlock.textContent);
        button.textContent = 'Скопировано!';
        button.classList.add('success');
      } catch {
        button.textContent = 'Ошибка';
        button.classList.add('error');
      } finally {
        setTimeout(() => {
          button.textContent = 'Копировать';
          button.classList.remove('success', 'error');
        }, 2000);
      }
    });
  });
}

document.addEventListener('DOMContentLoaded', initCodeCopyButtons);

Перехват системных событий копирования и вставки

Помимо программного доступа, браузер генерирует события copy, cut и paste, которые можно перехватывать.

// Добавить атрибуцию к тексту при копировании
document.addEventListener('copy', (event) => {
  const selection = window.getSelection().toString();
  if (!selection) return;

  const attribution = '\n\nИсточник: purpleschool.ru';
  event.clipboardData.setData('text/plain', selection + attribution);
  event.preventDefault();
});

// Обработать вставку изображения или текста
document.addEventListener('paste', (event) => {
  event.preventDefault();
  const pasteArea = document.querySelector('#paste-area');

  for (const item of event.clipboardData.items) {
    if (item.type.startsWith('image/')) {
      const blob = item.getAsFile();
      const url = URL.createObjectURL(blob);
      const img = document.createElement('img');
      img.src = url;
      pasteArea.appendChild(img);
    } else if (item.type === 'text/plain') {
      item.getAsString(text => {
        pasteArea.textContent = text;
      });
    }
  }
});

Совместимость с браузерами

Clipboard API поддерживается во всех современных браузерах:

  • Chrome / Edge: полная поддержка с версии 76+
  • Firefox: readText() и writeText() — с версии 63+; read() и write() — с ограничениями по типам
  • Safari: текст — с версии 13.1+; изображения — с версии 15.4+

Для проверки доступных возможностей в конкретном окружении:

function getClipboardCapabilities() {
  return {
    hasClipboardAPI: 'clipboard' in navigator,
    hasWriteText: 'clipboard' in navigator && 'writeText' in navigator.clipboard,
    hasReadText: 'clipboard' in navigator && 'readText' in navigator.clipboard,
    hasWrite: 'clipboard' in navigator && 'write' in navigator.clipboard,
    hasRead: 'clipboard' in navigator && 'read' in navigator.clipboard,
    isSecureContext: window.isSecureContext,
  };
}

console.table(getClipboardCapabilities());

Clipboard API является предпочтительным способом работы с буфером обмена в современных приложениях. При этом для максимальной совместимости стоит всегда держать наготове fallback через document.execCommand — он поддерживается даже там, где новый API недоступен.

Чтобы углубиться в работу с браузерными API и современным JavaScript, рекомендуем курс PurpleSchool: https://purpleschool.ru/course/javascript?utm_source=knowledgebase&utm_medium=text&utm_campaign=javascript-clipboard-api

Стрелочка влевоrequestAnimationFrame — плавные анимации в JavaScriptMutationObserver — наблюдение за изменениями DOMСтрелочка вправо

Постройте личный план изучения Javascript до уровня Middle — бесплатно!

Javascript — часть карты развития Frontend

  • step100+ шагов развития
  • lessons30 бесплатных лекций
  • lessons300 бонусных рублей на счет

Бесплатные лекции

Все гайды по Javascript

Как работает метод trim() - JavaScriptКак работает метод toUpperCase() - JavaScriptКак работает метод toLowerCase() - JavaScriptКак работает метод startsWith() - JavaScriptКак работает метод substring() - JavaScriptКак работает метод split() - JavaScriptКак работает метод slice() - JavaScriptКак работает метод search() - JavaScriptКак работает метод replaceAll() - JavaScriptКак работает метод replace() - JavaScriptКак работает метод repeat() - JavaScriptКак работает метод padStart() - JavaScriptКак работает метод padEnd() - JavaScriptКак работает метод matchAll() - JavaScriptКак работает метод match() - JavaScriptКак работает метод localeCompare() - JavaScriptКак работает свойство length - JavaScriptКак работает метод lastIndexOf() - JavaScriptКак работает метод indexOf() - JavaScriptКак работает метод includes() - JavaScriptКак работает метод fromCodePoint() - JavaScriptКак работает метод fromCharCode() - JavaScriptКак работает метод endsWith() - JavaScriptКак работает метод concat() - JavaScriptКак работает метод codePointAt() - JavaScriptКак работает метод charCodeAt() - JavaScriptКак работает метод charAt() - JavaScript
Итератор в JavaScript
try...catch в JavaScriptError в JavaScript
Событие wheel в JavaScriptСобытие unload в JavaScriptСобытие touch в JavaScriptСобытие submit в JavaScriptСобытие scroll в JavaScriptСобытие reset в JavaScriptМетод .preventDefault() в JavaScriptСобытие mouseover в JavaScriptСобытие mouseout в JavaScriptСобытие load в JavaScriptСобытие keyup в JavaScriptСобытие keydown в JavaScriptСобытие invalid в JavaScriptСобытие input в JavaScriptСобытийная модель Event в JavaScriptОбъект события Event в JavaScriptСобытие DOMContentLoaded в JavaScriptСобытие dblclick в JavaScriptСобытие click в JavaScriptСобытие change в JavaScriptBroadcast Channel API в JavaScriptJavaScript BroadcastChannel — межвкладочное взаимодействиеСобытие beforeunload в JavaScript
Error cause — цепочки ошибок в JavaScript
Методы массивов toSorted, toReversed и withКак работает метод some() - JavaScriptКак работает метод reverse() - JavaScriptКак работает метод reduce() - JavaScriptКак работает метод map() - JavaScriptКак работает метод isArray() - JavaScriptКак работает метод indexOf() - JavaScriptКак работает метод includes() - JavaScriptКак работает метод from() - JavaScriptКак работает метод forEach() - JavaScriptКак работает метод flatMap() - JavaScriptКак работает метод flat() - JavaScriptКак работает метод findIndex() - JavaScriptКак работает метод find() - JavaScriptКак работает метод filter() - JavaScriptКак работает метод every() - JavaScriptМассивы в JavaScriptArray.at, findLast, findLastIndex — новые методы массивов
Открыть базу знаний

Лучшие курсы по теме

изображение курса

Основы JavaScript

Антон Ларичев
AI-тренажерыAI-тренажеры
Практика в студииПрактика в студии
Гарантия
Бонусы
иконка звёздочки рейтинга4.8
3 999 ₽ 6 990 ₽
Подробнее
изображение курса

TypeScript с нуля

Антон Ларичев
AI-тренажерыAI-тренажеры
Практика в студииПрактика в студии
Гарантия
Бонусы
иконка звёздочки рейтинга4.8
3 999 ₽ 6 990 ₽
Подробнее
изображение курса

Next.js - с нуля

Антон Ларичев
AI-тренажерыAI-тренажеры
Практика в студииПрактика в студии
Гарантия
Бонусы
иконка звёздочки рейтинга4.7
3 999 ₽ 6 990 ₽
Подробнее

Отправить комментарий