ExecCommand'da "düz metin olarak yapıştır" için Javascript numarası


108

execCommandBurada tanıtılan örneğe dayanan temel bir editörüm var. execCommandAlana metin yapıştırmanın üç yolu vardır :

  • Ctrl+V
  • Sağ Tık -> Yapıştır
  • Sağ Tık -> Düz Metin Olarak Yapıştır

Herhangi bir HTML işaretlemesi olmadan yalnızca düz metin yapıştırmaya izin vermek istiyorum. İlk iki eylemi Düz Metni yapıştırmaya nasıl zorlayabilirim?

Olası Çözüm: Düşünebildiğim yol, ( Ctrl+ V) için keyup olayları için dinleyici ayarlamak ve yapıştırmadan önce HTML etiketlerini çıkarmaktır .

  1. En iyi çözüm bu mu?
  2. Yapıştırmada herhangi bir HTML işaretlemesinden kaçınmak kurşun geçirmez mi?
  3. Sağ Tık -> Yapıştır'a nasıl dinleyici eklenir?

5
Bir yan not olarak, düzenleyiciye sürüklenen metne de dikkat etmek istiyor musunuz? Bu, HTML'nin editöre sızmasının başka bir yoludur.
pimvdb

1
@pimvdb İhtiyacım için cevabınız yeterli oldu. Sadece meraktan soruyorum, sürüklenen sızıntıyı önlemek için basit bir yöntem var mı?
Googlebot

2
Bunun işi yapacağını düşündüm: jsfiddle.net/HBEzc/2 . Ancak en azından Chrome'da metin maalesef her zaman editörün başına eklenir.
pimvdb

Burada açıklandığı gibi pano API'sini kullanmanız gerekir. youtube.com/watch?v=Q81HH2Od5oo
Johne Doe

Yanıtlar:


249

pasteOlayı kesecek , iptal edecek pasteve panonun metin gösterimini manuel olarak ekleyecektir:
http://jsfiddle.net/HBEzc/ . Bu en güvenilir olmalı:

  • Her türlü yapıştırmayı yakalar ( Ctrl+ V, bağlam menüsü vb.)
  • Pano verilerini doğrudan metin olarak almanıza izin verir, böylece HTML'yi değiştirmek için çirkin hackler yapmak zorunda kalmazsınız.

Yine de çapraz tarayıcı desteğinden emin değilim.

editor.addEventListener("paste", function(e) {
    // cancel paste
    e.preventDefault();

    // get text representation of clipboard
    var text = (e.originalEvent || e).clipboardData.getData('text/plain');

    // insert text manually
    document.execCommand("insertHTML", false, text);
});

4
@Ali: Bariz bir şeyi kaçırdım. Eğer textHTML içeriyor (örneğin düz metin olarak HTML kodunu kopyalayın varsa) o zaman aslında HTML olarak yapıştırın olacaktır. İşte buna bir çözüm, ancak pek hoş değil: jsfiddle.net/HBEzc/3 .
pimvdb

14
var text = (event.originalEvent || event).clipboardData.getData('text/plain');biraz daha fazla tarayıcılar arası uyumluluk sağlar
Duncan Walker

10
Bu, geri alma işlevini bozar. (Ctrl + Z)
Rudey

2
Harika bir çözüm, ancak bu, varsayılan davranıştan farklıdır. Kullanıcı böyle bir şeyi kopyalarsa <div></div>içerik, içerik düzenlenebilir öğenin alt öğesi olarak eklenecektir. Bunu şu şekilde düzelttim:document.execCommand("insertText", false, text);
Jason Newell

5
IE11'de buldum insertHTMLve insertTextçalışmıyorum, ancak document.execCommand('paste', false, text);gayet iyi çalışıyor. Bu, diğer tarayıcılarda> _> çalışmıyor gibi görünse de.
Jamie Barker

39

Burada kabul edilen cevabı IE'de çalışmak için alamadım, bu yüzden biraz keşif yaptım ve IE11 ile Chrome ve Firefox'un en son sürümlerinde çalışan bu cevaba geldim.

$('[contenteditable]').on('paste', function(e) {
    e.preventDefault();
    var text = '';
    if (e.clipboardData || e.originalEvent.clipboardData) {
      text = (e.originalEvent || e).clipboardData.getData('text/plain');
    } else if (window.clipboardData) {
      text = window.clipboardData.getData('Text');
    }
    if (document.queryCommandSupported('insertText')) {
      document.execCommand('insertText', false, text);
    } else {
      document.execCommand('paste', false, text);
    }
});

