जावास्क्रिप्ट को क्लिपबोर्ड डेटा पेस्ट इवेंट पर मिलता है (क्रॉस ब्राउज़र)


299

वेब एप्लिकेशन किसी पेस्ट इवेंट का पता कैसे लगा सकता है और डेटा को पेस्ट किया जा सकता है?

पाठ को एक समृद्ध पाठ संपादक में पेस्ट करने से पहले मैं HTML सामग्री को निकालना चाहूंगा।

बाद में चिपकाए जाने के बाद पाठ को साफ करना काम करता है, लेकिन समस्या यह है कि पिछले सभी स्वरूपण खो गए हैं। उदाहरण के लिए, मैं संपादक में एक वाक्य लिख सकता हूं और इसे बोल्ड कर सकता हूं, लेकिन जब मैं नया पाठ पेस्ट करता हूं, तो सभी स्वरूपण खो जाते हैं। मैं केवल उस पाठ को साफ करना चाहता हूं जिसे चिपकाया गया है, और किसी भी पिछले स्वरूपण को अछूता छोड़ दें।

आदर्श रूप से, समाधान को सभी आधुनिक ब्राउज़रों (जैसे, MSIE, गेको, क्रोम और सफारी) पर काम करना चाहिए।

ध्यान दें कि MSIE के पास है clipboardData.getData(), लेकिन मुझे अन्य ब्राउज़रों के लिए समान कार्यक्षमता नहीं मिली।


ये सभी उत्तर बताते हैं कि पाठ सामग्री कैसे प्राप्त करें। छवि सामग्री या फ़ाइल सामग्री प्राप्त करने के लिए बहुत अधिक कार्य की आवश्यकता होती है। शायद हम शीर्षक को बदल सकते हैं "जावास्क्रिप्ट प्राप्त स्वच्छता पाठ क्लिपबोर्ड डेटा ..."
1.21 गीगावाट

1
जैसे निको ने कहा: event.clipboardData.getData('Text')मेरे लिए काम किया।
आंद्रे एलिको 13

