Опубликовано: 3 июля 2026 г.
Если вы когда-либо пользовались редактором документов, табличным редактором или любым другим приложением, где основным действием является вставка содержимого из буфера обмена, вы знаете ожидания пользователя: вставка должна происходить мгновенно.
К сожалению, во многих случаях веб-приложения считывают больше данных из буфера обмена, чем им необходимо, что может привести к замедлению процесса вставки контента, нерациональному использованию ресурсов процессора и использованию памяти для данных, которые приложению никогда не понадобятся.
Функция выборочного чтения форматов решает эту проблему производительности, модифицируя API асинхронного буфера обмена , чтобы лучше соответствовать ожиданиям пользователей и предоставлять разработчикам то, что им нужно — чтение только тех форматов, которые действительно необходимы приложению.
Проблема: необходимо заранее считать данные во всех форматах.
Когда пользователь копирует данные из любого приложения — например, Excel, Google Docs или Photoshop — приложение обычно записывает в буфер обмена системы несколько вариантов представления одних и тех же данных: обычный текст, HTML, изображение, а иногда и пользовательские форматы. Веб-приложению, которое считывает данные (когда пользователь вставляет их из буфера обмена), редко требуются все эти варианты представления. Редактору электронных таблиц может понадобиться HTML. Текстовому редактору может понадобиться команда «вставить как обычный текст». Редактору кода может понадобиться только текст и ничего больше.
До сих пор navigator.clipboard.read() извлекал все доступные представления данных из системного буфера обмена, выполнял очистку (которая может занять время, особенно при работе с большими объемами HTML-кода или изображений) и хранил байты в памяти, даже для форматов, которые приложение никогда не использовало. Чем больше неиспользуемый объем данных, тем дольше пользователю приходилось ждать, и тем больше памяти потребляла вкладка, как раз в тот момент, когда скорость вставки имеет первостепенное значение.
Решение: Просто перечислить, а затем прочитать по запросу.
Начиная с Chrome и Edge (версии 149), объекты ClipboardItem возвращаемые методом navigator.clipboard.read() , описывают только содержимое буфера обмена , а не предоставляют доступ к данным сразу. Фактические байты для заданного формата считываются из буфера обмена только при вызове ClipboardItem.getType(mimeType) . Интерфейс API остаётся неизменным. Тот же код, который вы написали вчера, по-прежнему работает сегодня, обеспечивая немедленное повышение производительности:
// Enumerate the available MIME types.
// No payload is fetched yet.
const items = await navigator.clipboard.read();
for (const item of items) {
if (item.types.includes('text/html')) {
// Only read the HTML data.
// The data is fetched (and sanitized) here only.
const blob = await item.getType('text/html');
useThePastedHtml(await blob.text());
break;
}
// text/plain, image/png, or any other custom formats
// stored in the clipboard are never read.
}
Еще одно преимущество в плане производительности заключается в автоматическом кэшировании. Дважды вызвав getType('text/html') для одного и того же ClipboardItem , вы считываете данные из системного буфера обмена только один раз, а при втором вызове возвращаете кэшированный Blob .
Преимущества
Задержка. Работа, которая должна была выполняться при вызове navigator.clipboard.read() , например, копирование байтов между процессами, очистка HTML или декодирование изображений, теперь выполняется только для тех форматов, которые действительно нужны вашему приложению. Приложения, которые читают только один формат, получают наибольшую экономию. Чем больше форматов вы пропускаете, тем больше выигрыш. HTML выигрывает больше всего, потому что очистка HTML — дорогостоящий процесс.
Память. Ранее вызов navigator.clipboard.read() , когда в буфере обмена находилось изображение размером 50 МБ, означал, что 50 МБ памяти требовалось использовать в памяти рендерера веб-страницы до тех пор, пока объект ClipboardItem не был освобожден, даже если приложению требовалось только text/plain представление данных. Теперь память в процессе рендеринга используют только те форматы, которые ваше приложение считывает при вызове метода ClipboardItem.getType() . Для приложений, где часто используется вставка, таких как редакторы документов, почтовые редакторы или инструменты дизайна, это значительно снижает использование памяти во время вставки, особенно в многовкладочных сессиях, где несколько редакторов могут одновременно считывать данные из буфера обмена.
Никаких дополнительных настроек не требуется. Каждый сайт в интернете получает эти преимущества автоматически. Вам не нужно переносить код или ограничивать доступ к нему за логикой определения функций.
Одно из изменений в поведении, о котором стоит знать.
При последующем чтении данных происходит небольшое изменение в поведении: объект ClipboardItem больше не является зафиксированным снимком буфера обмена в момент завершения вызова navigator.clipboard.read() .
Когда вызывается read() и возвращает объект ClipboardItem , этот объект представляет собой лишь отложенный дескриптор данных в системном буфере обмена. Если содержимое буфера обмена изменяется между вызовом read() и последующим вызовом getType() , этот вызов будет отклонен с InvalidStateError . Это справедливо даже для формата, который вы ранее успешно считывали с того же ClipboardItem .
// Read from the clipboard.
const items = await navigator.clipboard.read();
// Get the first ClipboardItem.
const item = items[0];
// Actually fetch the data now, which works fine.
const text = await item.getType('text/plain');
// The user writes something new to the clipboard
// by copying from another app, or your own code writes to
// the clipboard again.
await navigator.clipboard.writeText('something new');
// Fetching the data from the earlier ClipboardItem
// rejects with InvalidStateError, because the contents
// of the clipboard changed.
const html = await item.getType('text/html');
Это происходит потому, что возврат данных, которые больше не соответствуют содержимому буфера обмена, был бы запутанным и в противном случае привел бы к трудноотлаживаемым проблемам.
На практике это изменение незаметно для большинства кода, поскольку приложения обычно вызывают read() и getType() последовательно внутри одного и того же обработчика вставки. Однако вам следует следить за случаями, когда ваш код сохраняет объект ClipboardItem за пределами асинхронных операций.
Вот пример, где это изменение может стать проблемой: кэширование объектов ClipboardItem в window , их отображение в пользовательском интерфейсе истории обмена или ожидание взаимодействия с пользователем перед вызовом getType() . Чтобы избежать проблем в подобном сценарии:
- Оберните вызовы
getType()в блокиtry / catchи рассматривайтеInvalidStateErrorкак сигнал о том, что буфер обмена переместился и код должен прочитать данные заново. - Если вам необходимо сохранить данные из буфера обмена, скопируйте
Blobв свою собственную структуру сразу после вызоваgetType(). Не рассматривайте объектClipboardItemкак долговременное хранилище. - Используйте событие
clipboardChangeдля упреждающей аннулирования устаревших дескрипторов.
Стандарты и поддержка браузеров
Функция выборочного чтения формата — это изменение спецификации API буфера обмена, инициированное Рабочей группой по веб-редактированию и командой веб-платформы Microsoft Edge.
Safari также поддерживает такое поведение, а Firefox выразил положительную позицию в отношении стандартов и намерение к их соответствию.
Незаметное, но значимое улучшение производительности и совместимости API буфера обмена, благодаря чему Chromium теперь переходит на ту же модель.