1
Teşekkür ederim, aynı sorunla uğraşıyordum ... insertText ne IE11'de ne de en son FF'de çalışmıyordu :)
HeberLZ

1
Bazı durumlarda metni hem Firefox hem de Chrome'da iki kez yapıştırması mümkün müdür? Bana görünüyor ..
Fanky

1
@Fanky Oluşturduğumda bu sorunu yaşamadım, ancak artık bu kodu oluşturduğum yerde çalışmıyorum, bu yüzden hala çalışıp çalışmadığını söyleyemedim! Nasıl yapıştırıldığını iki kez anlatabilir misin?
Jamie Barker

2
@Fanky Burada yeniden oluşturabilir misiniz bir bakın: jsfiddle.net/v2qbp829 .
Jamie Barker

2
Görünüşe göre, yaşadığım sorun, komut dosyanızı bir komut dosyası tarafından yüklenmiş bir dosyadan çağırmaktan kaynaklanıyor. FF 47.0.1'de ne metin alanına ne de kemanınıza giriş yapamıyorum (bunu kromda yapabilir), ancak benim için anahtar olan div contenteditable'a yapıştırabilirim. Teşekkürler!
Fanky

21

Pimvdb kadar yakın bir çözüm. Ancak FF, Chrome ve IE 9'da çalışıyor:

editor.addEventListener("paste", function(e) {
    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);
    }   
});

5
Kısa devre contentdeğişken atamasını seviyorum . getData('Text')Tarayıcılar arası çalışmayı kullandığımı buldum , böylece bu atamayı sadece bir kez yapabilirsiniz: var content = ((e.originalEvent || e).clipboardData || window.clipboardData).getData('Text');O zaman, tarayıcılar arası yapıştır / ekle komutu için sadece mantığı kullanmanız gerekir.
gfullam

6
document.selection.createRange().pasteHTML(content)Yazabileceğinizi sanmıyorum ... sadece IE11'de test edildi ve bu şekilde çalışmıyor.
vsync

3
document.execCommand('insertText', false, content)IE11 ve Edge'den itibaren çalışmıyor. Ayrıca, Chrome'un en son sürümleri artık desteklemektedir document.execCommand('paste', false, content)ki bu daha basittir. Kullanımdan kaldırıyor olabilirler insertText.
Cannicide

19

Elbette soru zaten cevaplanmış ve konu çok eski ama basit ve temiz olduğu için çözümümü sunmak istiyorum:

Bu benim contenteditable-div'imdeki past-olayımın içinde.

var text = '';
var that = $(this);

if (e.clipboardData)
    text = e.clipboardData.getData('text/plain');
else if (window.clipboardData)
    text = window.clipboardData.getData('Text');
else if (e.originalEvent.clipboardData)
    text = $('<div></div>').text(e.originalEvent.clipboardData.getData('text'));

if (document.queryCommandSupported('insertText')) {
    document.execCommand('insertHTML', false, $(text).html());
    return false;
}
else { // IE > 7
    that.find('*').each(function () {
         $(this).addClass('within');
    });

    setTimeout(function () {
          // nochmal alle durchlaufen
          that.find('*').each(function () {
               // wenn das element keine klasse 'within' hat, dann unwrap
               // http://api.jquery.com/unwrap/
               $(this).not('.within').contents().unwrap();
          });
    }, 1);
}

Diğer kısım, artık bulamadığım başka bir SO-postundan ...


GÜNCELLEME 19.11.2014: Diğer SO-postası


2
Sanırım bu
gönderiye

1
Safari'de benim için işe yaramadı. Belki bir sorun var
Cannicide

8

Gönderilen yanıtların hiçbiri gerçekten tarayıcılar arası çalışmıyor gibi görünmüyor veya çözüm aşırı karmaşık:

  • Komut insertText, IE tarafından desteklenmiyor
  • pasteKomutu kullanmak, IE11'de yığın taşması hatasına neden olur

Benim için işe yarayan şey (IE11, Edge, Chrome ve FF) şudur:

$("div[contenteditable=true]").off('paste').on('paste', function(e) {
    e.preventDefault();
    var text = e.originalEvent.clipboardData ? e.originalEvent.clipboardData.getData('text/plain') : window.clipboardData.getData('Text');
    _insertText(text);
});

