Thủ thuật Javascript cho 'dán dưới dạng văn bản thuần túy' trong lệnh executeCommand


106

Tôi có một trình soạn thảo cơ bản dựa trên việc làm execCommandtheo mẫu được giới thiệu ở đây. Có ba cách để dán văn bản trong execCommandkhu vực:

  • Ctrl+V
  • Nhấp chuột phải -> Dán
  • Nhấp chuột phải -> Dán dưới dạng văn bản thuần túy

Tôi muốn chỉ cho phép dán văn bản thuần túy mà không có bất kỳ đánh dấu HTML nào. Làm cách nào để buộc hai hành động đầu tiên dán Văn bản thuần túy?

Giải pháp khả thi: Cách tôi có thể nghĩ đến là đặt trình lắng nghe cho các sự kiện keyup cho ( Ctrl+ V) và tách các thẻ HTML trước khi dán.

  1. Nó có phải là giải pháp tốt nhất?
  2. Nó có chống đạn để tránh bất kỳ đánh dấu HTML nào trong quá trình dán không?
  3. Làm thế nào để thêm người nghe vào Nhấp chuột phải -> Dán?

5
Ngoài ra, bạn có muốn lưu ý đến việc văn bản bị kéo vào trình chỉnh sửa không? Đó là một cách khác mà HTML có thể bị rò rỉ vào trình soạn thảo.
pimvdb

1
@pimvdb Bạn trả lời là đủ cho nhu cầu của tôi. Chỉ vì tò mò, có một phương pháp đơn giản để tránh rò rỉ kéo quá không?
Googlebot

2
Tôi nghĩ điều này sẽ thực hiện công việc: jsfiddle.net/HBEzc/2 . Nhưng ít nhất trên Chrome, không may là văn bản luôn được chèn vào đầu trình chỉnh sửa.
pimvdb 19/08/12

Bạn cần sử dụng api khay nhớ tạm như phần giải thích ở đây. youtube.com/watch?v=Q81HH2Od5oo
Johne Doe

Câu trả lời:


247

Nó sẽ chặn pastesự kiện, hủy bỏ pastevà chèn thủ công biểu diễn văn bản của khay nhớ tạm:
http://jsfiddle.net/HBEzc/ . Điều này phải là đáng tin cậy nhất:

  • Nó bắt tất cả các loại dán ( Ctrl+ V, menu ngữ cảnh, v.v.)
  • Nó cho phép bạn lấy dữ liệu clipboard trực tiếp dưới dạng văn bản, vì vậy bạn không cần phải thực hiện các thao tác hack xấu xí để thay thế HTML.

Tuy nhiên, tôi không chắc về việc hỗ trợ nhiều trình duyệt.

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: Tôi đã bỏ lỡ một cái gì đó rõ ràng. Nếu textchứa HTML (ví dụ: nếu bạn sao chép mã HTML dưới dạng văn bản thuần túy) thì nó sẽ thực sự dán nó dưới dạng HTML. Đây là một giải pháp cho nó, nhưng nó không đẹp lắm: jsfiddle.net/HBEzc/3 .
pimvdb

14
var text = (event.originalEvent || event).clipboardData.getData('text/plain');cung cấp hơn một chút khả năng tương thích qua trình duyệt
Duncan Walker

10
Điều này phá vỡ chức năng hoàn tác. (Ctrl + Z)
Rudey

2
Giải pháp tuyệt vời, nhưng điều này khác với hành vi mặc định. Nếu người dùng sao chép <div></div>nội dung nào đó tương tự , nội dung đó sẽ được thêm vào dưới dạng phần tử con của phần tử có thể nội dung. Tôi đã sửa nó như thế này:document.execCommand("insertText", false, text);
Jason Newell

5
Tôi đã tìm thấy insertHTMLinsertTextkhông hoạt động trong IE11, tuy nhiên document.execCommand('paste', false, text);hoạt động tốt. Mặc dù điều đó sau đó dường như không hoạt động trong các trình duyệt khác> _>.
Jamie Barker

39

Tôi không thể nhận được câu trả lời được chấp nhận ở đây để hoạt động trong IE vì vậy tôi đã thực hiện một số tìm hiểu và tìm ra câu trả lời này hoạt động trong IE11 và các phiên bản mới nhất của Chrome và Firefox.

$('[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
Cảm ơn bạn, tôi đã truggling với vấn đề tương tự ... insertText đã không làm việc trên không phải IE11 hay FF mới nhất :)
HeberLZ

1
Có thể trong một số trường hợp, nó sẽ dán văn bản hai lần trong cả Firefox và Chrome? Có vẻ với tôi ..
Fanky

1
@Fanky Tôi không gặp sự cố đó khi tôi tạo nó, tuy nhiên tôi không còn làm việc tại nơi tôi tạo mã này nữa nên tôi không thể cho bạn biết liệu nó có còn hoạt động hay không! Bạn có thể mô tả cách nó dán hai lần?
Jamie Barker

2
@Fanky Xem bạn có thể tạo lại nó trên đây không: jsfiddle.net/v2qbp829 .
Jamie Barker

