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

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

Sistem panosuna erişmek için geleneksel olarak kullanılan yöntem document.execCommand() kullanabilirsiniz. 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.

Küçük boyutlu metinlerde sorun yoktur ancak sayfası kötü bir deneyim. 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ı yapıştırılan bir dokümandan bağlantılı kaynakları yüklemesi veya satır içine alması 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, uygulamalar konusundaki izinler Pano etkileşimi için document.execCommand() genel olarak tanımlanmıştır ve değişiklik gösterir yardımcı olabilir.

İlgili içeriği oluşturmak için kullanılan Eşzamansız Pano API'si Bu sorunlara çözüm sunar ve sayfayı engelleyebilirsiniz. Eş zamansız Clipboard API'si, metin ve resimleri işleme ile sınırlıdır çoğu tarayıcıda çalışır; ancak destek farklılık gösterir. Tarayıcınızı dikkatli bir şekilde inceleyin uyumluluk özetine göz atabilirsiniz.

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..
  • Kenar: 79..
  • Firefox: 63..
  • Safari: 13.1.

Kaynak

Write()

Aslında, writeText() yalnızca genel write() için kolaylık yöntemidir yöntemini kullanarak aynı zamanda resimleri panoya da kopyalayabilirsiniz. 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 istenmesi, çeşitli nedenleri vardır. 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 sunma. ClipboardItem, şununla bir nesneyi alıyor: resmin MIME türünü anahtar ve değer olarak blob'u seçin. Blob için fetch() veya canvas.toBlob(), blob.type özelliğinden alınan nesneler otomatik olarak bir resim için 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 taahhüt 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: 66..
  • Kenar: 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: ve 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..
  • Kenar: 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: Panodaki veriler okunuyor

readText()

Panodaki metni okumak için navigator.clipboard.readText() numaralı telefonu arayıp bekleyin şunların çözülmesi gerekiyor:

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 şunların listesini alın: ClipboardItem ve bu nesneler üzerinde yinelemeler yapın.

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 öğesini elde etmek için geçerli türe sahip getType() yöntemini bağımsız değişken olarak olması gerekir. Daha önce olduğu gibi, bu kod resimlere bağlı değildir ve gelecekteki diğer dosya türleriyle çalışın.

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: 66..
  • Kenar: 79..
  • Firefox: 127..
  • Safari: 13.1.

Kaynak

Yapıştırılan dosyalarla çalışılıyor

Kullanıcıların ctrl+c ve ctrl+v. Chromium, aşağıda açıklandığı gibi panodaki salt okunur dosyaları gösterir. Bu işlem, kullanıcı işletim sisteminin varsayılan yapıştırma kısayoluna bastığında tetiklenir veya kullanıcı tarayıcının menü çubuğunda Düzenle'yi, ardından Yapıştır'ı tıkladığında ya da vardır. 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..
  • Kenar: 12..
  • Firefox: 3.6..
  • Safari: 4..

Kaynak

Yapıştırma etkinliği

Daha önce de belirttiğimiz gibi, Clipboard API'si ile çalışmak için etkinlikler sunmayı planlıyoruz. ancak şimdilik mevcut paste etkinliğini kullanabilirsiniz. Yeni panodaki metni okumak için eşzamansız yöntemler. copy etkinliğinde olduğu gibi preventDefault() numarayı aramayı unut.

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 işlemi için birden fazla veri biçimini panoya yerleştirir veya kopyalama işlemi. Bunun iki nedeni vardır: Bir uygulama geliştirici olarak kullanıcının metin veya resimleri kopyalamak istediği uygulamanın özelliklerini bilmenin mümkün olmadığını unutmayın. Ayrıca birçok uygulama, yapılandırılmış verilerin düz metin olarak yapıştırılmasını destekler. Bu genelde kullanıcılara Yapıştır ve eşleşme stili veya Biçimlendirme olmadan yapıştır'ı seçin.

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 bir güvenlik sorunu 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 bir dosyayı sessizce kopyalayan bir web sayfası dekompresyon bombası resmi panonuza yapıştırın.

Tarayıcı istemi, kullanıcıdan pano izni istiyor.
Clipboard API için izin istemi.

Web sayfalarına panoya kesintisiz okuma erişimi vermek daha da iyidir zahmetli. Kullanıcılar şifre ve e-posta adresi gibi hassas bilgileri düzenli olarak kopyalar panoya kopyalayabilirsiniz; bunlar daha sonra herhangi bir sayfa gerektirmeden bilmek kadar harika bir his yok.

Birçok yeni API'de olduğu gibi Clipboard API yalnızca HTTPS'ye dokunun. Kötüye kullanımın önüne geçmek için pano erişimine yalnızca bir sayfa yalnızca etkin sekme. Etkin sekmelerdeki sayfalar panoya ancak panodan okuma işlemi için her zaman izni gerekir.

Kopyalama ve yapıştırma izinleri Permissions API. clipboard-write izni, izin verilen sayfalara otomatik olarak verilir. etkin sekme. clipboard-read izninin istenmesi gerekir. Bunu yapabilirsiniz. panodaki verileri okumaya çalışarak. 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, kesme veya kesmeyi çağırmak için bir kullanıcı hareketinin gerekip yapıştırırken allowWithoutGesture seçeneğini kullanabilirsiniz. Bu değer için varsayılan tarayıcıya göre değiştiğinden bunu her zaman dahil etmeniz 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 Önemli bir konu: erteleyin setTimeout() tuşlarını kullanarak pano erişimini kullanın, ardından sayfanın içini tıklayarak fonksiyonlar çağrılmadan önce odaklamasını sağlar:

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

Async Clipboard API'sini tüm tarayıcıları desteklerken kullanmak için navigator.clipboard ve önceki yöntemlere geri dönebilirsiniz. Ö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'sinden önce web tarayıcılarında farklı kopyalama ve yapıştırma uygulamaları. Çoğu tarayıcıda tarayıcının kendi kopyalama ve yapıştırma işlevi document.execCommand('copy') ve document.execCommand('paste'). 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. Darwin de demoyu sundu. 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'ın sunduğu hero resim Lansmanı kaldırın.