Tarayıcıda panoya metin kopyalamanın bugünkü yolu Clipboard API'dir: bir düğmenin click dinleyicisinde await navigator.clipboard.writeText(metin) çağırırsınız, okumak için de navigator.clipboard.readText() kullanırsınız. İki metot da söz (promise) döndürür, yalnızca güvenli bağlamda (HTTPS) çalışır ve tarayıcı izin vermezse NotAllowedError ile reddedilir. Metin dışındaki veriler, örneğin HTML veya PNG görsel, ClipboardItem nesneleriyle write() ve read() üzerinden taşınır.
Bu yazıda dört metodun farkını, izin ve kullanıcı etkinliği kurallarını, tarayıcıların bu kuralları nasıl farklı uyguladığını, ClipboardItem ile çoklu biçim kopyalamayı, copy, cut ve paste olaylarını ezmeyi ve sık yapılan hataları MDN belgelerine dayanarak anlatıyoruz. Serinin tamamı için Web API öğrenme rehberi sayfasına bakabilirsiniz.
Clipboard API neyi çözer?

Sistem panosu, işletim sistemine ait ve uygulamalar arasında kısa süreli veri taşımak için kullanılan bir arabellektir. Web sayfaları uzun süre bu arabelleğe document.execCommand("copy") gibi eşzamanlı bir komutla erişti. MDN bu yöntemi artık kullanımdan kalkmış (deprecated) olarak işaretler ve pano erişimi için Clipboard API'yi önerir. Yeni API eşzamansızdır: işlemler bir söz döndürür, böylece büyük bir görselin panoya yazılması sayfayı dondurmaz.
API iki parçadan oluşur. Birincisi navigator.clipboard üzerinden erişilen Clipboard nesnesidir ve sistem panosunu doğrudan okuyup yazar. MDN bu nesnenin Web Worker içinde bulunmadığını, yani WorkerNavigator üzerinde olmadığını özellikle not eder. İkincisi, kullanıcı klavye kısayolu veya sağ tık menüsüyle kopyalama, kesme ve yapıştırma yaptığında öğeler üzerinde tetiklenen copy, cut ve paste olaylarıdır. Bu olayların varsayılan davranışını kendi kodunuzla değiştirebilirsiniz.
| Metot | Ne yapar | Söz neyle tamamlanır |
|---|---|---|
| writeText(metin) | Panoya düz metin yazar | Değer yok, pano güncellenince tamamlanır |
| readText() | Panodaki metni okur | Metin dizesi (metin yoksa boş dize) |
| write(ogeler) | ClipboardItem dizisini yazar | Değer yok, veri yazılınca tamamlanır |
| read(secenekler) | Panodaki öğeleri okur | ClipboardItem dizisi |
Güvenli bağlam, izin ve kullanıcı etkinliği
Clipboard API'nin bütün metotları güvenli bağlam ister. Sayfanız HTTPS üzerinden ya da yerel geliştirme adresi gibi güvenli sayılan bir kaynaktan sunulmuyorsa navigator.clipboard kullanılamaz. Sitenizi HTTPS'e taşırken karışık içerik uyarıları alıyorsanız karma içerik hatası çözümü, sertifika ve TLS ayarları için HTTPS ve TLS 1.3 rehberi yazılarına bakabilirsiniz.
İkinci koşul kullanıcının sayfayla gerçekten etkileşimde olmasıdır. MDN'nin kullanıcı etkinliği sayfası read(), readText(), write() ve writeText() metotlarını geçici kullanıcı etkinliği (transient activation) gerektiren API'ler arasında sayar. Geçici etkinlik, kullanıcının az önce bir tuşa basması, tıklaması gibi güvenilir bir etkileşimden sonra kısa bir süre geçerli olan durumdur ve zaman aşımıyla sona erer. Pratikte bu, pano çağrısını sayfa yüklenirken değil, bir düğmenin tıklama dinleyicisinin içinde yapmanız gerektiği anlamına gelir.
Spesifikasyon okuma için hem yakın zamanlı etkileşim hem de kullanıcının tarayıcı ya da işletim sisteminin "Yapıştır" öğesini seçmesini ister; yazma için ise Permissions API'deki clipboard-write izninin verilmiş olmasını bekler. MDN, tarayıcıların bu konuda spesifikasyondan ayrıştığını açıkça yazar ve durumu şöyle özetler:
| Durum | Chromium tabanlı tarayıcılar | Firefox ve Safari |
|---|---|---|
| Okuma kuralı karşılanmadığında | Belge odaktaysa clipboard-read izni istenir, izin varsa okuma başarılı olur | Geçici etkinlik varsa tek "Yapıştır" seçenekli geçici bir menü açılır |
| Yazma | clipboard-write izni veya geçici etkinlik yeterli | Geçici etkinlik gerekir |
| clipboard-read ve clipboard-write izinleri | Desteklenir, verilen izin kalıcıdır | Desteklenmez ve MDN'ye göre planlanmıyor |
| iframe içinde kullanım | Permissions-Policy ile clipboard-read ve clipboard-write izni gerekir | Ayrıntı için MDN uyumluluk tablosuna bakın |
Bu tablodan çıkan pratik ders şudur: kodunuzu en katı kurala göre yazın. Pano çağrısını her zaman bir kullanıcı eylemine bağlayın, hata durumunu yakalayın ve kullanıcıya anlaşılır bir mesaj gösterin. İzin hataları ve NotAllowedError gibi adlar DOMException ve güvenli bağlam hataları yazısında ayrıntılı anlatılıyor; kullanıcı etkinliği kavramı da orada kısaca açıklanıyor.
writeText ve readText ile metin kopyalama
En sık ihtiyaç, bir kod parçasını veya bağlantıyı tek tıkla kopyalatmaktır. MDN'nin örneğini Türkçeleştirerek aktarıyoruz. Tarayıcı gerektirdiği için çalıştırılmadı. HTML tarafında kaynak metin, iki düğme ve sonucun yazılacağı bir paragraf var:
<p id="kaynak">Kopyalanacak örnek metin.</p>
<button id="kopyala" type="button">Kopyala</button>
<button id="yapistir" type="button">Yapıştır</button>
<p id="hedef"></p>const kaynak = document.querySelector("#kaynak");
const hedef = document.querySelector("#hedef");
document.querySelector("#kopyala").addEventListener("click", async () => {
try {
await navigator.clipboard.writeText(kaynak.textContent);
hedef.textContent = "Kopyalandı.";
} catch (hata) {
hedef.textContent = `Kopyalanamadı: ${hata.message}`;
}
});
document.querySelector("#yapistir").addEventListener("click", async () => {
try {
hedef.textContent = await navigator.clipboard.readText();
} catch (hata) {
hedef.textContent = `Yapıştırılamadı: ${hata.message}`;
}
});writeText() tek parametre alır: panoya yazılacak dize. Söz, pano içeriği güncellendiğinde tamamlanır; yazmaya izin verilmezse NotAllowedError ile reddedilir. readText() parametre almaz ve panodaki metnin bir kopyasıyla tamamlanır. MDN, pano boşsa, metin içermiyorsa veya içerik temsilleri arasında metin yoksa boş dize döndüğünü belirtir. Yani boş dize her zaman "hata" anlamına gelmez; kullanıcı panoya bir görsel kopyalamış olabilir. Pano metin olarak temsil edilebilecek bir veri içerdiğini söyleyip bu temsili sağlayamazsa NotFoundError fırlatılır.
Sonucu ekrana basarken textContent kullanmaya dikkat edin. Panodan gelen metin kullanıcının başka bir yerden kopyaladığı herhangi bir şey olabilir; bunu innerHTML ile sayfaya koymak HTML olarak yorumlanmasına yol açar. İki özelliğin farkı innerHTML, textContent ve innerText farkı yazısında anlatılıyor.
ClipboardItem ile birden fazla biçim