2
Có vẻ như vấn đề tôi gặp phải là do gọi tập lệnh của bạn từ một tệp mà chính nó đã được tải bởi một tập lệnh. Tôi không thể dán vào không gian văn bản cũng như đầu vào trong fiddle của bạn trong FF 47.0.1 (có thể làm điều đó trong chrome), nhưng có thể dán vào div contenteditable, đó là chìa khóa cho tôi. Cảm ơn!
Fanky

21

Một giải pháp gần như pimvdb. Nhưng nó hoạt động với FF, Chrome và IE 9:

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
Tôi thích việc contentgán biến ngắn mạch . Tôi nhận thấy rằng việc sử dụng getData('Text')hoạt động trên nhiều trình duyệt, vì vậy bạn có thể thực hiện chỉ định đó một lần như sau: var content = ((e.originalEvent || e).clipboardData || window.clipboardData).getData('Text');Sau đó, bạn chỉ phải sử dụng logic cho lệnh dán / chèn trình duyệt chéo.
gfullam

6
Tôi không nghĩ bạn có thể viết document.selection.createRange().pasteHTML(content)... chỉ mới thử nghiệm trên IE11 và nó không hoạt động như vậy.
vsync

3
document.execCommand('insertText', false, content)không hoạt động như IE11 và Edge. Ngoài ra, các phiên bản Chrome mới nhất hiện cũng hỗ trợ document.execCommand('paste', false, content), điều này đơn giản hơn. Họ có thể không dùng nữa insertText.
Cannicide

19

Tất nhiên câu hỏi đã được trả lời và chủ đề rất cũ nhưng tôi muốn cung cấp giải pháp của mình vì nó đơn giản là:

Đây là bên trong paste-event của tôi trên contenteditable-div của tôi.

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);
}

Phần khác là từ một bài đăng khác mà tôi không thể tìm thấy nữa ...


CẬP NHẬT 19.11.2014: SO-post khác


2
Tôi nghĩ bạn đang đề cập đến bài đăng này: stackoverflow.com/questions/21257688/…
gfullam,

1
Dường như không hiệu quả với tôi trong Safari. Có thể có điều gì đó không ổn
Cannicide

8

Không có câu trả lời đã đăng nào thực sự hoạt động trên nhiều trình duyệt hoặc giải pháp quá phức tạp:

  • Lệnh insertTextkhông được IE hỗ trợ
  • Sử dụng pastelệnh dẫn đến lỗi tràn ngăn xếp trong IE11

Những gì phù hợp với tôi (IE11, Edge, Chrome và FF) là như sau:

$("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>

Lưu ý rằng trình xử lý dán tùy chỉnh chỉ cần thiết / hoạt động cho contenteditablecác nút. Vì cả trường textareainputtrường thuần đều không hỗ trợ dán nội dung HTML, vì vậy không cần phải làm gì ở đây.


Tôi đã phải loại bỏ .originalEventtrong trình xử lý sự kiện (dòng 3) để điều này hoạt động. Vì vậy, cái nhìn dòng hoàn toàn như thế này: const text = ev.clipboardData ? ev.clipboardData.getData('text/plain') : window.clipboardData.getData('Text');. Hoạt động trên Chrome, Safari, Firefox mới nhất.
Pwdr

3

Firefox không cho phép bạn truy cập vào dữ liệu clipboard, vì vậy bạn sẽ cần thực hiện 'hack' để nó hoạt động. Tôi không thể tìm thấy giải pháp hoàn chỉnh, tuy nhiên, bạn có thể sửa nó cho ctrl + v pastes bằng cách tạo một vùng văn bản và dán vào đó:

//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

Tôi cũng đang làm việc trên một văn bản thuần túy dán và tôi bắt đầu ghét tất cả các lỗi ExitCommand và getData, vì vậy tôi quyết định thực hiện theo cách cổ điển và nó hoạt động như một sự quyến rũ:

$('#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);
});

Có thể tìm thấy mã với các ký hiệu của tôi tại đây: 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();
    }
}

Mã trên hoạt động với tôi trong IE10 và IE11 và bây giờ cũng hoạt động trong Chrome và Safari. Không được thử nghiệm trong Firefox.


1

Trong IE11, executeCommand không hoạt động tốt. Tôi sử dụng mã dưới đây cho IE11 <div class="wmd-input" id="wmd-input-md" contenteditable=true> là hộp div của tôi.

Tôi đọc dữ liệu clipboard từ window.clipboardData và sửa đổi textContent của div và đưa ra dấu mũ.

Tôi đưa ra thời gian chờ để đặt dấu mũ, bởi vì nếu tôi không đặt thời gian chờ, dấu mũ sẽ đi đến cuối div.

và bạn nên đọc clipboardData trong IE11 theo cách dưới đây. Nếu bạn không làm điều đó, ký tự mã vạch mới không được xử lý đúng cách, vì vậy dấu mũ sẽ bị sai.

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

Đã thử nghiệm trên IE11 và chrome. Nó có thể không hoạt động trên IE9

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

Sau khi cùng tìm kiếm và cố gắng, tôi đã tìm thấy bằng cách nào đó giải pháp tối ưu

điều quan trọng cần ghi nhớ

// /\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

OK vì mọi người đang cố gắng xử lý dữ liệu khay nhớ tạm, kiểm tra sự kiện nhấn phím và sử dụng lệnh executeCommand.

Tôi đã nghĩ về điều này

CODE

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>

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.