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() üzerinden gerçekleşiyordu. Bu yöntem geniş çapta desteklense de, yapıştırmanın bir maliyeti vardı: pano erişimi eşzamanlıydı ve yalnızca okuyabiliyordu DOM'ye yazmam gerekecek.

Bu, küçük metin parçaları için iyidir ancak sayfanın panoya aktarma için engellenmesi kötü bir deneyime neden olabilir. Zaman alan arındırma ya da İçeriğin güvenli bir şekilde yapıştırılabilmesi için resim kodunun çözülmesi gerekebilir. Tarayıcının, yapıştırılan bir belgedeki bağlantılı kaynakları yüklemesi veya satır içi olarak eklemesi gerekebilir. Bu, diskte veya ağda beklerken sayfayı engelleyebilir. Yeni bir web sitesi için Buna, istekte bulunurken tarayıcının sayfayı engellemesi gerekir. pano erişimi. Aynı zamanda, document.execCommand() etrafında panoya etkileşim için uygulanan izinler gevşek şekilde tanımlanır ve tarayıcılar arasında değişiklik gösterir.

İlgili içeriği oluşturmak için kullanılan Eşzamansız Pano API'si Bu sorunlara çözüm sunar ve sayfayı engelleyebilirsiniz. Async Clipboard API, çoğu tarayıcıda metin ve resimlerle işlem yapmakla 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ışı dikkatlice inceleyin.

Kopyala: panoya veri yazma

writeText()

Metni panoya kopyalamak için writeText() komutunu çağırın. Bu API, eşzamansızsa writeText() işlevi, isteği, geçirilen metnin başarılı bir şekilde kopyalanıp kopyalanmadığına bağlı olarak reddedilir:

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

  • Chrome: 66.
  • Edge: 79.
  • Firefox: 63.
  • Safari: 13.1.

Kaynak

Write()

Aslında writeText(), genel write() yöntemi için kolaylık sağlayan bir yöntemdir ve görüntüleri panoya kopyalamanıza da olanak tanır. writeText() gibi, eşzamansızdır ve bir Promise döndürür.

Panoya bir resim yazmak için resmin blob. Bunu yapmanın Bunun için fetch() kullanarak bir sunucudan resim isteyip blob() tıklayın.

Sunucudan resim istemek çeşitli nedenlerle istenmeyebilir veya mümkün olmayabilir. Neyse ki resmi tuvale çizebilir ve tuvali çağır toBlob() yöntemidir.

Sonra, write() öğesine parametre olarak bir ClipboardItem nesne dizisini iletin. yöntemidir. Şu anda tek seferde yalnızca bir resim iletebilirsiniz ancak gelecekte birden fazla resim desteği sunabilir. ClipboardItem, anahtar olarak resmin MIME türünü ve değer olarak blob'u içeren bir nesne alır. fetch() veya canvas.toBlob() kaynağından elde edilen blob nesneleri için blob.type mülkü, bir resim için doğru MIME türünü otomatik olarak 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 önceden verilerin MIME türünü 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);
}

Tarayıcı desteği

  • Chrome: 76.
  • Edge: 79.
  • Firefox: 127.
  • Safari: 13.1

Kaynak

Kopyalama etkinliği

Kullanıcının bir pano kopyası başlatması durumunda ve preventDefault() çağrılamazsa copy etkinlik , öğelerin zaten doğru biçimde olduğu bir clipboardData özelliği içerir. Kendi mantığınızı uygulamak istiyorsanız preventDefault() öğesini çağırmanız gerekir: kendi uygulamanızın lehine ilerlemesini önlemek için Bu durumda, clipboardData boş olur. Metin ve resim içeren bir sayfa düşünün; kullanıcı tüm seçili öğeleri bir pano kopyası başlatırsa özel çözümünüz metni silip yalnızca resmi kopyalayın. Bunu aşağıdaki kod örneğinde gösterildiği gibi yapabilirsiniz. Bu örnekte ele alınmayan, öncekine nasıl döneceğinizdir. Clipboard API'nin desteklenmediği API'ler.

<!-- 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

  • Chrome: 1.
  • Edge: 12.
  • Firefox: 22.
  • Safari: 3.

Kaynak

ClipboardItem için:

Tarayıcı desteği

  • Chrome: 76.
  • Kenar: 79.
  • Firefox: 127.
  • Safari: 13.1

Kaynak

Yapıştır: Verileri panodan okuma

readText()

Panoya alınan metni okumak için navigator.clipboard.readText() işlevini çağırın ve döndürülen promise işlevinin çözülmesini 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

  • Chrome: 66.
  • Kenar: 79.
  • Firefox: 125.
  • Safari: 13.1

Kaynak

Read()

navigator.clipboard.read() yöntemi de eşzamansızdır ve bir sözü. Panodaki bir resmi okumak için ClipboardItem nesnelerin listesini alın ve ardından bu nesneleri iteratif olarak inceleyin.

Her ClipboardItem, içeriğini farklı türlerde koruyabilir. Bu nedenle tür listesi üzerinde yine bir for...of döngüsü kullanarak yineleme gerçekleştirin. Her tür için, ilgili blob'u almak üzere getType() yöntemini geçerli türle birlikte bağımsız değişken olarak çağırın. Bu kod, daha önce olduğu gibi resimlere bağlı değildir ve gelecekte diğer dosya türleriyle de çalışacaktı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

  • Chrome: 76.
  • Kenar: 79.
  • Firefox: 127.
  • Safari: 13.1

Kaynak

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

