Pano erişiminin engeli kaldırılıyor

Metin ve resimler için daha güvenli, engelsiz pano erişimi

Sistem panosuna erişmenin geleneksel yolu, pano etkileşimleri için document.execCommand() üzerindenydi. Yaygın bir şekilde desteklenmesine rağmen bu kesme ve yapıştırma yönteminin bir maliyeti vardı: Pano erişimi eşzamanlıydı ve DOM'de yalnızca okuma ve yazma işlemleri yapılabiliyordu.

Bu durum küçük metin parçaları için uygundur, ancak pano aktarımı için sayfayı engellemenin kötü bir deneyim olduğu birçok durum vardır. İçeriğin güvenli bir şekilde yapıştırılabilmesi için zaman alan temizleme veya görüntü kodunu çözme işlemi gerekebilir. Tarayıcının, yapıştırılan bir dokümandaki bağlı kaynakları yüklemesi veya satır içinde yapması gerekebilir. Bu, diskte veya ağda beklerken sayfayı engeller. Karışıma izinler eklediğinizi ve tarayıcının pano erişimi isterken sayfayı engellemesini gerektirdiğini düşünün. Aynı zamanda, pano etkileşimi için document.execCommand() çevresinde alınan izinler serbest şekilde tanımlanmıştır ve tarayıcılar arasında farklılık gösterir.

Async Clipboard API bu sorunları ele alarak sayfayı engellemeyen iyi tanımlanmış bir izin modeli sağlar. Async Clipboard API, çoğu tarayıcıda metin ve resimleri işlemeyle sınırlıdır ancak destek değişiklik gösterir. Aşağıdaki bölümlerin her biri için tarayıcı uyumluluğuna genel bakışı dikkatle inceleyin.

Kopyala: panoya veri yazma

writeText()

Metni panoya kopyalamak için writeText() numaralı telefonu arayın. Bu API eşzamansız olduğundan writeText() işlevi, iletilen metnin başarıyla kopyalanıp kopyalanmadığına bağlı olarak çözümlenen veya reddeden bir Promise döndürür:

async function copyPageUrl() {
  try {
    await navigator.clipboard.writeText(location.href);
    console.log('Page URL copied to clipboard');
  } catch (err) {
    console.error('Failed to copy: ', err);
  }
}

Tarayıcı Desteği

  • 66
  • 79
  • 63
  • 13.1

Kaynak

write()

Aslında writeText(), resimleri panoya kopyalamanıza da olanak tanıyan genel write() yöntemi için kullanışlı bir yöntemdir. writeText() işlevi gibi eşzamansız olup bir Promise döndürür.

Panoya resim yazmak için ilgili resmin blob biçiminde olması gerekir. Bunu yapmanın bir yolu, fetch() kullanarak resmi bir sunucudan istemek ve yanıtta blob() yöntemini çağırmaktır.

Sunucudan resim isteme, çeşitli nedenlerle istenmeyebilir veya mümkün olmayabilir. Neyse ki resmi bir tuvale çizebilir ve tuvale toBlob() yöntemini çağırabilirsiniz.

Daha sonra, write() yöntemine parametre olarak ClipboardItem nesne dizisi iletin. Şu anda tek seferde yalnızca bir resim iletebilirsiniz ancak gelecekte birden fazla resim desteği eklemeyi umuyoruz. ClipboardItem, anahtar olarak görüntünün MIME türüne ve değer olarak bloba sahip bir nesneyi alır. fetch() veya canvas.toBlob()'den alınan blob nesneleri için blob.type özelliği, bir görüntü için otomatik olarak doğru MIME türünü içerir.

try {
  const imgURL = '/images/generic/file.png';
  const data = await fetch(imgURL);
  const blob = await data.blob();
  await navigator.clipboard.write([
    new ClipboardItem({
      // The key is determined dynamically based on the blob's type.
      [blob.type]: blob
    })
  ]);
  console.log('Image copied.');
} catch (err) {
  console.error(err.name, err.message);
}

Alternatif olarak, ClipboardItem nesnesine bir söz yazabilirsiniz. Bu kalıp için verilerin MIME türünü önceden bilmeniz gerekir.

