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);
}
}
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);
}
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:
ClipboardItem
için:
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);
}
}
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);
}
}
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());
});
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);
});
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.
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.
İlgili bağlantılar
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.