Kullanıcıların ctrl+c ve ctrl+v. Chromium, aşağıda açıklandığı gibi panodaki salt okunur dosyaları gösterir. Bu kural, kullanıcı işletim sisteminin varsayılan yapıştırma kısayoluna bastığında veya tarayıcı menüsünde Düzenle'yi, ardından Yapıştır'ı tıkladığında tetiklenir. Başka bir 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

  • Chrome: 3.
  • Edge: 12.
  • Firefox: 3.6.
  • Safari: 4.

Kaynak

Yapıştırma etkinliği

Daha önce de belirtildiği gibi, Clipboard API ile çalışacak etkinliklerin kullanıma sunulması planlanmaktadır ancak şimdilik mevcut paste etkinliğini kullanabilirsiniz. Bu yöntem, pano metnini okumak için yeni asenkron yöntemlerle iyi çalışır. copy etkinliğinde olduğu gibi preventDefault()'ı aramayı unutmayın.

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

Tarayıcı Desteği

  • Chrome: 1.
  • Kenar: 12.
  • Firefox: 22.
  • Safari: 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 bilemezsiniz ve birçok uygulama, yapılandırılmış verileri düz metin olarak yapıştırmayı destekler. Bu seçenek genellikle kullanıcılara Yapıştır ve stili eşleştir veya Biçimlendirme olmadan yapıştır gibi bir ada sahip bir Düzenle menüsü öğesiyle sunulur.

Aşağıdaki örnekte bunun nasıl yapılacağı gösterilmektedir. Bu örnekte, şu verileri almak için fetch() kullanılmıştır: fakat bu veriler farklı bir dilden de <canvas> veya File System Access API'yi kullanabilirsiniz.

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 güvenlik endişesi oluşturmuştur. Yok: bir sayfa, izin verilmeyen her tür kötü amaçlı içeriği sessizce kullanıcının panosuna yapıştırır. rm -rf / veya 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ına panoya kesintisiz okuma erişimi vermek daha da iyidir zahmetli. Kullanıcılar, şifreler ve kişisel bilgiler gibi hassas bilgileri rutin olarak panosuna 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 yalnızca HTTPS'ye dokunun. Kötüye kullanımı önlemek için pano erişimine yalnızca bir sayfa etkin sekme olduğunda izin verilir. Etkin sekmelerdeki sayfalar, izin istemeden panoya yazabilir ancak panodan okumak için her zaman izin gerekir.

Kopyalama ve yapıştırma izinleri Permissions API'ye eklendi. clipboard-write izni, etkin sekme olduklarında sayfalara otomatik olarak verilir. clipboard-read izninin istenmesi gerekir. Bunu, panodaki verileri okumayı deneyerek yapabilirsiniz. Aşağıdaki kod ikincisini 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şlemini başlatmak için kullanıcı hareketi gerekip gerekmediğini de kontrol edebilirsiniz. Bu değerin varsayılan değeri tarayıcıya göre değişir. Bu nedenle, bu değeri her zaman eklemeniz gerekir.

Clipboard API'sinin eşzamansız doğası işte tam olarak burada işe yarar: pano verilerini okumaya veya yazmaya çalışırken kullanıcıdan otomatik olarak izin verilmemesini sağlar. API vaat temelli olduğundan bu tamamen şeffaftır ve bir kullanıcının pano iznini reddetmesi yanıt verebilmesi için reddetme taahhüdü vermelidir.

Tarayıcılar pano erişimine yalnızca sayfa etkin sekme olduğunda izin verdiği için buradaki örneklerden bazılarının doğrudan geliştirici araçlarının kendisi etkin sekme olduğu için tarayıcının konsolunda Bunun için setTimeout() kullanarak panosuna erişimi erteleyin, ardından işlevler çağrılmadan önce sayfanın üzerine gelip sayfayı 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 API'yi İzin Politikası, Bu aşama, kullanıcının seçmeli şekilde etkinleştirmesine ve çeşitli tarayıcı özelliklerini ve API'leri devre dışı bırakmak. Somut olarak, karşınızdaki paydaşların Uygulamanızın ihtiyaçlarına bağlı olarak clipboard-read veya clipboard-write.

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

Özellik algılama

Tüm tarayıcıları desteklerken Async Clipboard API'yi kullanmak için navigator.clipboard olup olmadığını test edin ve önceki yöntemlere geçin. Örneğin, aşağıdaki gibi diğer tarayıcıları içerecek şekilde yapıştırmayı 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);
});

Hikayenin tamamı bundan ibaret değil. Async Clipboard API'den önce, web tarayıcılarında farklı kopyalama ve yapıştırma uygulamaları kullanılıyordu. Çoğu tarayıcıda document.execCommand('copy') ve document.execCommand('paste') kullanılarak tarayıcının kendi kopyalama ve yapıştırma işlevi tetiklenebilir. Metin DOM'de bulunmayan bir dizeyse, bu dizenin DOM ve seçilenler:

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'sini kullanabilirsiniz. Glitch you'de metin demosunu remiksleyebilir veya resim demosunu izleyin. bunlarla denemeler yapabilirsiniz.

İlk örnekte metnin pano üzerinde ve dışına taşınması gösterilmektedir.

API'yi resimlerle denemek için bu demoyu kullanın. Yalnızca PNG'lerin desteklendiğini unutmayın. ve yalnızca şurada: birkaç tarayıcıda.

Teşekkür

Eşzamansız Clipboard API, Darwin Huang ve Gary Kačmarčík ile de uyumlu. Demoyu da Darwin sağladı. Bu katkı için Kyarik ve tekrar Gary Kačmarčík'e teşekkür ederiz. bu makalenin bölümlerini gözden geçirin.

Markus Winkler'in Unsplash'taki lokomotif resmi.