function _insertText(text) { 
    // use insertText command if supported
    if (document.queryCommandSupported('insertText')) {
        document.execCommand('insertText', false, text);
    }
    // or insert the text content at the caret's current position
    // replacing eventually selected content
    else {
        var range = document.getSelection().getRangeAt(0);
        range.deleteContents();
        var textNode = document.createTextNode(text);
        range.insertNode(textNode);
        range.selectNodeContents(textNode);
        range.collapse(false);

        var selection = window.getSelection();
        selection.removeAllRanges();
        selection.addRange(range);
    }
};
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<body>
<textarea name="t1"></textarea>
<div style="border: 1px solid;" contenteditable="true">Edit me!</div>
<input />
</body>

Özel yapıştırma işleyicisinin yalnızca contenteditabledüğümler için gerekli / çalıştığını unutmayın . Hem textareadüz inputalanlar hem de düz alanlar HTML içeriğinin yapıştırılmasını desteklemediğinden, burada hiçbir şey yapılması gerekmez.


Bunun .originalEventişe yaraması için olay işleyicisinden (3. satır) kurtulmam gerekiyordu. Yani tam çizgi görünüyor bu gibi: const text = ev.clipboardData ? ev.clipboardData.getData('text/plain') : window.clipboardData.getData('Text');. En son Chrome, Safari, Firefox'ta çalışır.
Pwdr

3

Firefox, pano verilerine erişmenize izin vermez, bu nedenle çalışması için bir 'hack' yapmanız gerekir. Tam bir çözüm bulamadım, ancak bunun yerine bir metin alanı oluşturup yapıştırarak ctrl + v yapıştırmaları için düzeltebilirsiniz:

//Test if browser has the clipboard object
if (!window.Clipboard)
{
    /*Create a text area element to hold your pasted text
    Textarea is a good choice as it will make anything added to it in to plain text*/           
    var paster = document.createElement("textarea");
    //Hide the textarea
    paster.style.display = "none";              
    document.body.appendChild(paster);
    //Add a new keydown event tou your editor
    editor.addEventListener("keydown", function(e){

        function handlePaste()
        {
            //Get the text from the textarea
            var pastedText = paster.value;
            //Move the cursor back to the editor
            editor.focus();
            //Check that there is a value. FF throws an error for insertHTML with an empty string
            if (pastedText !== "") document.execCommand("insertHTML", false, pastedText);
            //Reset the textarea
            paster.value = "";
        }

        if (e.which === 86 && e.ctrlKey)
        {
            //ctrl+v => paste
            //Set the focus on your textarea
            paster.focus();
            //We need to wait a bit, otherwise FF will still try to paste in the editor => settimeout
            window.setTimeout(handlePaste, 1);
        }

    }, false);
}
else //Pretty much the answer given by pimvdb above
{
    //Add listener for paster to force paste-as-plain-text
    editor.addEventListener("paste", function(e){

        //Get the plain text from the clipboard
        var plain = (!!e.clipboardData)? e.clipboardData.getData("text/plain") : window.clipboardData.getData("Text");
            //Stop default paste action
        e.preventDefault();
        //Paste plain text
        document.execCommand("insertHTML", false, plain);

    }, false);
}

2

Ayrıca düz bir metin yapıştırma üzerinde çalışıyordum ve tüm execCommand ve getData hatalarından nefret etmeye başladım, bu yüzden bunu klasik şekilde yapmaya karar verdim ve harika çalışıyor:

$('#editor').bind('paste', function(){
    var before = document.getElementById('editor').innerHTML;
    setTimeout(function(){
        var after = document.getElementById('editor').innerHTML;
        var pos1 = -1;
        var pos2 = -1;
        for (var i=0; i<after.length; i++) {
            if (pos1 == -1 && before.substr(i, 1) != after.substr(i, 1)) pos1 = i;
            if (pos2 == -1 && before.substr(before.length-i-1, 1) != after.substr(after.length-i-1, 1)) pos2 = i;
        }
        var pasted = after.substr(pos1, after.length-pos2-pos1);
        var replace = pasted.replace(/<[^>]+>/g, '');
        var replaced = after.substr(0, pos1)+replace+after.substr(pos1+pasted.length);
        document.getElementById('editor').innerHTML = replaced;
    }, 100);
});

Notasyonlarımın olduğu kod burada bulunabilir: http://www.albertmartin.de/blog/code.php/20/plain-text-paste-with-javascript


1
function PasteString() {
    var editor = document.getElementById("TemplateSubPage");
    editor.focus();
  //  editor.select();
    document.execCommand('Paste');
}

function CopyString() {
    var input = document.getElementById("TemplateSubPage");
    input.focus();
   // input.select();
    document.execCommand('Copy');
    if (document.selection || document.textSelection) {
        document.selection.empty();
    } else if (window.getSelection) {
        window.getSelection().removeAllRanges();
    }
}