document.addEventListener('paste'...मेरे लिए काम किया है, लेकिन संघर्ष अगर एक उपयोगकर्ता पृष्ठ पर कहीं और पेस्ट करने में सक्षम होना चाहता था। फिर मैंने कोशिश की myCanvasElement.addEventListener('paste'..., लेकिन वह काम नहीं किया। आखिरकार मुझे लगा कि myCanvasElement.parentElement.addEventListener('paste'...काम किया है।
रयान

जवाबों:


149

इस उत्तर को लिखने के बाद से स्थिति बदल गई है: अब जब फ़ायरफ़ॉक्स ने संस्करण 22 में समर्थन जोड़ दिया है, सभी प्रमुख ब्राउज़र अब एक पेस्ट ईवेंट में क्लिपबोर्ड डेटा तक पहुंचने का समर्थन करते हैं। एक उदाहरण के लिए निको बर्न्स का जवाब देखें ।

अतीत में यह आमतौर पर क्रॉस-ब्राउज़र तरीके से संभव नहीं था। आदर्श pasteघटना के माध्यम से चिपकाई गई सामग्री प्राप्त करने में सक्षम होगा , जो हाल के ब्राउज़रों में संभव है लेकिन कुछ पुराने ब्राउज़रों में (विशेष रूप से, फ़ायरफ़ॉक्स <22) नहीं।

जब आपको पुराने ब्राउज़रों का समर्थन करने की आवश्यकता होती है, तो आप जो कर सकते हैं वह काफी शामिल होता है और थोड़ा सा हैक होता है जो फ़ायरफ़ॉक्स 2+, IE 5.5+ और वेबिट ब्राउज़र जैसे सफारी या क्रोम में काम करेगा। TinyMCE और CKEditor दोनों के हालिया संस्करण इस तकनीक का उपयोग करते हैं:

  1. Keypress Event हैंडलर का उपयोग करके ctrl-v / shift-ins इवेंट का पता लगाएं
  2. उस हैंडलर में, वर्तमान उपयोगकर्ता चयन को सेव करें, डॉक्यूमेंट में एक टेक्स्टएरिया एलिमेंट ऑफ-स्क्रीन (लेफ्ट -1000 पीएक्स पर) जोड़ें, टेक्सारिया को designModeबंद और कॉल करें focus(), इस तरह कैरेट को मूव करें और प्रभावी रूप से पेस्ट को रीडायरेक्ट करें।
  3. एक अन्य फ़ंक्शन को कॉल करने के लिए इवेंट हैंडलर में एक बहुत ही संक्षिप्त टाइमर (कहते हैं 1 मिलीसेकंड) सेट करें जो टेक्स्टारिया मान को संग्रहीत करता है, दस्तावेज़ से टेक्स्टारिया निकालता है, designModeवापस मुड़ता है, उपयोगकर्ता चयन को पुनर्स्थापित करता है और पाठ को पेस्ट करता है।

ध्यान दें कि यह केवल कीबोर्ड पेस्ट घटनाओं के लिए काम करेगा और संदर्भ या संपादन मेनू से चिपकेगा नहीं। जब तक पेस्ट इवेंट में आग लग जाती है, तब तक टेक्टारिया (कुछ ब्राउज़रों में, कम से कम) में कैरेट को रीडायरेक्ट करने में बहुत देर हो जाती है।

उस अप्रत्याशित घटना में, जिसे आपको फ़ायरफ़ॉक्स 2 का समर्थन करने की आवश्यकता है, ध्यान दें कि आपको उस ब्राउज़र में WYSIWYG संपादक iframe के दस्तावेज़ के बजाय मूल दस्तावेज़ में textarea को रखने की आवश्यकता होगी।


1
वाह, इसके लिए धन्यवाद! लगता है एक बहुत ही परिष्कृत हैक होने के लिए ;-) क्या आप कृपया उस डिजाइन का वर्णन कर सकते हैं और चयन की बात थोड़ी और, विशेष रूप से चरण 3 में? आपका बहुत बहुत धन्यवाद!
एलेक्स

5
मुझे एक भयानक अहसास था जो आप पूछेंगे। जैसा कि मैं कहता हूं, यह काफी शामिल है: मैं सुझाव दूंगा कि TinyMCE या CKEditor के स्रोत को देखना, क्योंकि मुझे इसमें शामिल सभी मुद्दों को रेखांकित करने का समय नहीं मिला है। हालांकि, संक्षेप में, designModeबूलियन की संपत्ति है documentऔर जब पूरे पृष्ठ को संपादन योग्य बनाता है true। WYSIWYG संपादक आमतौर designModeपर संपादन योग्य फलक के रूप में एक iframe का उपयोग करते हैं । उपयोगकर्ता चयन को सहेजना और पुनर्स्थापित करना IE में एक तरह से और अन्य ब्राउज़रों में किया जाता है, जैसा कि संपादक में सामग्री को चिपकाना है। आपको TextRangeIE में और Rangeअन्य ब्राउज़रों में प्राप्त करने की आवश्यकता है।
टिम डाउन

6
@ सैमुअल: आप इस pasteघटना का उपयोग करके इसका पता लगा सकते हैं लेकिन आमतौर पर तब तक पेस्ट को किसी अन्य तत्व में पुनर्निर्देशित करने में बहुत देर हो जाती है, इसलिए यह हैक काम करेगा। अधिकांश संपादकों में गिरावट उपयोगकर्ता को चिपकाने के लिए एक संवाद दिखाने की है।
टिम डाउन

6
इस पर कुछ और जानकारी: फ़ायरफ़ॉक्स आपको pasteघटना में किसी अन्य तत्व पर ध्यान केंद्रित करने की अनुमति नहीं देगा , हालांकि यह आपको तत्व की सामग्री को साफ़ करने की अनुमति देगा (और इसे एक चर में सहेज सकता है ताकि आप इसे बाद में पुनर्स्थापित कर सकें)। यदि यह कंटेनर एक है div(यह संभवतः iframeबहुत अधिक काम करता है) तो आप तब सामान्य डोम तरीकों का उपयोग करके पेस्ट की गई सामग्री के माध्यम से चक्र कर सकते हैं, या इसे स्ट्रिंग का उपयोग करके प्राप्त कर सकते हैं innerHTML। आप तब की पिछली सामग्री को पुनर्स्थापित कर सकते हैं div, और जो भी सामग्री आपको पसंद है उसे सम्मिलित कर सकते हैं। ओह, और आपको ऊपर के समान टाइमर हैक का उपयोग करना होगा। मुझे आश्चर्य है कि TinyMCE ऐसा नहीं करता है ...
निको बर्न्स

8
@ResistDesign: मैं असहमत हूँ - एक समझदार एपीआई की कमी के लिए यह एक असभ्य और जटिल तरीका है। पेस्ट की गई घटना से सीधे पेस्ट की गई सामग्री प्राप्त करने में सक्षम होना बेहतर होगा, जो कुछ ब्राउज़रों में सीमित तरीके से संभव है
टिम डाउन

318

समाधान # 1 (केवल सादा पाठ और फ़ायरफ़ॉक्स 22+ की आवश्यकता है)

IE6 +, FF 22+, Chrome, Safari, Edge के लिए काम करता है (केवल IE9 + में परीक्षण किया गया है, लेकिन निम्न संस्करणों के लिए काम करना चाहिए)

यदि आपको HTML या फ़ायरफ़ॉक्स <= 22 चिपकाने के लिए समर्थन की आवश्यकता है, तो समाधान # 2 देखें।

एचटीएमएल

<div id='editableDiv' contenteditable='true'>Paste</div>

जावास्क्रिप्ट

function handlePaste (e) {
    var clipboardData, pastedData;

    // Stop data actually being pasted into div
    e.stopPropagation();
    e.preventDefault();

    // Get pasted data via clipboard API
    clipboardData = e.clipboardData || window.clipboardData;
    pastedData = clipboardData.getData('Text');

    // Do whatever with pasteddata
    alert(pastedData);
}

document.getElementById('editableDiv').addEventListener('paste', handlePaste);

JSFiddle: https://jsfiddle.net/swL8ftLs/12/

ध्यान दें कि यह समाधान getDataफ़ंक्शन के लिए पैरामीटर 'टेक्स्ट' का उपयोग करता है , जो कि गैर-मानक है। हालाँकि, यह लेखन के समय सभी ब्राउज़रों में काम करता है।


समाधान # 2 (HTML और फ़ायरफ़ॉक्स के लिए काम करता है <= 22)

IE6 +, FF 3.5+, क्रोम, सफारी, एज में परीक्षण किया गया

एचटीएमएल

<div id='div' contenteditable='true'>Paste</div>

जावास्क्रिप्ट

var editableDiv = document.getElementById('editableDiv');

function handlepaste (e) {
    var types, pastedData, savedContent;

    // Browsers that support the 'text/html' type in the Clipboard API (Chrome, Firefox 22+)
    if (e && e.clipboardData && e.clipboardData.types && e.clipboardData.getData) {

        // Check for 'text/html' in types list. See abligh's answer below for deatils on
        // why the DOMStringList bit is needed. We cannot fall back to 'text/plain' as
        // Safari/Edge don't advertise HTML data even if it is available
        types = e.clipboardData.types;
        if (((types instanceof DOMStringList) && types.contains("text/html")) || (types.indexOf && types.indexOf('text/html') !== -1)) {

            // Extract data and pass it to callback
            pastedData = e.clipboardData.getData('text/html');
            processPaste(editableDiv, pastedData);

            // Stop the data from actually being pasted
            e.stopPropagation();
            e.preventDefault();
            return false;
        }
    }

    // Everything else: Move existing element contents to a DocumentFragment for safekeeping
    savedContent = document.createDocumentFragment();
    while(editableDiv.childNodes.length > 0) {
        savedContent.appendChild(editableDiv.childNodes[0]);
    }

    // Then wait for browser to paste content into it and cleanup
    waitForPastedData(editableDiv, savedContent);
    return true;
}

function waitForPastedData (elem, savedContent) {

    // If data has been processes by browser, process it
    if (elem.childNodes && elem.childNodes.length > 0) {

        // Retrieve pasted content via innerHTML
        // (Alternatively loop through elem.childNodes or elem.getElementsByTagName here)
        var pastedData = elem.innerHTML;

        // Restore saved content
        elem.innerHTML = "";
        elem.appendChild(savedContent);

        // Call callback
        processPaste(elem, pastedData);
    }

    // Else wait 20ms and try again
    else {
        setTimeout(function () {
            waitForPastedData(elem, savedContent)
        }, 20);
    }
}

function processPaste (elem, pastedData) {
    // Do whatever with gathered data;
    alert(pastedData);
    elem.focus();
}

// Modern browsers. Note: 3rd argument is required for Firefox <= 6
if (editableDiv.addEventListener) {
    editableDiv.addEventListener('paste', handlepaste, false);
}
// IE <= 8
else {
    editableDiv.attachEvent('onpaste', handlepaste);
}

JSFiddle: https://jsfiddle.net/nicoburns/wrqmuabo/23/

व्याख्या

onpasteकी घटना divहै handlePaste: समारोह इसे से जुड़ी है और एक भी तर्क पारित कर दिया eventपेस्ट घटना के लिए वस्तु। हमारे लिए विशेष रूप से रुचि clipboardDataइस घटना की संपत्ति है जो क्लिपबोर्ड को गैर-अर्थात ब्राउज़रों में उपयोग करने में सक्षम बनाती है। IE में समतुल्य है window.clipboardData, हालांकि इसमें थोड़ा अलग एपीआई है।

नीचे संसाधन अनुभाग देखें।


handlepasteसमारोह:

इस फ़ंक्शन की दो शाखाएँ हैं।

के अस्तित्व के लिए पहली जाँच करता है event.clipboardDataऔर जाँचता है कि क्या इसमें typesसंपत्ति 'पाठ / html' है ( typesया तो विधि DOMStringListका उपयोग करके जाँच की जा सकती है contains, या एक स्ट्रिंग जो indexOfविधि का उपयोग करके जाँच की जाती है )। यदि ये सभी शर्तें पूरी हो जाती हैं, तो हम '# पाठ / सादे' के बजाय 'टेक्स्ट / html' को छोड़कर, समाधान # 1 में आगे बढ़ते हैं। यह वर्तमान में Chrome और Firefox 22+ में काम करता है।

यदि यह विधि समर्थित नहीं है (सभी अन्य ब्राउज़र), तो हम

  1. तत्व की सामग्री को एक में सहेजें DocumentFragment
  2. तत्व को खाली करो
  3. waitForPastedDataफ़ंक्शन को कॉल करें

waitforpastedataसमारोह:

यह फ़ंक्शन पहले चिपकाया गया डेटा (एक बार प्रति 20ms) के लिए मतदान करता है, जो आवश्यक है क्योंकि यह सीधे दिखाई नहीं देता है। जब डेटा दिखाई दिया है:

  1. एक चर के लिए संपादन योग्य div (जो अब चिपकाया गया डेटा है) के आंतरिक HTML को बचाता है
  2. DocumentFragment में सहेजी गई सामग्री को पुनर्स्थापित करता है
  3. पुनर्प्राप्त डेटा के साथ 'processPaste' फ़ंक्शन को कॉल करता है

processpasteसमारोह:

चिपकाया डेटा के साथ मनमानी करता है। इस मामले में हम केवल डेटा को सचेत करते हैं, आप जो चाहें कर सकते हैं। आप शायद किसी तरह की डेटा सैनिटाइजिंग प्रक्रिया के माध्यम से पेस्ट किए गए डेटा को चलाना चाहेंगे।


कर्सर स्थिति को सहेजना और पुनर्स्थापित करना

एक वास्तविक स्थिति में, आप शायद चयन से पहले बचाना चाहते हैं, और बाद में इसे पुनर्स्थापित करना चाहते हैं ( सेट करें सामग्री स्थिति पर कर्सर <div> )। आप उस स्थिति में चिपकाए गए डेटा को डाल सकते हैं जब कर्सर उस स्थिति में था जब उपयोगकर्ता ने पेस्ट क्रिया शुरू की थी।

संसाधन:

किसी दस्तावेज़फ़्रेगमेंट के उपयोग का सुझाव देने के लिए टिम डाउन का धन्यवाद, और क्लिपबोर्ड के लिए एक स्ट्रिंग के बजाय DOMStringList के उपयोग के कारण फ़ायरफ़ॉक्स में एक त्रुटि को पकड़ने के लिए abligh।


4
दिलचस्प। मुझे लगा कि मैंने अतीत में यह कोशिश की थी और यह कुछ ब्राउज़र में काम नहीं किया था, लेकिन मुझे यकीन है कि आप सही हैं। मैं निश्चित रूप DocumentFragmentसे innerHTMLकई कारणों का उपयोग करने के बजाय मौजूदा सामग्री को स्थानांतरित करना पसंद करूंगा : पहले, आप किसी भी मौजूदा इवेंट हैंडलर को रखना; दूसरा, बचत और पुनर्स्थापन innerHTMLपिछले डोम की एक समान प्रतिलिपि बनाने के लिए गारंटी नहीं है; तीसरा, आप Rangeमार्कर तत्वों को जोड़ने या टेक्स्ट ऑफ़सेट्स की गणना करने (जो कि आपके द्वारा उपयोग किए जाने पर आपको करना होगा innerHTML) के बजाय फ़ेफ़ करने के बजाय चयन को बचा सकते हैं ।
टिम डाउन

3
वास्तव में कोई सामग्री (FONC?) का फ्लैश है, जो स्पष्ट रूप से बदतर होगा यदि चिपकाया गया सामग्री के प्रसंस्करण में कुछ समय लगता है। Btw, क्यों DocumentFragmentIE में एक दर्द के लिए निकाल रहा है? यह अन्य ब्राउज़रों की तरह ही है, जब तक कि आप एक रेंज का उपयोग न करें और extractContents()इसे करें, जो किसी भी मामले में विकल्प से अधिक संक्षिप्त नहीं है। मैंने आपकी तकनीक का एक उदाहरण लागू किया है, रंगी का उपयोग करके चीजों को ब्राउज़रों में अच्छा और समान रखने के लिए: jsfiddle.net/bQeWC-4
टिम डाउन

1
@ मार्टिन: jsFiddle डेमो मैं टिप्पणियों में तैनात मदद कर सकता है।
टिम डाउन

1
ऐसा लगता है कि यह विंडोज के लिए फ़ायरफ़ॉक्स 28 (कम से कम) पर अब काम नहीं करता है। यह कभी भी waitforpastedataफ़ंक्शन से बाहर नहीं जाता है
ओलीबोय 50

1
FYI करें: एज अब text/htmlW3C क्लिपबोर्ड एपीआई का उपयोग करके MIME-Type के साथ डेटा पढ़ने का समर्थन करता है। अतीत में इस तरह की कोशिश एक अपवाद फेंक देती थी। इसलिए अब किसी को भी एज के लिए इस वर्कअराउंड / हैक की जरूरत नहीं है।
जेनी ओ'रेली

130

सरल संस्करण:

document.querySelector('[contenteditable]').addEventListener('paste', (e) => {
    e.preventDefault();
    const text = (e.originalEvent || e).clipboardData.getData('text/plain');
    window.document.execCommand('insertText', false, text);
});

का उपयोग करते हुए clipboardData

डेमो: http://jsbin.com/nozifexasu/edit?js,output

एज, फ़ायरफ़ॉक्स, क्रोम, सफारी, ओपेरा का परीक्षण किया।

Document.execCommand () अब अप्रचलित है।


नोट: सर्वर-साइड पर भी इनपुट / आउटपुट की जांच करना याद रखें (जैसे PHP स्ट्रिप-टैग )


4
यह वास्तव में अच्छी तरह से काम करता है, लेकिन IE का कोई भी संस्करण घटना से क्लिपबोर्डडेटा तक पहुंच की अनुमति नहीं देता है :( महान समाधान, हालांकि, यह अधिक होना चाहिए!
एरिक वुड

1
ऐसा लगता है कि आप IE में क्लिपबोर्ड डेटा को एक अलग तरीके से प्राप्त कर सकते हैं, इसलिए यदि आप आईई का पता लगाते हैं तो आप उस डेटा का उपयोग शीघ्र पतन के
एंड्रयू

4
अब तक का सबसे अच्छा क्रॉस ब्राउज़र उत्तर मिला। बस IE और इसके सही के लिए कोड जोड़ें।
आर्टुरो

6
यह IE में काम करता है (आह, मीठा, विपरीत आईई)window.clipboardData.getData('Text');
बेंजीनर

9
e.preventDefault(); if (e.clipboardData) { content = (e.originalEvent || e).clipboardData.getData('text/plain'); document.execCommand('insertText', false, content); } else if (window.clipboardData) { content = window.clipboardData.getData('Text'); document.selection.createRange().pasteHTML(content); }
युकुलेक्स

26

लाइव डेमो

Chrome / FF / IE11 पर परीक्षण किया गया

एक क्रोम / IE झुंझलाहट है जो यह है कि ये ब्राउज़र <div>प्रत्येक नई लाइन के लिए तत्व जोड़ते हैं । यहाँ इसके बारे में एक पोस्ट है और इसे संतुष्ट करने वाले तत्व सेट करके तय किया जा सकता हैdisplay:inline-block

कुछ हाइलाइट किए गए HTML का चयन करें और इसे यहां पेस्ट करें:

function onPaste(e){
  var content;
  e.preventDefault();

  if( e.clipboardData ){
    content = e.clipboardData.getData('text/plain');
    document.execCommand('insertText', false, content);
    return false;
  }
  else if( window.clipboardData ){
    content = window.clipboardData.getData('Text');
    if (window.getSelection)
      window.getSelection().getRangeAt(0).insertNode( document.createTextNode(content) );
  }
}


/////// EVENT BINDING /////////
document.querySelector('[contenteditable]').addEventListener('paste', onPaste);
[contenteditable]{ 
  /* chroem bug: https://stackoverflow.com/a/24689420/104380 */
  display:inline-block;
  width: calc(100% - 40px);
  min-height:120px; 
  margin:10px;
  padding:10px;
  border:1px dashed green;
}

/* 
 mark HTML inside the "contenteditable"  
 (Shouldn't be any OFC!)'
*/
[contenteditable] *{
  background-color:red;
}
<div contenteditable></div>


1
मुझे सादे टेक्स्ट फीचर के रूप में एक पेस्ट की आवश्यकता थी। IE9 और IE10 पर परीक्षण किया गया और बढ़िया काम किया। उल्लेख करने की आवश्यकता है कि प्रमुख ब्राउज़रों पर भी काम करता है ... धन्यवाद।
सावास वेदोवा

2
आपके कोड में एक बग होता है: अगर (e.originalEvent.clipboardData) एक NPE पैदा कर सकता है क्योंकि आप नहीं जानते कि क्या e.originalEvent उस बिंदु पर मौजूद है
सेबस्टियन

15

मैंने ऑफ-स्क्रीन टेक्स्टारिया के साथ टिम डाउन्स प्रस्ताव के लिए अवधारणा का एक छोटा सा प्रमाण लिखा है। और यहाँ कोड जाता है:

<html>
<head>
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.4/jquery.min.js"></script> 
<script language="JavaScript">
 $(document).ready(function()
{

var ctrlDown = false;
var ctrlKey = 17, vKey = 86, cKey = 67;

$(document).keydown(function(e)
{
    if (e.keyCode == ctrlKey) ctrlDown = true;
}).keyup(function(e)
{
    if (e.keyCode == ctrlKey) ctrlDown = false;
});

$(".capture-paste").keydown(function(e)
{
    if (ctrlDown && (e.keyCode == vKey || e.keyCode == cKey)){
        $("#area").css("display","block");
        $("#area").focus();         
    }
});

$(".capture-paste").keyup(function(e)
{
    if (ctrlDown && (e.keyCode == vKey || e.keyCode == cKey)){                      
        $("#area").blur();
        //do your sanitation check or whatever stuff here
        $("#paste-output").text($("#area").val());
        $("#area").val("");
        $("#area").css("display","none");
    }
});

});
</script>

</head>
<body class="capture-paste">

<div id="paste-output"></div>


    <div>
    <textarea id="area" style="display: none; position: absolute; left: -99em;"></textarea>
    </div>

</body>
</html>

बस एक HTML फ़ाइल में पूरे कोड को कॉपी और पेस्ट करें और दस्तावेज़ पर कहीं भी क्लिपबोर्ड से पाठ (ctrl-v) का उपयोग करने का प्रयास करें।

मैंने इसे IE9 और फ़ायरफ़ॉक्स, क्रोम और ओपेरा के नए संस्करणों में परीक्षण किया है। काफी अच्छा काम करता है। इसके अलावा यह अच्छा है कि इस कार्यक्षमता को ट्रायगर करने के लिए वह जो भी महत्वपूर्ण संयोजन का उपयोग कर सकता है। बेशक jQuery के स्रोतों को शामिल करने के लिए मत भूलना।

इस कोड का उपयोग करने के लिए स्वतंत्र महसूस करें और यदि आप कुछ सुधार या समस्याओं के साथ आते हैं तो कृपया उन्हें वापस पोस्ट करें। यह भी ध्यान दें कि मैं कोई जावास्क्रिप्ट डेवलपर नहीं हूं, इसलिए मुझे कुछ याद नहीं होगा (=> अपना खुद का परीक्षण करें)।


मैक ctrl-v के साथ पेस्ट नहीं करते हैं, वे cmd-v का उपयोग करते हैं। तो ctrlKey = 91 को 17 के बजाय सेट करें
जेरेमी टी

2
या हो सकता है कि यह हमेशा 91 न हो: stackoverflow.com/questions/3834175/… भले ही, मुझे पूरा यकीन है कि jQuery आप के लिए सब कुछ संभालता है, बस e.ctrlKey या e.metaKey मुझे लगता है।
जेरेमी टी

3
e.ctrlKey या e.metaKey जावास्क्रिप्ट डोम का हिस्सा है, jQuery नहीं: developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent
rvighne

2
मुझे नहीं लगता कि यह राइट क्लिक और पेस्ट करने के लिए काम करता है। बहुत सारे लोग उस दृष्टिकोण को अपनाते हैं।
एरिक वुड

10

L2aelba anwser पर आधारित है । यह FF, Safari, Chrome, IE (8,9,10 और 11) पर परीक्षण किया गया था

    $("#editText").on("paste", function (e) {
        e.preventDefault();

        var text;
        var clp = (e.originalEvent || e).clipboardData;
        if (clp === undefined || clp === null) {
            text = window.clipboardData.getData("text") || "";
            if (text !== "") {
                if (window.getSelection) {
                    var newNode = document.createElement("span");
                    newNode.innerHTML = text;
                    window.getSelection().getRangeAt(0).insertNode(newNode);
                } else {
                    document.selection.createRange().pasteHTML(text);
                }
            }
        } else {
            text = clp.getData('text/plain') || "";
            if (text !== "") {
                document.execCommand('insertText', false, text);
            }
        }
    });

क्या IE को चिपकाते समय नई लाइनों को संरक्षित करने का एक तरीका है?
स्टे

10

यह कोई भी सेटटाइमआउट () का उपयोग नहीं करता है।

मैंने क्रॉस ब्राउज़र समर्थन प्राप्त करने के लिए इस महान लेख का उपयोग किया है।

$(document).on("focus", "input[type=text],textarea", function (e) {
    var t = e.target;
    if (!$(t).data("EventListenerSet")) {
        //get length of field before paste
        var keyup = function () {
            $(this).data("lastLength", $(this).val().length);
        };
        $(t).data("lastLength", $(t).val().length);
        //catch paste event
        var paste = function () {
            $(this).data("paste", 1);//Opera 11.11+
        };
        //process modified data, if paste occured
        var func = function () {
            if ($(this).data("paste")) {
                alert(this.value.substr($(this).data("lastLength")));
                $(this).data("paste", 0);
                this.value = this.value.substr(0, $(this).data("lastLength"));
                $(t).data("lastLength", $(t).val().length);
            }
        };
        if (window.addEventListener) {
            t.addEventListener('keyup', keyup, false);
            t.addEventListener('paste', paste, false);
            t.addEventListener('input', func, false);
        }
        else {//IE
            t.attachEvent('onkeyup', function () {
                keyup.call(t);
            });
            t.attachEvent('onpaste', function () {
                paste.call(t);
            });
            t.attachEvent('onpropertychange', function () {
                func.call(t);
            });
        }
        $(t).data("EventListenerSet", 1);
    }
}); 

यह कोड पेस्ट से पहले चयन हैंडल के साथ बढ़ाया जाता है: डेमो


+1 मुझे यह नीको बर्न्स से बेहतर लगता है, हालांकि मुझे लगता है कि प्रत्येक का अपना स्थान है।
n0nag0n

5

पेस्ट किए गए पाठ को साफ करने के लिए और वर्तमान में चुने गए पाठ को पेस्ट किए गए पाठ के साथ बदलने के लिए मामला बहुत मामूली है:

<div id='div' contenteditable='true' onpaste='handlepaste(this, event)'>Paste</div>

जे एस:

function handlepaste(el, e) {
  document.execCommand('insertText', false, e.clipboardData.getData('text/plain'));
  e.preventDefault();
}

क्या आप एक डेमो पेज प्रदान कर सकते हैं जहां यह काम करता है? मैंने इसे आजमाया है और यह काम नहीं करता है
vsync

5

यह उन सभी ब्राउज़रों पर काम करना चाहिए जो ऑनपेस्ट इवेंट और म्यूटेशन ऑब्जर्वर का समर्थन करते हैं।

यह समाधान केवल पाठ प्राप्त करने से परे एक कदम है, यह वास्तव में आपको चिपकाया जाता है इससे पहले कि यह एक तत्व में चिपकाया जाता है।

यह कंटेंटेबल, ऑनपेस्ट ईवेंट (सभी प्रमुख ब्राउज़रों द्वारा समर्थित) एन म्यूटेशन ऑब्जर्वर (क्रोम, फ़ायरफ़ॉक्स और IE11 + द्वारा समर्थित) का उपयोग करके काम करता है

चरण 1

संतुष्ट करने वाला HTML-element बनाएँ

<div contenteditable="true" id="target_paste_element"></div>

चरण 2

अपने जावास्क्रिप्ट कोड में निम्नलिखित घटना जोड़ें

document.getElementById("target_paste_element").addEventListener("paste", pasteEventVerifierEditor.bind(window, pasteCallBack), false);

हमें पेस्टकॉलबैक को बांधने की आवश्यकता है, क्योंकि उत्परिवर्तन पर्यवेक्षक को अतुल्यकालिक रूप से कहा जाएगा।

चरण 3

अपने कोड में निम्न फ़ंक्शन जोड़ें

function pasteEventVerifierEditor(callback, e)
{
   //is fired on a paste event. 
    //pastes content into another contenteditable div, mutation observer observes this, content get pasted, dom tree is copied and can be referenced through call back.
    //create temp div
    //save the caret position.
    savedCaret = saveSelection(document.getElementById("target_paste_element"));

    var tempDiv = document.createElement("div");
    tempDiv.id = "id_tempDiv_paste_editor";
    //tempDiv.style.display = "none";
    document.body.appendChild(tempDiv);
    tempDiv.contentEditable = "true";

    tempDiv.focus();

    //we have to wait for the change to occur.
    //attach a mutation observer
    if (window['MutationObserver'])
    {
        //this is new functionality
        //observer is present in firefox/chrome and IE11
        // select the target node
        // create an observer instance
        tempDiv.observer = new MutationObserver(pasteMutationObserver.bind(window, callback));
        // configuration of the observer:
        var config = { attributes: false, childList: true, characterData: true, subtree: true };

        // pass in the target node, as well as the observer options
        tempDiv.observer.observe(tempDiv, config);

    }   

}



function pasteMutationObserver(callback)
{

    document.getElementById("id_tempDiv_paste_editor").observer.disconnect();
    delete document.getElementById("id_tempDiv_paste_editor").observer;

    if (callback)
    {
        //return the copied dom tree to the supplied callback.
        //copy to avoid closures.
        callback.apply(document.getElementById("id_tempDiv_paste_editor").cloneNode(true));
    }
    document.body.removeChild(document.getElementById("id_tempDiv_paste_editor"));

}

function pasteCallBack()
{
    //paste the content into the element.
    restoreSelection(document.getElementById("target_paste_element"), savedCaret);
    delete savedCaret;

    pasteHtmlAtCaret(this.innerHTML, false, true);
}   


saveSelection = function(containerEl) {
if (containerEl == document.activeElement)
{
    var range = window.getSelection().getRangeAt(0);
    var preSelectionRange = range.cloneRange();
    preSelectionRange.selectNodeContents(containerEl);
    preSelectionRange.setEnd(range.startContainer, range.startOffset);
    var start = preSelectionRange.toString().length;

    return {
        start: start,
        end: start + range.toString().length
    };
}
};

restoreSelection = function(containerEl, savedSel) {
    containerEl.focus();
    var charIndex = 0, range = document.createRange();
    range.setStart(containerEl, 0);
    range.collapse(true);
    var nodeStack = [containerEl], node, foundStart = false, stop = false;

    while (!stop && (node = nodeStack.pop())) {
        if (node.nodeType == 3) {
            var nextCharIndex = charIndex + node.length;
            if (!foundStart && savedSel.start >= charIndex && savedSel.start <= nextCharIndex) {
                range.setStart(node, savedSel.start - charIndex);
                foundStart = true;
            }
            if (foundStart && savedSel.end >= charIndex && savedSel.end <= nextCharIndex) {
                range.setEnd(node, savedSel.end - charIndex);
                stop = true;
            }
            charIndex = nextCharIndex;
        } else {
            var i = node.childNodes.length;
            while (i--) {
                nodeStack.push(node.childNodes[i]);
            }
        }
    }

    var sel = window.getSelection();
    sel.removeAllRanges();
    sel.addRange(range);
}

function pasteHtmlAtCaret(html, returnInNode, selectPastedContent) {
//function written by Tim Down

var sel, range;
if (window.getSelection) {
    // IE9 and non-IE
    sel = window.getSelection();
    if (sel.getRangeAt && sel.rangeCount) {
        range = sel.getRangeAt(0);
        range.deleteContents();

        // Range.createContextualFragment() would be useful here but is
        // only relatively recently standardized and is not supported in
        // some browsers (IE9, for one)
        var el = document.createElement("div");
        el.innerHTML = html;
        var frag = document.createDocumentFragment(), node, lastNode;
        while ( (node = el.firstChild) ) {
            lastNode = frag.appendChild(node);
        }
        var firstNode = frag.firstChild;
        range.insertNode(frag);

        // Preserve the selection
        if (lastNode) {
            range = range.cloneRange();
            if (returnInNode)
            {
                range.setStart(lastNode, 0); //this part is edited, set caret inside pasted node.
            }
            else
            {
                range.setStartAfter(lastNode); 
            }
            if (selectPastedContent) {
                range.setStartBefore(firstNode);
            } else {
                range.collapse(true);
            }
            sel.removeAllRanges();
            sel.addRange(range);
        }
    }
} else if ( (sel = document.selection) && sel.type != "Control") {
    // IE < 9
    var originalRange = sel.createRange();
    originalRange.collapse(true);
    sel.createRange().pasteHTML(html);
    if (selectPastedContent) {
        range = sel.createRange();
        range.setEndPoint("StartToStart", originalRange);
        range.select();
    }
}
}

कोड क्या करता है:

  1. कोई ctrl-v, Referencemenu या अन्य साधनों का उपयोग करके पेस्ट ईवेंट को फायर करता है
  2. पेस्ट इवेंट में कंटेडटेबल के साथ एक नया एलीमेंट बनाया जाता है (कॉन्टेडटेबल के साथ एक एलीमेंट विशेषाधिकार को बढ़ा देता है)
  3. लक्ष्य तत्व की देखभाल की स्थिति बच गई है।
  4. फोकस नए तत्व पर सेट है
  5. सामग्री नए तत्व में चिपका दी जाती है और DOM में प्रदान की जाती है।
  6. म्यूटेशन ऑब्जर्वर इसे पकड़ता है (यह डोम ट्री और सामग्री में सभी परिवर्तनों को पंजीकृत करता है)। फिर उत्परिवर्तन घटना को निकालता है।
  7. चिपकाई गई सामग्री का डोम एक चर में क्लोन हो जाता है और कॉलबैक में वापस आ जाता है। अस्थायी तत्व नष्ट हो जाता है।
  8. कॉलबैक से क्लोन डोम प्राप्त होता है। देखभाल को बहाल किया जाता है। इसे अपने लक्ष्य पर जोड़ने से पहले आप इसे संपादित कर सकते हैं। तत्व। इस उदाहरण में मैं तत्व में कार्यवाहक को सहेजने और HTML को चिपकाने / सहेजने के लिए टिम डाउंस फ़ंक्शन का उपयोग कर रहा हूं।

उदाहरण


टिम डाउन के लिए बहुत धन्यवाद जवाब के लिए इस पोस्ट को देखें:

पेस्ट इवेंट पर दस्तावेज़ पर चिपकाई गई सामग्री प्राप्त करें


4

समाधान जो मेरे लिए काम करता है, घटना श्रोता को घटना को चिपकाने के लिए जोड़ रहा है यदि आप एक पाठ इनपुट पर चिपका रहे हैं। चूंकि पेस्ट परिवर्तन इनपुट परिवर्तन में पाठ से पहले होता है, मेरे अंदर पेस्ट हैंडलर के अंदर मैं एक आस्थगित फ़ंक्शन बनाता हूं जिसके अंदर मैं अपने इनपुट बॉक्स में बदलाव के लिए जांच करता हूं जो पेस्ट पर हुआ था:

onPaste: function() {
    var oThis = this;
    setTimeout(function() { // Defer until onPaste() is done
        console.log('paste', oThis.input.value);
        // Manipulate pasted input
    }, 1);
}

2
हॉरर, दुर्भाग्य से, हमारे नौकरी विवरण का एक हिस्सा है;) लेकिन मैं सहमत हूं, यह एक हैक है और हैक का उपयोग केवल तभी किया जाना चाहिए जब अन्य सभी विकल्प समाप्त हो जाते हैं।
लेक्स

4

यह निको के जवाब पर एक टिप्पणी के लिए बहुत लंबा था, जो मुझे नहीं लगता कि फ़ायरफ़ॉक्स पर काम करता है (टिप्पणियों के अनुसार), और सफारी पर मेरे लिए काम नहीं किया है।

सबसे पहले, आप अब क्लिपबोर्ड से सीधे पढ़ने में सक्षम होते हैं। कोड की बजाय:

if (/text\/plain/.test(e.clipboardData.types)) {
    // shouldn't this be writing to elem.value for text/plain anyway?
    elem.innerHTML = e.clipboardData.getData('text/plain');
}

उपयोग:

types = e.clipboardData.types;
if (((types instanceof DOMStringList) && types.contains("text/plain")) ||
    (/text\/plain/.test(types))) {
    // shouldn't this be writing to elem.value for text/plain anyway?
    elem.innerHTML = e.clipboardData.getData('text/plain');
}

क्योंकि फ़ायरफ़ॉक्स में एक typesफ़ील्ड है जो DOMStringListलागू नहीं होता है test

अगला फ़ायरफ़ॉक्स पेस्ट की अनुमति नहीं देगा जब तक कि फ़ोकस एक contenteditable=trueक्षेत्र में न हो।

अंत में, फ़ायरफ़ॉक्स पेस्ट को विश्वसनीय रूप से तब तक करने की अनुमति नहीं देगा जब तक कि फोकस एक textarea(या शायद इनपुट) में न हो जो न केवल है, contenteditable=trueबल्कि:

  • नहीं display:none
  • नहीं visibility:hidden
  • शून्य आकार नहीं

मैं पाठ क्षेत्र को छिपाने की कोशिश कर रहा था ताकि मैं जेएस वीएनसी एमुलेटर पर पेस्ट का काम कर सकूं (यानी यह एक दूरस्थ ग्राहक के लिए जा रहा था और वास्तव textareaमें पेस्ट करने के लिए कोई आदि नहीं था )। मैंने उपर्युक्त दिए गए लक्षणों में पाठ क्षेत्र को छिपाने की कोशिश की, जहां यह कभी-कभी काम करता है, लेकिन आमतौर पर दूसरे पेस्ट पर विफल रहा (या जब फ़ील्ड को दो बार एक ही डेटा को चिपकाने से रोकने के लिए मंजूरी दे दी गई थी) क्योंकि फ़ील्ड ने फ़ोकस खो दिया और ठीक से प्राप्त नहीं होगा। इसके बावजूद focus()। मैं जिस समाधान के साथ आया था, उसने इसे डाल दिया z-order: -1000, इसे बनाया display:none, इसे 1px से 1px के रूप में बनाया, और सभी रंगों को पारदर्शी बनाया। छी।

सफारी पर, आप ऊपर का दूसरा भाग लागू करते हैं, यानी आपको एक ऐसा होना चाहिए textareaजो नहीं है display:none


शायद डेवलपर्स जो ब्राउज़र रेंडरिंग इंजन पर काम करते हैं, उनके पास प्रलेखन साइटों पर एक पृष्ठ या स्थान होना चाहिए जिसका उपयोग वे उन विशेषताओं के बारे में नोट्स में लिखने के लिए कर सकते हैं जो वे काम करते हैं। उदाहरण के लिए, यदि वे पेस्ट फ़ंक्शन पर काम करते हैं, तो वे कहते हैं, "पेस्ट काम नहीं करेगा यदि डिस्प्ले कोई नहीं है, तो दृश्यता छिपी हुई है या आकार शून्य है"।
1.21 गीगावाट

3

सबसे पहले जो दिमाग में आता है वह है google के क्लोजर lib का पेस्टहैंडर http://closure-library.googlecode.com/svn/trunk/closure/goog/demos/pastehandler.html


यह एक सुरक्षित रूप से एक पेस्ट ईवेंट का पता लगाता है, लेकिन ऐसा लगता है कि यह पेस्ट की गई सामग्री को पकड़ने / वापस करने में सक्षम नहीं है?
एलेक्स

@ एलेक्स: आप सही हैं, और यह भी केवल टैक्स्टरेस के साथ काम करता है, न कि रिच टेक्स्ट एडिटर्स के लिए।
टिम डाउन

3

सरल समाधान:

document.onpaste = function(e) {
    var pasted = e.clipboardData.getData('Text');
    console.log(pasted)
}

2

यह मेरे लिए काम किया:

function onPasteMe(currentData, maxLen) {
    // validate max length of pasted text
    var totalCharacterCount = window.clipboardData.getData('Text').length;
}

<input type="text" onPaste="return onPasteMe(this, 50);" />

2
function myFunct( e ){
    e.preventDefault();

    var pastedText = undefined;
    if( window.clipboardData && window.clipboardData.getData ){
    pastedText = window.clipboardData.getData('Text');
} 
else if( e.clipboardData && e.clipboardData.getData ){
    pastedText = e.clipboardData.getData('text/plain');
}

//work with text

}
document.onpaste = myFunct;

1

आप इसे इस तरह से कर सकते हैं:

पूर्व और पोस्ट पेस्ट घटनाओं के लिए इस jQuery प्लगइन का उपयोग करें:

$.fn.pasteEvents = function( delay ) {
    if (delay == undefined) delay = 20;
    return $(this).each(function() {
        var $el = $(this);
        $el.on("paste", function() {
            $el.trigger("prepaste");
            setTimeout(function() { $el.trigger("postpaste"); }, delay);
        });
    });
};

अब आप इस प्लगइन का उपयोग कर सकते हैं ;:

$('#txt').on("prepaste", function() { 

    $(this).find("*").each(function(){

        var tmp=new Date.getTime();
        $(this).data("uid",tmp);
    });


}).pasteEvents();

$('#txt').on("postpaste", function() { 


  $(this).find("*").each(function(){

     if(!$(this).data("uid")){
        $(this).removeClass();
          $(this).removeAttr("style id");
      }
    });
}).pasteEvents();

explaination

पहले सभी मौजूदा तत्वों के लिए डेटा विशेषता के रूप में एक यूआईडी सेट करें।

फिर सभी नोड्स POST PASTE इवेंट की तुलना करें। इसलिए तुलना करके आप नए सम्मिलित लोगों की पहचान कर सकते हैं क्योंकि उनके पास एक यूआईडी होगा, फिर नए बनाए गए तत्वों से स्टाइल / क्लास / आईडी विशेषता को हटा दें, ताकि आप अपने पुराने स्वरूपण को रख सकें।



1

बस अपनी सामग्री संपादन योग्य div में हमेशा की तरह ब्राउज़र पेस्ट करें और फिर पेस्ट के बाद पाठ के साथ कस्टम पाठ शैलियों के लिए उपयोग किए गए किसी भी स्पैन तत्वों को स्वैप करें। यह इंटरनेट एक्सप्लोरर में ठीक काम करने के लिए लगता है और अन्य ब्राउज़र मैंने कोशिश की ...

$('[contenteditable]').on('paste', function (e) {
    setTimeout(function () {
        $(e.target).children('span').each(function () {
            $(this).replaceWith($(this).text());
        });
    }, 0);
});

यह समाधान मानता है कि आप jQuery चला रहे हैं और आप अपने किसी भी कंटेंट संपादन योग्य डिवेट्स में टेक्स्ट फॉर्मेटिंग नहीं चाहते हैं

प्लस साइड यह है कि यह सुपर सिंपल है।


spanटैग क्यों ? मुझे लगता है कि सवाल सभी टैग के बारे में था।
एलेक्सिस विलके

1

यह समाधान HTML टैग को प्रतिस्थापित करता है, यह सरल और क्रॉस-ब्राउज़र है; इस jsfiddle की जाँच करें: http://jsfiddle.net/tomwan/cbp1u2cx/1/ , मूल लेख:

var $plainText = $("#plainText");
var $linkOnly = $("#linkOnly");
var $html = $("#html");

$plainText.on('paste', function (e) {
    window.setTimeout(function () {
        $plainText.html(removeAllTags(replaceStyleAttr($plainText.html())));
    }, 0);
});

$linkOnly.on('paste', function (e) {
    window.setTimeout(function () {
        $linkOnly.html(removeTagsExcludeA(replaceStyleAttr($linkOnly.html())));
    }, 0);
});

function replaceStyleAttr (str) {
    return str.replace(/(<[\w\W]*?)(style)([\w\W]*?>)/g, function (a, b, c, d) {
        return b + 'style_replace' + d;
    });
}

function removeTagsExcludeA (str) {
    return str.replace(/<\/?((?!a)(\w+))\s*[\w\W]*?>/g, '');
}

function removeAllTags (str) {
    return str.replace(/<\/?(\w+)\s*[\w\W]*?>/g, '');
}

सूचना: आपको पीछे की तरफ xss फ़िल्टर के बारे में कुछ काम करना चाहिए क्योंकि यह घोल '<< >>' जैसे तारों को फ़िल्टर नहीं कर सकता है


सर्वर पर XSS फाइलिंग का इससे कोई लेना-देना नहीं है कि आपका जावास्क्रिप्ट फ़िल्टर अच्छा काम करता है या नहीं। हैकर वैसे भी आपके JS फ़िल्टरिंग का 100% बायपास करते हैं।
एलेक्सिस विल्के

HTML को पार्स / रूपांतरित करने के लिए रेगेक्स का उपयोग कभी न करें!
सबलीमेसिम

0

यह ऊपर पोस्ट किया गया एक मौजूदा कोड है, लेकिन मैंने इसे IE के लिए अपडेट किया है, बग तब था जब मौजूदा पाठ का चयन किया गया है और चिपकाया गया है चयनित सामग्री को नहीं हटाएगा। यह नीचे दिए गए कोड द्वारा तय किया गया है

selRange.deleteContents(); 

नीचे पूरा कोड देखें

$('[contenteditable]').on('paste', function (e) {
    e.preventDefault();

    if (window.clipboardData) {
        content = window.clipboardData.getData('Text');        
        if (window.getSelection) {
            var selObj = window.getSelection();
            var selRange = selObj.getRangeAt(0);
            selRange.deleteContents();                
            selRange.insertNode(document.createTextNode(content));
        }
    } else if (e.originalEvent.clipboardData) {
        content = (e.originalEvent || e).clipboardData.getData('text/plain');
        document.execCommand('insertText', false, content);
    }        
});
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.