在 execCommand 中“粘贴为纯文本”的 Javascript 技巧

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/12027137/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-23 06:56:25  来源:igfitidea点击:

Javascript trick for 'paste as plain text` in execCommand

javascripthtmldom-eventsexeccommand

提问by Googlebot

I have a basic editor based on execCommandfollowing the sample introduced here. There are three ways to paste text within the execCommandarea:

我有一个基于execCommand这里介绍的示例的基本编辑器。可通过三种方式在execCommand区域内粘贴文本:

  • Ctrl+V
  • Right Click -> Paste
  • Right Click -> Paste As Plain Text
  • Ctrl+V
  • 右键单击 -> 粘贴
  • 右键单击 -> 粘贴为纯文本

I want to allow pasting only plain text without any HTML markup. How can I force the first two actions to paste Plain Text?

我想只允许粘贴没有任何 HTML 标记的纯文本。如何强制前两个操作粘贴纯文本?

Possible Solution:The way I can think of is to set listener for keyup events for (Ctrl+V) and strip HTML tags before paste.

可能的解决方案:我能想到的方法是为 ( Ctrl+ V) 的keyup 事件设置侦听器并在粘贴之前去除 HTML 标签。

  1. Is it the best solution?
  2. Is it bulletproof to avoid any HTML markup in paste?
  3. How to add listener to Right Click -> Paste?
  1. 这是最好的解决方案吗?
  2. 避免粘贴任何 HTML 标记是否防弹?
  3. 如何将侦听器添加到右键单击-> 粘贴?

回答by pimvdb

It will intercept the pasteevent, cancel the paste, and manually insert the text representation of the clipboard:
http://jsfiddle.net/HBEzc/. This should be the most reliable:

它将拦截paste事件,取消paste,并手动插入剪贴板的文本表示:
http: //jsfiddle.net/HBEzc/。这应该是最可靠的:

  • It catches all kinds of pasting (Ctrl+V, context menu, etc.)
  • It allows you to get the clipboard data directly as text, so you don't have to do ugly hacks to replace HTML.
  • 它捕获各种粘贴(Ctrl+ V,上下文菜单等)
  • 它允许您直接以文本形式获取剪贴板数据,因此您不必进行丑陋的黑客攻击来替换 HTML。

I'm not sure of cross-browser support, though.

不过,我不确定是否支持跨浏览器。

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

回答by Jamie Barker

I couldn't get the accepted answer here to work in IE so I did some scouting around and came to this answer which works in IE11 and the latest versions of Chrome and Firefox.

我无法在这里获得可接受的答案以在 IE 中工作,因此我进行了一些侦察并得出了这个适用于 IE11 以及最新版本的 Chrome 和 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);
    }
});

回答by Adriano Galesso Alves

A close solution as pimvdb. But it's working of FF, Chrome and IE 9:

作为 pimvdb 的紧密解决方案。但它适用于 FF、Chrome 和 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);
    }   
});

回答by webprogrammer

Of course the question is already answered and the topic very old but I want to provide my solution as it is simple an clean:

当然,这个问题已经回答了,而且这个话题很老了,但我想提供我的解决方案,因为它很简单:

This is inside my paste-event on my contenteditable-div.

这是在我的 contenteditable-div 上的粘贴事件中。

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

The else-part is from another SO-post I couldn't find anymore...

其他部分来自另一个我找不到的SO帖子......



UPDATE 19.11.2014: The other SO-post

2014 年 11 月 19 日更新: 另一个 SO-post

回答by dpr

None of the posted answers really seems to work cross browser or the solution is over complicated:

发布的答案似乎都没有真正跨浏览器工作,或者解决方案过于复杂:

  • The command insertTextis not supported by IE
  • Using the pastecommand results in stack overflow error in IE11
  • insertTextIE 不支持该命令
  • paste在 IE11 中使用该命令导致堆栈溢出错误

What worked for me (IE11, Edge, Chrome and FF) is the following:

对我有用的(IE11、Edge、Chrome 和 FF)如下:

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

Note that the custom paste handler is only needed/working for contenteditablenodes. As both textareaand plain inputfields don't support pasting HTML content at all, so nothing needs to be done here.

请注意,自定义粘贴处理程序只需要/为contenteditable节点工作。由于字段textarea和普通input字段根本不支持粘贴 HTML 内容,因此此处无需执行任何操作。

回答by MidnightTortoise

Firefox does not allow you to access the clipboard data so you'll need to make a 'hack' to get it to work. I've not been able to find a complete solution, however you can fix it for ctrl+v pastes by creating a textarea & pasting to that instead:

Firefox 不允许您访问剪贴板数据,因此您需要进行“破解”才能使其工作。我无法找到完整的解决方案,但是您可以通过创建一个 textarea 并粘贴到该文本区域来修复它以用于 ctrl+v 粘贴:

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

回答by Albert

I was also working on a plain text paste and I started to hate all the execCommand and getData errors, so I decided to do it the classic way and it works like a charm:

我也在处理纯文本粘贴,我开始讨厌所有 execCommand 和 getData 错误,所以我决定用经典的方式来做,它就像一个魅力:

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

The code with my notations can be found here: http://www.albertmartin.de/blog/code.php/20/plain-text-paste-with-javascript

可以在这里找到带有我的符号的代码:http: //www.albertmartin.de/blog/code.php/20/plain-text-paste-with-javascript

回答by Nikhil Ghuse

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

Above code works for me in IE10 and IE11 and now also works in Chrome and Safari. Not tested in Firefox.

以上代码适用于 IE10 和 IE11,现在也适用于 Chrome 和 Safari。未在 Firefox 中测试。

回答by Lee Hojin

In IE11, execCommand doesn't work well. I use below code for IE11 <div class="wmd-input" id="wmd-input-md" contenteditable=true>is my div box.

在 IE11 中,execCommand 不能正常工作。我使用下面的 IE11 代码 <div class="wmd-input" id="wmd-input-md" contenteditable=true>是我的 div 框。

I read clipboard data from window.clipboardData and modify div's textContent and give caret.

我从 window.clipboardData 读取剪贴板数据并修改 div 的 textContent 并给出插入符号。

I give timeout for setting caret, because if I don't set timeout, a caret goes to end of div.

我给设置插入符号超时,因为如果我不设置超时,插入符号会转到 div 的末尾。

and you should read clipboardData in IE11 in below way. If you don't do it, newline caracter is not handled properly, so caret goes wrong.

并且您应该按照以下方式在 IE11 中读取 clipboardData。如果你不这样做,换行符处理不当,所以插入符号出错。

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

Tested on IE11 and chrome. It may not work on IE9

在 IE11 和 chrome 上测试。它可能不适用于 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);

回答by mooga

After along search and trying I have found somehow kind of optimal solution

经过搜索和尝试,我找到了某种最佳解决方案

what is important to keep in mind

重要的是要记住

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