Реалізація Clipboard API на сайті
Clipboard API — замінює застарілий document.execCommand('copy'). Асинхронний, permission-based, підтримує не тільки текст, але й зображення, HTML, користувацькі формати даних. Працює через navigator.clipboard і вимагає Secure Context.
Базове використання
Копіювання тексту — найчастіший сценарій:
async function copyToClipboard(text: string): Promise<void> {
if (!navigator.clipboard) {
// Fallback для HTTP-контексту або старих браузерів
const textarea = document.createElement('textarea')
textarea.value = text
textarea.style.cssText = 'position:fixed;opacity:0;pointer-events:none'
document.body.appendChild(textarea)
textarea.select()
document.execCommand('copy')
document.body.removeChild(textarea)
return
}
await navigator.clipboard.writeText(text)
}
async function readFromClipboard(): Promise<string> {
return navigator.clipboard.readText()
}
Дозволи
Запис (writeText, write) не вимагає явного запиту дозволу — достатньо, що сторінка у фокусі та дія ініціюється користувачем. Читання (readText, read) запрашивает дозвіл clipboard-read через Permissions API:
async function checkClipboardPermission(): Promise<PermissionState> {
const result = await navigator.permissions.query({
name: 'clipboard-read' as PermissionName,
})
return result.state // 'granted' | 'denied' | 'prompt'
}
Робота зі зображеннями
async function copyImageToClipboard(blob: Blob): Promise<void> {
const item = new ClipboardItem({ [blob.type]: blob })
await navigator.clipboard.write([item])
}
async function copyCanvasToClipboard(canvas: HTMLCanvasElement): Promise<void> {
const blob = await new Promise<Blob>((resolve, reject) =>
canvas.toBlob((b) => (b ? resolve(b) : reject(new Error('Помилка конвертації'))), 'image/png')
)
await copyImageToClipboard(blob)
}
Читаємо зображення з буфера:
async function pasteImage(): Promise<HTMLImageElement | null> {
const items = await navigator.clipboard.read()
for (const item of items) {
const imageType = item.types.find((t) => t.startsWith('image/'))
if (imageType) {
const blob = await item.getType(imageType)
const url = URL.createObjectURL(blob)
const img = new Image()
img.src = url
img.onload = () => URL.revokeObjectURL(url)
return img
}
}
return null
}
React-хук зі станом зворотного зв'язку
function useCopyToClipboard(resetDelay = 2000) {
const [copied, setCopied] = useState(false)
const timerRef = useRef<ReturnType<typeof setTimeout>>()
const copy = useCallback(async (text: string) => {
try {
await copyToClipboard(text)
setCopied(true)
clearTimeout(timerRef.current)
timerRef.current = setTimeout(() => setCopied(false), resetDelay)
} catch {
setCopied(false)
}
}, [resetDelay])
useEffect(() => () => clearTimeout(timerRef.current), [])
return { copy, copied }
}
// Компонент
function CopyButton({ text }: { text: string }) {
const { copy, copied } = useCopyToClipboard()
return (
<button onClick={() => copy(text)}>
{copied ? 'Скопійовано!' : 'Копіювати'}
</button>
)
}
Подія Paste на рівні документа
Перехоплення вставки для drag-and-drop редакторів та завантажувачів зображень:
document.addEventListener('paste', async (event: ClipboardEvent) => {
const items = event.clipboardData?.items ?? []
for (const item of Array.from(items)) {
if (item.type.startsWith('image/')) {
event.preventDefault()
const file = item.getAsFile()
if (file) await handleImagePaste(file)
}
}
})
Що входить у роботу
Реалізація утилітів копіювання/вставки з fallback, React-хук useCopyToClipboard, підтримка тексту та зображень, візуальна зворотна вязь (іконка, тост, tooltip). Опціонально — інтеграція з редактором або завантажувачем файлів.
Строк: половина дня.