Yukarıdaki kod benim için IE10 ve IE11'de çalışıyor ve artık Chrome ve Safari'de de çalışıyor. Firefox'ta test edilmemiştir.


1

IE11'de execCommand pek iyi çalışmıyor. IE11 için aşağıdaki kodu kullanıyorum <div class="wmd-input" id="wmd-input-md" contenteditable=true> benim div kutum .

Pano verilerini window.clipboardData'dan okudum ve div'in textContent öğesini değiştirip düzeltme işareti veriyorum.

İmleci ayarlamak için zaman aşımı veriyorum, çünkü zaman aşımını ayarlamazsam, düzeltme işareti div'in sonuna gider.

ve aşağıdaki şekilde IE11'deki clipboardData'yı okumalısınız. Bunu yapmazsanız, satırsonu karakteri düzgün işlenmez, bu nedenle düzeltme yanlış olur.

var tempDiv = document.createElement("div");
tempDiv.textContent = window.clipboardData.getData("text");
var text = tempDiv.textContent;

IE11 ve chrome üzerinde test edilmiştir. IE9'da çalışmayabilir

document.getElementById("wmd-input-md").addEventListener("paste", function (e) {
    if (!e.clipboardData) {
        //For IE11
        e.preventDefault();
        e.stopPropagation();
        var tempDiv = document.createElement("div");
        tempDiv.textContent = window.clipboardData.getData("text");
        var text = tempDiv.textContent;
        var selection = document.getSelection();
        var start = selection.anchorOffset > selection.focusOffset ? selection.focusOffset : selection.anchorOffset;
        var end = selection.anchorOffset > selection.focusOffset ? selection.anchorOffset : selection.focusOffset;                    
        selection.removeAllRanges();

        setTimeout(function () {    
            $(".wmd-input").text($(".wmd-input").text().substring(0, start)
              + text
              + $(".wmd-input").text().substring(end));
            var range = document.createRange();
            range.setStart(document.getElementsByClassName("wmd-input")[0].firstChild, start + text.length);
            range.setEnd(document.getElementsByClassName("wmd-input")[0].firstChild, start + text.length);

            selection.addRange(range);
        }, 1);
    } else {                
        //For Chrome
        e.preventDefault();
        var text = e.clipboardData.getData("text");

        var selection = document.getSelection();
        var start = selection.anchorOffset > selection.focusOffset ? selection.focusOffset : selection.anchorOffset;
        var end = selection.anchorOffset > selection.focusOffset ? selection.anchorOffset : selection.focusOffset;

        $(this).text($(this).text().substring(0, start)
          + text
          + $(this).text().substring(end));

        var range = document.createRange();
        range.setStart($(this)[0].firstChild, start + text.length);
        range.setEnd($(this)[0].firstChild, start + text.length);
        selection.removeAllRanges();
        selection.addRange(range);
    }
}, false);

0

Aramadan ve denemekten sonra, bir şekilde en uygun çözümü buldum

akılda tutulması gereken önemli

// /\x0D/g return key ASCII
window.document.execCommand('insertHTML', false, text.replace('/\x0D/g', "\\n"))


and give the css style white-space: pre-line //for displaying

var contenteditable = document.querySelector('[contenteditable]')
            contenteditable.addEventListener('paste', function(e){
                let text = ''
                contenteditable.classList.remove('empty')                
                e.preventDefault()
                text = (e.originalEvent || e).clipboardData.getData('text/plain')
                e.clipboardData.setData('text/plain', '')                 
                window.document.execCommand('insertHTML', false, text.replace('/\x0D/g', "\\n"))// /\x0D/g return ASCII
        })
#input{
  width: 100%;
  height: 100px;
  border: 1px solid black;
  white-space: pre-line; 
}
<div id="input"contenteditable="true">
        <p>
        </p>
</div>   


0

Tamam, herkes pano verileri etrafında çalışmaya, tuşa basma olayını kontrol etmeye ve execCommand'ı kullanmaya çalışıyor.

Bunu düşündüm

KOD

handlePastEvent=()=>{
    document.querySelector("#new-task-content-1").addEventListener("paste",function(e)
    {
        
        setTimeout(function(){
            document.querySelector("#new-task-content-1").innerHTML=document.querySelector("#new-task-content-1").innerText.trim();
        },1);
    });

}
handlePastEvent();
<div contenteditable="true" id="new-task-content-1">You cann't paste HTML here</div>

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.