write() ve read() metin yerine ClipboardItem dizileriyle çalışır. Tek bir ClipboardItem, aynı içeriğin birden fazla temsilini taşıyabilir ve her temsil bir medya türüyle (MIME türü) anahtarlanır. Örneğin kalın yazı içeren bir paragrafı hem text/html hem de text/plain olarak kopyalarsanız, yapıştırılan uygulama anladığı en iyi biçimi seçer; HTML gösteremeyen bir metin kutusu düz metne düşer.
ClipboardItem() kurucusunun ilk parametresi, anahtarları MIME türü olan bir nesnedir. MDN her değerin bir Blob, bir dize ya da bunlardan birine çözülen bir söz olabileceğini belirtir. İkinci, isteğe bağlı parametredeki presentationStyle seçeneği "unspecified" (varsayılan), "inline" veya "attachment" değerini alır ve yapıştıran uygulamaya içeriğin satır içinde mi yoksa ek olarak mı eklenmesi gerektiğini ipucu olarak bildirir.
Temsilleri hazırlamak için kullanılan Blob tarayıcıdan bağımsız bir API olduğundan bu kısmı Node.js 24 ile çalıştırıp çıktıları doğruladık. size değerinin bayt cinsinden olduğuna dikkat edin: "ı" harfi UTF-8 içinde iki bayt tuttuğu için düz metin 11 değil 12 bayttır. Son fonksiyon, okuma tarafında tercih sırasına göre en uygun biçimi seçmek için kullanabileceğiniz sade bir yardımcıdır:
const html = "<p><strong>Kalın</strong> metin</p>";
const metin = "Kalın metin";
const temsiller = {
"text/html": new Blob([html], { type: "text/html" }),
"text/plain": new Blob([metin], { type: "text/plain" }),
};
console.log(temsiller["text/html"].size); // 36
console.log(temsiller["text/plain"].size); // 12
console.log(await temsiller["text/plain"].text()); // Kalın metin
function enIyiTur(mevcut, tercihler) {
return tercihler.find((t) => mevcut.includes(t)) ?? null;
}
console.log(enIyiTur(["text/plain", "text/html"], ["image/png", "text/html", "text/plain"])); // text/html
console.log(enIyiTur(["text/plain"], ["image/png", "text/html"])); // nullTarayıcı tarafında bu nesneyi ClipboardItem içine koyup yazmak ve okurken types dizisinde dolaşıp getType() ile her temsilin Blob değerini almak şöyle görünür. Kod MDN örneğinin uyarlamasıdır ve çalıştırılmadı:
document.querySelector("#kopyala-html").addEventListener("click", async () => {
const kutu = document.querySelector("#zengin");
try {
const oge = new ClipboardItem({
"text/html": new Blob([kutu.innerHTML], { type: "text/html" }),
"text/plain": new Blob([kutu.innerText], { type: "text/plain" }),
});
await navigator.clipboard.write([oge]);
} catch (hata) {
console.error(hata.message);
}
});
document.querySelector("#yapistir-html").addEventListener("click", async () => {
try {
const [oge] = await navigator.clipboard.read();
for (const tur of oge.types) {
const blob = await oge.getType(tur);
console.log(tur, await blob.text());
}
} catch (hata) {
console.error(hata.message);
}
});Hangi MIME türleri destekleniyor?
MDN'ye göre tarayıcıların read() ve write() ile text/plain, text/html ve image/png temsillerini desteklemesi zorunludur. image/svg+xml ve özel biçimler isteğe bağlıdır. Bir türün desteklenip desteklenmediğini yazmadan önce ClipboardItem.supports(tur) ile sorabilirsiniz; zorunlu üç tür için bu metot her zaman true döner, yani onları test etmenize gerek yoktur. Bazı tarayıcı ve işletim sistemi birleşimlerinin tek bir ClipboardItem yazmaya izin verdiğini de unutmayın: MDN, sistem birden fazla öğeyi desteklemiyorsa dizideki yalnızca ilk öğenin yazıldığını, bazı ortamların fazlasında hata verdiğini belirtir.
Özel biçimler, MIME türünün başına "web" sözcüğü ve bir boşluk eklenerek tanımlanır; örneğin "web text/x-envanter+json". Bu sayede uygulamanız, standart bir karşılığı olmayan yapılandırılmış veriyi kayıpsız kopyalayabilir; aynı öğeye ekleyeceğiniz HTML ve düz metin temsilleri ise diğer uygulamalar için yedek görevi görür. Özel biçim desteği tarayıcıya göre değiştiği için kullanmadan önce supports() ile kontrol edin ve tarayıcı desteği için MDN uyumluluk tablosuna bakın.
Görsel kopyalama ve temizlenmemiş HTML
Bir canvas içeriğini panoya görsel olarak koymak için önce canvas'ı toBlob() ile bir PNG Blob değerine çevirir, sonra new ClipboardItem({ [blob.type]: blob }) ile yazarsınız. Okurken types dizisinde "image/png" arar, getType("image/png") ile aldığınız Blob için URL.createObjectURL() ile bir adres üretip bir img öğesinde gösterirsiniz. Canvas'ı görsele çevirme ayrıntıları Canvas görsel ve piksel işlemleri, Blob ve nesne adresleri File API, FileReader ve Blob yazısında anlatılıyor.
read() isteğe bağlı bir formats nesnesi de alır. Bu nesnenin unsanitized dizisine yazdığınız MIME türleri tarayıcı tarafından temizlenmeden döndürülür. MDN, Chromium tabanlı tarayıcıların okuma sırasında HTML içindeki script etiketlerini ve tehlikeli olabilecek diğer içerikleri ayıkladığını örnek verir. Temizlenmemiş HTML'i sayfaya eklemek XSS riski taşır; bu riskin genel çerçevesi XSS ve CSP koruma rehberi yazısında.
copy, cut ve paste olaylarını ezmek
Kullanıcı Ctrl+C gibi bir kısayol kullandığında tarayıcı odaktaki öğede copy olayını tetikler ve varsayılan davranış olarak seçili metni panoya koyar. Olay nesnesi bir ClipboardEvent örneğidir ve clipboardData özelliğinde bir DataTransfer taşır. copy ve cut dinleyicilerinde setData(bicim, veri) ile panoya gidecek veriyi belirler, paste dinleyicisinde getData(bicim) ile yapıştırılacak veriyi okursunuz. Varsayılan davranışı iptal etmek için preventDefault() çağırmanız gerekir. MDN, tarayıcının gönderdiği olaylarda clipboardData değerinin hiçbir zaman null olmadığını, yalnızca kurucuyla elle oluşturulan olaylarda null olabileceğini belirtir.
Aşağıdaki örnek MDN'deki örneğin uyarlamasıdır: bir metin kutusunda kopyalanan metnin sonuna kaynak notu eklenir, yapıştırılan metin ise büyük harfe çevrilerek imlecin olduğu yere konur. Çalıştırılmadı.
<textarea id="alan" rows="3">Bu metnin bir kısmını seçip kopyalayın veya yapıştırın.</textarea>const alan = document.querySelector("#alan");
alan.addEventListener("copy", (event) => {
const secili = alan.value.slice(alan.selectionStart, alan.selectionEnd);
event.clipboardData.setData("text/plain", `${secili} (kaynak: örnek site)`);
event.preventDefault();
});
alan.addEventListener("paste", (event) => {
const gelen = event.clipboardData.getData("text/plain");
const { selectionStart, selectionEnd, value } = alan;
alan.value = value.slice(0, selectionStart) + gelen.toUpperCase() + value.slice(selectionEnd);
event.preventDefault();
});Bu olaylar navigator.clipboard metotlarından farklıdır: kullanıcı zaten kopyalama veya yapıştırma eylemini başlattığı için izin istemi çıkmaz, ama yalnızca o eylem sırasında çalışırsınız. Olay dinleyicileri ve preventDefault() hakkında daha fazlası olay yayılımı ve preventDefault yazısında var.
Son olarak, Clipboard nesnesi bir EventTarget olduğu için üzerinde clipboardchange olayını dinleyebilirsiniz. Bu olay sistem panosu herhangi bir kaynaktan değiştiğinde gelir; kendi pano geçmişini tutan uygulamalar için kullanışlıdır. MDN olayın yalnızca kalıcı kullanıcı etkinliğinden (sticky activation) sonra veya clipboard-read izni verildikten sonra gönderildiğini belirtir. Tarayıcı desteği için MDN uyumluluk tablosuna bakın.
Sık yapılan hatalar
- Sayfa yüklenirken panoya yazmaya çalışmak. Geçici kullanıcı etkinliği olmadan yapılan çağrılar reddedilebilir; çağrıyı bir tıklama dinleyicisine taşıyın.
- HTTP üzerinde test etmek. Güvenli bağlam dışında
navigator.clipboardkullanılamaz. Yerel geliştirmede güvenli sayılan adresi, yayında HTTPS kullanın. - Hata yakalamamak.
awaitedilen çağrıyıtry...catchiçine almazsanız reddedilen söz sessizce kaybolur ve kullanıcı kopyalamanın neden çalışmadığını anlamaz. Söz yapısı için async/await rehberi yazısına bakın. - Boş dizeyi hata saymak.
readText()panoda metin yoksa boş dize döndürür; bu bir izin hatası değildir. clipboard-readiznine güvenmek. Bu izin bütün tarayıcılarda yoktur; okuma akışınız izin istemi olmadan da anlamlı bir mesaj göstermeli.- iframe içinde izin vermeyi unutmak. Gömülü sayfada pano kullanacaksanız Permissions-Policy ile ilgili izinleri açmanız gerekir.
- Panodan gelen HTML'i doğrudan
innerHTMLile eklemek. Özellikleunsanitizedile okunan içerik güvenilmez kabul edilmelidir.
Sık Sorulan Sorular
document.execCommand("copy") hâlâ çalışır mı?
MDN execCommand() yöntemini kullanımdan kalkmış olarak işaretler ve pano erişimi için Clipboard API'yi önerir. Bazı düzenleme senaryolarında hâlâ geçerli kullanım alanları olduğunu da belirtir, ama yeni kodda kopyalama ve yapıştırma için navigator.clipboard tercih edilmelidir.
Neden "NotAllowedError" alıyorum?
Bu hata tarayıcının pano erişimine izin vermediğini gösterir. En sık nedenler sayfanın güvenli bağlamda olmaması, çağrının bir kullanıcı etkileşiminin dışında yapılması, kullanıcının izin istemini reddetmesi veya iframe için gerekli iznin verilmemesidir.
Panoya görsel kopyalayabilir miyim?
Evet. image/png desteği zorunlu türler arasındadır. Görseli bir PNG Blob olarak hazırlayıp ClipboardItem içinde write() ile yazarsınız. SVG gibi diğer türler için önce ClipboardItem.supports() ile kontrol edin.
Kullanıcının panosunu arka planda izleyebilir miyim?
Hayır, API bunu engelleyecek şekilde tasarlanmıştır. Okuma geçici kullanıcı etkinliği ve çoğu tarayıcıda açık bir kullanıcı onayı ister. clipboardchange olayı da yalnızca kalıcı kullanıcı etkinliği veya okuma izni olduğunda gönderilir ve size içeriği değil, değişikliğin olduğunu bildirir.
Marka adları sahiplerine aittir. Bu yazı bağımsız bir teknik rehberdir; adı geçen kuruluşlarla bayilik veya ortaklık ilişkisi bulunduğu anlamına gelmez.
İlgili içerikler: Web API öğrenme rehberi · DOMException ve güvenli bağlam hataları · File API ve Blob · Canvas görsel işlemleri · innerHTML ve textContent farkı · HTTPS ve TLS 1.3 rehberi