टेक्स्ट और इमेज के लिए, क्लिपबोर्ड को ज़्यादा सुरक्षित तरीके से ऐक्सेस करने की सुविधा
सिस्टम क्लिपबोर्ड का ऐक्सेस पाने का पारंपरिक तरीका, क्लिपबोर्ड इंटरैक्शन के लिए 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()
की तरह, यह एसिंक्रोनस है और Promise दिखाता है.
इमेज को क्लिपबोर्ड पर लिखने के लिए, आपके पास इमेज को blob
के तौर पर सेव किया गया होना चाहिए. इसके लिए, fetch()
का इस्तेमाल करके सर्वर से इमेज का अनुरोध किया जा सकता है. इसके बाद, जवाब पर blob()
को कॉल किया जा सकता है.
कई वजहों से, सर्वर से इमेज का अनुरोध करना सही नहीं हो सकता या ऐसा करना मुमकिन नहीं हो सकता. हालांकि, इमेज को कैनवस पर भी बनाया जा सकता है. इसके लिए, कैनवस के toBlob()
तरीके का इस्तेमाल करें.
इसके बाद, write()
तरीके में पैरामीटर के तौर पर, ClipboardItem
ऑब्जेक्ट का कलेक्शन पास करें. फ़िलहाल, एक बार में सिर्फ़ एक इमेज पास की जा सकती है. हालांकि, हमें उम्मीद है कि आने वाले समय में एक साथ कई इमेज पास करने की सुविधा उपलब्ध होगी. ClipboardItem
, एक ऑब्जेक्ट लेता है. इसमें इमेज का एमआईएमई टाइप, कुंजी के तौर पर और ब्लॉब, वैल्यू के तौर पर होता है. 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
ऑब्जेक्ट के लिए प्रॉमिस लिखा जा सकता है.
इस पैटर्न के लिए, आपको डेटा के MIME टाइप के बारे में पहले से पता होना चाहिए.
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
खाली होगा.
मान लें कि किसी पेज पर टेक्स्ट और इमेज मौजूद है. जब उपयोगकर्ता 'सभी चुनें' विकल्प चुनता है और क्लिपबोर्ड पर कॉपी करने की प्रोसेस शुरू करता है, तो आपके कस्टम समाधान को टेक्स्ट को हटा देना चाहिए और सिर्फ़ इमेज को कॉपी करना चाहिए. नीचे दिए गए कोड के उदाहरण में दिखाए गए तरीके से, ऐसा किया जा सकता है.
इस उदाहरण में यह नहीं बताया गया है कि Clipboard API के काम न करने पर, पुराने एपीआई पर कैसे वापस जाएं.
<!-- 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());
});
पेस्ट इवेंट
जैसा कि पहले बताया गया है, Clipboard API के साथ काम करने के लिए इवेंट लॉन्च करने की योजना है. हालांकि, फ़िलहाल मौजूदा 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 /
या डिकंप्रेशन बॉम्ब इमेज कॉपी करता है.

वेब पेजों को क्लिपबोर्ड की सामग्री पढ़ने का ऐक्सेस देने से और भी ज़्यादा समस्याएं हो सकती हैं. उपयोगकर्ता अक्सर पासवर्ड और निजी जानकारी जैसी संवेदनशील जानकारी को क्लिपबोर्ड पर कॉपी करते हैं. इसके बाद, कोई भी पेज इस जानकारी को उपयोगकर्ता की जानकारी के बिना पढ़ सकता है.
कई नए एपीआई की तरह, Clipboard API सिर्फ़ उन पेजों के लिए काम करता है जो HTTPS पर दिखाए जाते हैं. क्लिपबोर्ड के गलत इस्तेमाल को रोकने के लिए, क्लिपबोर्ड को सिर्फ़ तब ऐक्सेस किया जा सकता है, जब कोई पेज ऐक्टिव टैब हो. चालू टैब में मौजूद पेज, अनुमति मांगे बिना क्लिपबोर्ड में लिख सकते हैं. हालांकि, क्लिपबोर्ड से पढ़ने के लिए हमेशा अनुमति की ज़रूरत होती है.
कॉपी और चिपकाने की अनुमतियां, 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 में एपीआई का इस्तेमाल करने के लिए, आपको इसे Permissions Policy की मदद से चालू करना होगा. यह एक ऐसा तरीका है जिससे ब्राउज़र की अलग-अलग सुविधाओं और एपीआई को चुनिंदा तौर पर चालू और बंद किया जा सकता है. आपको अपने ऐप्लिकेशन की ज़रूरतों के हिसाब से, 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();
});
डेमो
नीचे दिए गए डेमो में, Async Clipboard API का इस्तेमाल किया जा सकता है. पहले उदाहरण में, टेक्स्ट को क्लिपबोर्ड पर ले जाने और वहां से हटाने का तरीका दिखाया गया है.
इमेज के साथ एपीआई आज़माने के लिए, इस डेमो का इस्तेमाल करें. याद रखें कि सिर्फ़ PNG फ़ाइलें इस्तेमाल की जा सकती हैं. साथ ही, इन्हें सिर्फ़ कुछ ब्राउज़र में इस्तेमाल किया जा सकता है.
इसी विषय से जुड़े कुछ लिंक
Acknowledgements
Asynchronous Clipboard API को Darwin Huang और Gary Kačmarčík ने लागू किया था. डार्विन ने भी डेमो दिया. इस लेख के कुछ हिस्सों की समीक्षा करने के लिए, Kyarik और गैरी काचमर्चिक का धन्यवाद.
Unsplash पर Markus Winkler की हीरो इमेज.