try {
  const imgURL = '/images/generic/file.png';
  await navigator.clipboard.write([
    new ClipboardItem({
      // Set the key beforehand and write a promise as the value.
      'image/png': fetch(imgURL).then(response => response.blob()),
    })
  ]);
  console.log('Image copied.');
} catch (err) {
  console.error(err.name, err.message);
}
öğesine atadığınız bir taahhütte çalıştırın

Tarayıcı Desteği

  • 66
  • 79
  • 13.1

Kaynak

Kopyalama etkinliği

Bir kullanıcının pano kopyalama işlemini başlattığı ancak preventDefault() öğesini çağrı yapmadığı durumlarda copy etkinliği, öğelerin zaten doğru biçimde olduğu bir clipboardData özelliği içerir. Kendi mantığınızı uygulamak istiyorsanız varsayılan davranışın önüne geçmek ve kendi uygulamanızı geliştirmek için preventDefault() yöntemini çağırmanız gerekir. Bu durumda, clipboardData boş olur. Metin ve resim içeren bir sayfa düşünün. Kullanıcı tümünü seçip pano kopyasını başlattığında özel çözümünüz metni silerek yalnızca resmi kopyalamalıdır. Bunu aşağıdaki kod örneğinde gösterildiği gibi yapabilirsiniz. Clipboard API desteklenmediğinde önceki API'lere nasıl geri dönüleceği bu örnekte ele alınmamaktadır.

<!-- The image we want on the clipboard. -->
<img src="kitten.webp" alt="Cute kitten.">
<!-- Some text we're not interested in. -->
<p>Lorem ipsum</p>
document.addEventListener("copy", async (e) => {
  // Prevent the default behavior.
  e.preventDefault();
  try {
    // Prepare an array for the clipboard items.
    let clipboardItems = [];
    // Assume `blob` is the blob representation of `kitten.webp`.
    clipboardItems.push(
      new ClipboardItem({
        [blob.type]: blob,
      })
    );
    await navigator.clipboard.write(clipboardItems);
    console.log("Image copied, text ignored.");
  } catch (err) {
    console.error(err.name, err.message);
  }
});

copy etkinliği için:

Tarayıcı Desteği

  • 1
  • 12
  • 22
  • 3

Kaynak

ClipboardItem için:

Tarayıcı Desteği

  • 76
  • 79
  • 13.1

Kaynak

Yapıştır: panodaki veriler okunuyor

readText()

Panodaki metni okumak için navigator.clipboard.readText() numaralı telefonu arayın ve verilen sözün çözümlenmesini bekleyin:

async function getClipboardContents() {
  try {
    const text = await navigator.clipboard.readText();
    console.log('Pasted content: ', text);
  } catch (err) {
    console.error('Failed to read clipboard contents: ', err);
  }
}

Tarayıcı Desteği

  • 66
  • 79
  • 13.1

Kaynak

Read()

navigator.clipboard.read() yöntemi de eşzamansızdır ve bir vaat döndürür. Panodaki bir resmi okumak için ClipboardItem nesnelerinin bir listesini alın ve bunları yineleyin.

Her ClipboardItem, içeriğini farklı türlerde barındırabilir. Bu nedenle, yine bir for...of döngüsü kullanarak tür listesini tekrarlamanız gerekir. Her tür için karşılık gelen blob'u almak üzere geçerli türle getType() yöntemini bağımsız değişken olarak çağırın. Daha önce olduğu gibi bu kod resimlere bağlı değildir ve gelecekteki diğer dosya türleriyle çalışır.

async function getClipboardContents() {
  try {
    const clipboardItems = await navigator.clipboard.read();
    for (const clipboardItem of clipboardItems) {
      for (const type of clipboardItem.types) {
        const blob = await clipboardItem.getType(type);
        console.log(URL.createObjectURL(blob));
      }
    }
  } catch (err) {
    console.error(err.name, err.message);
  }
}

Tarayıcı Desteği

  • 66
  • 79
  • 13.1

Kaynak

Yapıştırılan dosyalarla çalışma

Kullanıcıların, ctrl+c ve ctrl+v gibi pano klavye kısayollarını kullanabilmesi faydalıdır. Chromium, aşağıda belirtildiği gibi panoda salt okunur dosyaları gösterir. Bu API, kullanıcı işletim sisteminin varsayılan yapıştırma kısayolunu tıkladığında veya tarayıcı menü çubuğunda Düzenle'yi, ardından Yapıştır'ı tıkladığında tetiklenir. Başka sıhhi tesisat koduna gerek yoktur.

