टेक्स्ट और इमेज के लिए, क्लिपबोर्ड का सुरक्षित और बिना ब्लॉक किया गया ऐक्सेस
क्लिपबोर्ड के इंटरैक्शन के लिए, सिस्टम क्लिपबोर्ड का ऐक्सेस पाने का पारंपरिक तरीका,
document.execCommand()
था. काटने और चिपकाने के इस तरीके का इस्तेमाल, ज़्यादातर ब्राउज़र पर किया जा सकता है. हालांकि, इसकी कुछ सीमाएं भी हैं: क्लिपबोर्ड का ऐक्सेस सिंक किया जाता था और सिर्फ़ डीओएम को पढ़ा और उसमें लिखा जा सकता था.
टेक्स्ट के छोटे हिस्सों के लिए, यह ठीक है. हालांकि, कई मामलों में क्लिपबोर्ड पर टेक्स्ट ट्रांसफ़र करने के लिए पेज को ब्लॉक करना, खराब अनुभव देता है. कॉन्टेंट को सुरक्षित तरीके से चिपकाने से पहले, उसे साफ़ करने या इमेज को डिकोड करने में काफ़ी समय लग सकता है. ब्राउज़र को, चिपकाए गए दस्तावेज़ से लिंक किए गए रिसॉर्स को लोड या इनलाइन करना पड़ सकता है. इससे डिस्क या नेटवर्क के इंतज़ार के दौरान, पेज ब्लॉक हो जाएगा. अब कल्पना करें कि अनुमतियों को मिक्स में जोड़ने पर, ब्राउज़र को क्लिपबोर्ड का ऐक्सेस मांगते समय पेज को ब्लॉक करना पड़े. साथ ही, क्लिपबोर्ड इंटरैक्शन के लिए document.execCommand()
से जुड़ी अनुमतियों में बदलाव किया जाता है. साथ ही, अलग-अलग ब्राउज़र के लिए अलग-अलग होती हैं.
Async Clipboard API, इन समस्याओं को ठीक करता है. यह अनुमतियों का ऐसा मॉडल उपलब्ध कराता है जो पेज को ब्लॉक नहीं करता. Async Clipboard API, ज़्यादातर ब्राउज़र पर टेक्स्ट और इमेज को मैनेज करने के लिए ही उपलब्ध है. हालांकि, इसकी सुविधा अलग-अलग ब्राउज़र पर अलग-अलग होती है. नीचे दिए गए हर सेक्शन के लिए, ब्राउज़र के साथ काम करने की खास जानकारी को ध्यान से पढ़ें.
कॉपी करें: डेटा को क्लिपबोर्ड पर लिखना
writeText()
टेक्स्ट को क्लिपबोर्ड पर कॉपी करने के लिए, writeText()
बोलें. यह एपीआई असींक्रोनस है. इसलिए, writeText()
फ़ंक्शन एक प्रॉमिस दिखाता है. यह प्रॉमिस, पास किए गए टेक्स्ट को कॉपी किए जाने के आधार पर पूरा या अस्वीकार होता है:
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()
दरअसल, writeText()
सिर्फ़ सामान्य write()
तरीके की सुविधा देने वाला तरीका है, जो आपको क्लिपबोर्ड पर इमेज कॉपी करने की सुविधा भी देता है. writeText()
की तरह, यह एसिंक्रोनस है और प्रॉमिस रिटर्न करता है.
किसी इमेज को क्लिपबोर्ड पर सेव करने के लिए, आपके पास इमेज के तौर पर blob
होना चाहिए. ऐसा करने का एक तरीका यह है कि fetch()
का इस्तेमाल करके, सर्वर से इमेज का अनुरोध करें. इसके बाद, रिस्पॉन्स पर blob()
को कॉल करें.
कई वजहों से, सर्वर से इमेज का अनुरोध करना सही या मुमकिन नहीं हो सकता. हालांकि, इमेज को कैनवस पर भी बनाया जा सकता है और कैनवस के toBlob()
तरीके को भी इस्तेमाल किया जा सकता है.
इसके बाद, write()
वाले तरीके से ClipboardItem
ऑब्जेक्ट के कलेक्शन को पैरामीटर के तौर पर पास करें. फ़िलहाल, एक बार में सिर्फ़ एक इमेज पास की जा सकती है. हालांकि, हमें उम्मीद है कि आने वाले समय में हम एक से ज़्यादा इमेज के लिए भी यह सुविधा जोड़ पाएंगे. ClipboardItem
, इमेज के MIME टाइप को कुंजी और ब्लॉब को वैल्यू के तौर पर इस्तेमाल करता है. fetch()
या canvas.toBlob()
से मिले ब्लॉब ऑब्जेक्ट के लिए, blob.type
प्रॉपर्टी में इमेज का सही MIME टाइप अपने-आप शामिल हो जाता है.
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);
}
इसके अलावा, ClipboardItem
ऑब्जेक्ट के लिए एक प्रॉमिस लिखा जा सकता है.
इस पैटर्न के लिए, आपको पहले से डेटा के एमआईएमई टाइप की जानकारी होनी चाहिए.
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);
}
कॉपी इवेंट
अगर कोई उपयोगकर्ता क्लिपबोर्ड पर कॉपी करने की सुविधा का इस्तेमाल करता है और preventDefault()
को नहीं कॉल करता है, तो copy
इवेंट में clipboardData
प्रॉपर्टी शामिल होती है. इसमें पहले से ही सही फ़ॉर्मैट में आइटम होते हैं.
अगर आपको अपना लॉजिक लागू करना है, तो आपको preventDefault()
को कॉल करना होगा, ताकि आपके डिफ़ॉल्ट ऐक्शन को लागू किया जा सके.
इस मामले में, clipboardData
खाली होगा.
टेक्स्ट और इमेज वाले पेज पर, जब उपयोगकर्ता 'सभी चुनें' को चुनता है और क्लिपबोर्ड पर कॉपी करने की प्रोसेस शुरू करता है, तो आपके कस्टम समाधान को टेक्स्ट को खारिज करना चाहिए और सिर्फ़ इमेज को कॉपी करना चाहिए. ऐसा करने के लिए, नीचे दिए गए कोड सैंपल में दिखाए गए तरीके का इस्तेमाल करें.
इस उदाहरण में यह नहीं बताया गया है कि क्लिपबोर्ड एपीआई काम न करने पर,
पिछले एपीआई पर वापस कैसे जाएं.
<!-- 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
इवेंट के लिए:
ClipboardItem
के लिए:
चिपकाएं: क्लिपबोर्ड से डेटा पढ़ना
readText()
क्लिपबोर्ड से टेक्स्ट पढ़ने के लिए, navigator.clipboard.readText()
को कॉल करें और रिटर्न किए गए प्रॉमिस के हल होने का इंतज़ार करें:
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()
वाला तरीका भी एसिंक्रोनस (अलग-अलग समय पर) है और यह एक प्रोमिस दिखाता है. क्लिपबोर्ड पर मौजूद कोई इमेज पढ़ने के लिए, ClipboardItem
ऑब्जेक्ट की सूची पाएं. इसके बाद, उन पर फिर से कार्रवाई करें.
हर ClipboardItem
में अलग-अलग तरह के कॉन्टेंट हो सकते हैं. इसलिए, आपको for...of
लूप का फिर से इस्तेमाल करके, टाइप की सूची को दोहराना होगा. हर टाइप के लिए, getType()
तरीके को आर्ग्युमेंट के तौर पर मौजूदा टाइप के साथ कॉल करें, ताकि उससे जुड़ा ब्लॉब मिल सके. पहले की तरह, यह कोड इमेज से जुड़ा नहीं है. साथ ही, यह आने वाले समय में अन्य फ़ाइल टाइप के साथ भी काम करेगा.
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);
}
}
चिपकाई गई फ़ाइलों के साथ काम करना
यह उपयोगकर्ताओं के लिए काम का है, ताकि वे ctrl+c और ctrl+v जैसे क्लिपबोर्ड कीबोर्ड शॉर्टकट का इस्तेमाल कर सकें. Chromium, रीड-ओनली फ़ाइलों को क्लिपबोर्ड पर दिखाता है, जैसा कि नीचे बताया गया है. यह तब ट्रिगर होता है, जब उपयोगकर्ता ऑपरेटिंग सिस्टम के डिफ़ॉल्ट चिपकाने के शॉर्टकट पर क्लिक करता है या जब उपयोगकर्ता ब्राउज़र के मेन्यू बार में बदलाव करें के बाद चिपकाएं पर क्लिक करता है. किसी और प्लंबिंग कोड की ज़रूरत नहीं है.
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());
});
चिपकाने का इवेंट
जैसा कि पहले बताया गया है, क्लिपबोर्ड एपीआई के साथ काम करने के लिए इवेंट लॉन्च करने की योजना है. हालांकि, फ़िलहाल मौजूदा paste
इवेंट का इस्तेमाल किया जा सकता है. यह क्लिपबोर्ड पर मौजूद टेक्स्ट को पढ़ने के लिए, एक साथ काम करने वाले नए तरीकों के साथ अच्छी तरह से काम करता है. copy
इवेंट की तरह ही, preventDefault()
को कॉल करना न भूलें.
document.addEventListener('paste', async (e) => {
e.preventDefault();
const text = await navigator.clipboard.readText();
console.log('Pasted text: ', text);
});
एक से ज़्यादा MIME टाइप को हैंडल करना
ज़्यादातर लागू करने की सुविधाएं, एक बार काटने या कॉपी करने की कार्रवाई के लिए, क्लिपबोर्ड पर कई डेटा फ़ॉर्मैट डालती हैं. इसकी दो वजहें हैं: ऐप्लिकेशन डेवलपर के तौर पर, आपके पास यह जानने का कोई तरीका नहीं है कि उपयोगकर्ता टेक्स्ट या इमेज को किस ऐप्लिकेशन में कॉपी करना चाहता है. साथ ही, कई ऐप्लिकेशन में स्ट्रक्चर्ड डेटा को सादे टेक्स्ट के तौर पर चिपकाया जा सकता है. आम तौर पर, उपयोगकर्ताओं को यह विकल्प बदलाव करें मेन्यू आइटम के तौर पर दिखता है. इस मेन्यू आइटम का नाम चिपकाएं और स्टाइल मैच करें या फ़ॉर्मैट किए बिना चिपकाएं हो सकता है.
नीचे दिए गए उदाहरण में, ऐसा करने का तरीका बताया गया है. इस उदाहरण में, इमेज डेटा पाने के लिए fetch()
का इस्तेमाल किया गया है. हालांकि, यह डेटा <canvas>
या File System Access API से भी मिल सकता है.
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]);
}
सुरक्षा और अनुमतियां
क्लिपबोर्ड का ऐक्सेस, ब्राउज़र के लिए हमेशा सुरक्षा से जुड़ी समस्या रहा है. सही अनुमतियों के बिना, कोई पेज उपयोगकर्ता के क्लिपबोर्ड पर चुपचाप सभी तरह के नुकसान पहुंचाने वाले कॉन्टेंट को कॉपी कर सकता है. इस कॉन्टेंट को चिपकाने पर, खतरनाक नतीजे मिल सकते हैं.
मान लें कि कोई वेब पेज, आपके क्लिपबोर्ड पर rm -rf /
या डिकंप्रेसन बम इमेज को चुपचाप कॉपी करता है.
वेब पेजों को क्लिपबोर्ड का ऐक्सेस देने से भी ज़्यादा समस्याएं आ सकती हैं. उपयोगकर्ता, पासवर्ड और निजी जानकारी जैसी संवेदनशील जानकारी को नियमित रूप से क्लिपबोर्ड पर कॉपी करते हैं. इसके बाद, उपयोगकर्ता को बताए बिना कोई भी पेज उसे पढ़ सकता है.
कई नए एपीआई की तरह, क्लिपबोर्ड एपीआई सिर्फ़ उन पेजों के लिए काम करता है जो एचटीटीपीएस पर दिखाए जाते हैं. गलत इस्तेमाल को रोकने के लिए, क्लिपबोर्ड को सिर्फ़ तब ऐक्सेस करने की अनुमति दी जाती है, जब कोई पेज ऐक्टिव टैब हो. चालू टैब में मौजूद पेज, अनुमति का अनुरोध किए बिना क्लिपबोर्ड में लिख सकते हैं. हालांकि, क्लिपबोर्ड से पढ़ने के लिए हमेशा अनुमति लेनी पड़ती है.
कॉपी और चिपकाने की अनुमतियां, Permissions API में जोड़ी गई हैं.
जब पेज चालू टैब होते हैं, तब उन्हें clipboard-write
अनुमति अपने-आप मिल जाती है. clipboard-read
की अनुमति का अनुरोध करना ज़रूरी है. इसके लिए, क्लिपबोर्ड से डेटा पढ़ने की कोशिश करें. नीचे दिया गया कोड, बाद वाले विकल्प को दिखाता है:
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);
};
यह भी कंट्रोल किया जा सकता है कि allowWithoutGesture
विकल्प का इस्तेमाल करके, काटने या चिपकाने के लिए, उपयोगकर्ता के जेस्चर की ज़रूरत है या नहीं. इस वैल्यू की डिफ़ॉल्ट वैल्यू, ब्राउज़र के हिसाब से अलग-अलग होती है. इसलिए, आपको इसे हमेशा शामिल करना चाहिए.
यहां क्लिपबोर्ड एपीआई की असाइनोक्रोनस सुविधा का ज़्यादा फ़ायदा मिलता है: क्लिपबोर्ड का डेटा पढ़ने या उसमें डेटा लिखने की कोशिश करने पर, उपयोगकर्ता से अनुमति मांगी जाती है. ऐसा तब होता है, जब अनुमति पहले से नहीं दी गई हो. एपीआई प्रॉमिस-आधारित है, इसलिए यह पूरी तरह से पारदर्शी है. साथ ही, अगर कोई उपयोगकर्ता क्लिपबोर्ड की अनुमति को अस्वीकार करता है, तो वह प्रॉमिस अस्वीकार कर देता है, ताकि पेज सही तरीके से जवाब दे सके.
ब्राउज़र, क्लिपबोर्ड को सिर्फ़ तब ऐक्सेस करने की अनुमति देते हैं, जब कोई पेज चालू टैब हो. इसलिए, आपको यहां दिए गए कुछ उदाहरणों को सीधे ब्राउज़र के कंसोल में चिपकाने पर, वे काम नहीं करेंगे. इसकी वजह यह है कि डेवलपर टूल ही चालू टैब होते हैं. इसके लिए एक तरकीब है: setTimeout()
का इस्तेमाल करके क्लिपबोर्ड को ऐक्सेस करने की प्रक्रिया को रोकें. इसके बाद, फ़ंक्शन के कॉल होने से पहले, पेज पर तुरंत क्लिक करके उस पर फ़ोकस करें:
setTimeout(async () => {
const text = await navigator.clipboard.readText();
console.log(text);
}, 2000);
अनुमतियों की नीति का इंटिग्रेशन
iframe में एपीआई का इस्तेमाल करने के लिए, आपको इसे अनुमतियों से जुड़ी नीति की मदद से चालू करना होगा. इस नीति में एक ऐसा तरीका बताया गया है जिसकी मदद से, ब्राउज़र की अलग-अलग सुविधाओं और एपीआई को चुनिंदा तौर पर चालू और बंद किया जा सकता है. खास तौर पर, आपको अपने ऐप्लिकेशन की ज़रूरतों के हिसाब से, clipboard-read
या clipboard-write
में से किसी एक या दोनों को पास करना होगा.
<iframe
src="index.html"
allow="clipboard-read; clipboard-write"
>
</iframe>
फ़ीचर का पता लगाना
सभी ब्राउज़र के साथ काम करने वाले Async Clipboard API का इस्तेमाल करने के लिए, navigator.clipboard
की जांच करें और पुराने तरीकों का इस्तेमाल करें. उदाहरण के लिए, अन्य ब्राउज़र को शामिल करने के लिए, चिपकाने की सुविधा को लागू करने का तरीका यहां बताया गया है.
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);
});
यह पूरी कहानी नहीं है. Async Clipboard API के पहले, वेब ब्राउज़र में कॉपी करने और चिपकाने के अलग-अलग तरीके थे. ज़्यादातर ब्राउज़र में, document.execCommand('copy')
और document.execCommand('paste')
का इस्तेमाल करके, ब्राउज़र की खुद की कॉपी करें और चिपकाएं को ट्रिगर किया जा सकता है. अगर कॉपी किया जाने वाला टेक्स्ट, ऐसी स्ट्रिंग है जो DOM में मौजूद नहीं है, तो उसे DOM में इंजेक्ट किया जाना चाहिए और चुना जाना चाहिए:
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();
});
डेमो
नीचे दिए गए डेमो में, एसिंक्रोनस क्लिपबोर्ड एपीआई इस्तेमाल किया जा सकता है. Glitch पर एक्सपेरिमेंट करने के लिए, टेक्स्ट डेमो या इमेज डेमो को रीमिक्स किया जा सकता है.
पहले उदाहरण में, टेक्स्ट को क्लिपबोर्ड पर ले जाने और उससे हटाने का तरीका बताया गया है.
इमेज के साथ एपीआई को आज़माने के लिए, इस डेमो का इस्तेमाल करें. याद रखें कि सिर्फ़ PNG फ़ॉर्मैट के आइकॉन इस्तेमाल किए जा सकते हैं. साथ ही, ये कुछ ही ब्राउज़र में काम करते हैं.
इसी विषय से जुड़े कुछ लिंक
धन्यवाद
Async Clipboard API को Darwin Huang और Gary Kačmarčík ने लागू किया था. डार्विन ने भी डेमो दिया. इस लेख के कुछ हिस्सों की समीक्षा करने के लिए, क्यारिक और फिर गैरी कैकमर्किक को धन्यवाद.
Unsplash पर मार्कस विंकलर की हीरो इमेज.