document.addEventListener("paste", async e => {
  e.preventDefault();
  if (!e.clipboardData.files.length) {
    return;
  }
  const file = e.clipboardData.files[0];
  // Read the file's contents, assuming it's a text file.
  // There is no way to write back to it.
  console.log(await file.text());
});

Tarayıcı Desteği

  • 3
  • 12
  • 3.6
  • 4

Kaynak

Yapıştırma etkinliği

Daha önce de belirtildiği gibi, Clipboard API ile çalışacak etkinlikler tanıtma planları mevcuttur ancak şimdilik mevcut paste etkinliğini kullanabilirsiniz. Pano metinlerini okumak için kullanılan yeni eşzamansız yöntemlerle birlikte oldukça kullanışlıdır. copy etkinliğinde olduğu gibi, preventDefault() işlevini çağırmayı unutmayın.

document.addEventListener('paste', async (e) => {
  e.preventDefault();
  const text = await navigator.clipboard.readText();
  console.log('Pasted text: ', text);
});

Tarayıcı Desteği

  • 1
  • 12
  • 22
  • 3

Kaynak

Birden fazla MIME türünü işleme

Çoğu uygulama, tek bir kesme veya kopyalama işlemi için panoya birden fazla veri biçimi yerleştirir. Bunun iki nedeni vardır: Uygulama geliştirici olarak kullanıcının metin veya resim kopyalamak istediği uygulamanın özelliklerini bilmenin imkanı yoktur ve birçok uygulama yapılandırılmış verilerin düz metin olarak yapıştırılmasını destekler. Bu menü genellikle Düzenle menü öğesi ve Biçimi yapıştır veya Biçimlendirmeden yapıştır gibi bir ada sahip kullanıcılara sunulur.

Aşağıdaki örnekte bunun nasıl yapılacağı gösterilmektedir. Bu örnekte, görüntü verilerini elde etmek için fetch() kullanılmıştır ancak veriler <canvas> veya File System Access API'den de gelebilir.

async function copy() {
  const image = await fetch('kitten.png').then(response => response.blob());
  const text = new Blob(['Cute sleeping kitten'], {type: 'text/plain'});
  const item = new ClipboardItem({
    'text/plain': text,
    'image/png': image
  });
  await navigator.clipboard.write([item]);
}

Güvenlik ve izinler

Pano erişimi, tarayıcılar için her zaman bir güvenlik sorunu oluşturmuştur. Uygun izinler olmadan, bir sayfa her türlü kötü amaçlı içeriği kullanıcının panosuna sessizce kopyalayarak yapıştırıldığında feci sonuçlar doğurabilir. rm -rf / veya bir sıkıştırma bombası resmini panonuza sessizce kopyalayan bir web sayfası düşünün.

Kullanıcıdan pano izni isteyen tarayıcı istemi.
Clipboard API için izin istemi.

Web sayfalarının panoya sınırsız okuma erişimi vermek daha da can sıkıcı bir durumdur. Kullanıcılar, şifreler ve kişisel bilgiler gibi hassas bilgileri düzenli olarak panoya kopyalar. Bu bilgiler daha sonra kullanıcının bilgisi olmadan herhangi bir sayfa tarafından okunabilir.

Birçok yeni API'de olduğu gibi, Clipboard API de yalnızca HTTPS üzerinden sunulan sayfalar için desteklenir. Kötüye kullanımın önlenmesine yardımcı olmak amacıyla, pano erişimine yalnızca bir sayfa etkin sekmedeyken izin verilir. Etkin sekmelerdeki sayfalar izin istemeden panoya yazabilir, ancak panodan okuma her zaman izin gerektirir.

Kopyalama ve yapıştırma izinleri Permissions API'ye eklendi. clipboard-write izni, etkin sekmeye geldiklerinde sayfalara otomatik olarak verilir. clipboard-read izninin istenmesi gerekir. Bu izni, panodaki verileri okumaya çalışarak yapabilirsiniz. Aşağıdaki kod, sonuncuyu gösterir:

const queryOpts = { name: 'clipboard-read', allowWithoutGesture: false };
const permissionStatus = await navigator.permissions.query(queryOpts);
// Will be 'granted', 'denied' or 'prompt':
console.log(permissionStatus.state);

// Listen for changes to the permission state
permissionStatus.onchange = () => {
  console.log(permissionStatus.state);
};

Ayrıca, allowWithoutGesture seçeneğini kullanarak kesme veya yapıştırma işlevlerini çağırmak için bir kullanıcı hareketinin gerekip gerekmediğini de kontrol edebilirsiniz. Bu değerin varsayılan değeri tarayıcıya göre değiştiğinden her zaman eklemeniz gerekir.

Clipboard API'nin eşzamansız doğası tam da bu noktada fayda sağlar: Pano verilerini okumaya veya yazmaya çalışmak, henüz verilmemişse kullanıcıdan otomatik olarak izin ister. API söze dayalı olduğundan bu işlem tamamen şeffaftır ve kullanıcının pano iznini reddetmesine yol açması, sayfanın uygun şekilde yanıt verebilmesi için sözün reddedilmesine neden olur.

Tarayıcılar pano erişimine yalnızca bir sayfa etkin sekme olduğunda izin verdiğinden, buradaki örneklerden bazılarının doğrudan tarayıcının konsoluna yapıştırıldığında çalışmadığını görebilirsiniz. Bunun nedeni, geliştirici araçlarının kendileri etkin sekme olmasıdır. Bir püf noktası var: setTimeout() öğesini kullanarak pano erişimini erteleyin, ardından işlevler çağrılmadan önce odaklamak için sayfanın içini hızlıca tıklayın:

setTimeout(async () => {
  const text = await navigator.clipboard.readText();
  console.log(text);
}, 2000);

İzin politikası entegrasyonu

API'yi iframe'lerde kullanmak için çeşitli tarayıcı özelliklerini ve API'leri seçerek etkinleştirmeye ve devre dışı bırakmaya izin veren bir mekanizma tanımlayan İzin Politikası ile etkinleştirmeniz gerekir. Uygulamanızın ihtiyaçlarına bağlı olarak clipboard-read veya clipboard-write yöntemlerinden birini ya da ikisini birden tamamlamanız gerekir.

<iframe
    src="index.html"
    allow="clipboard-read; clipboard-write"
>
</iframe>

Özellik algılama

Async Clipboard API'yi tüm tarayıcıları desteklerken kullanmak için navigator.clipboard test edin ve önceki yöntemlere geri dönün. Örneğin, diğer tarayıcıları dahil etmek için yapıştırmayı aşağıdaki gibi uygulayabilirsiniz.

document.addEventListener('paste', async (e) => {
  e.preventDefault();
  let text;
  if (navigator.clipboard) {
    text = await navigator.clipboard.readText();
  }
  else {
    text = e.clipboardData.getData('text/plain');
  }
  console.log('Got pasted text: ', text);
});

Bütün hikaye bundan ibaret değildir. Async Clipboard API'den önce, web tarayıcılarında farklı kopyalama ve yapıştırma uygulamaları bulunuyordu. Çoğu tarayıcıda, tarayıcının kendi kopyalama ve yapıştırma işlemi document.execCommand('copy') ve document.execCommand('paste') kullanılarak tetiklenebilir. Kopyalanacak metin, DOM'da bulunmayan bir dizeyse DOM'ye eklenmesi ve seçilmesi gerekir:

button.addEventListener('click', (e) => {
  const input = document.createElement('input');
  input.style.display = 'none';
  document.body.appendChild(input);
  input.value = text;
  input.focus();
  input.select();
  const result = document.execCommand('copy');
  if (result === 'unsuccessful') {
    console.error('Failed to copy text.');
  }
  input.remove();
});

Demolar

Aşağıdaki demolarda Async Clipboard API ile oynayabilirsiniz. Glitch'te bunlarla denemeler yapmak için metin demosunu veya resim demosunu remiks olarak derleyebilirsiniz.

İlk örnekte metnin pano üzerinde ve dışında nasıl hareket ettiği gösterilmektedir.

API'yi görüntülerle denemek için bu demoyu kullanın. Sadece PNG'lerin desteklendiğini ve sadece birkaç tarayıcıda desteklendiğini unutmayın.

Teşekkür

Asenkron Clipboard API, Darwin Huang ve Gary Kačmarčík tarafından uygulanmıştır. Darwin de demoyu sunmuştur. Bu makalenin bazı bölümlerini incelediğiniz için Kyarik ve tekrar Gary Kačmarčík teşekkür ederiz.

Markus Winkler'ın Unsplash'teki lokomotif resmi.