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


106

根据execCommand下面介绍的示例,我有一个基本的编辑器。有三种方法可以在execCommand区域内粘贴文本:

  • Ctrl+V
  • 右键单击->粘贴
  • 右键单击->作为纯文本粘贴

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

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

  1. 这是最好的解决方案吗?
  2. 避免粘贴任何HTML标记是否安全?
  3. 如何将侦听器添加到右键单击->粘贴?

5
作为附带说明,您是否还想处理将文本拖到编辑器中的情况?这是HTML泄漏到编辑器中的另一种方式。
pimvdb

1
@pimvdb您的回答足以满足我的需要。只是出于好奇,有没有一种简单的方法来避免被拖走的泄漏?
Googlebot 2012年

2
我认为这可以完成工作:jsfiddle.net/HBEzc/2。但不幸的是,至少在Chrome上,文本总是插入到编辑器的开头。
pimvdb 2012年

您需要在此处使用剪贴板api作为说明。 youtube.com/watch?v=Q81HH2Od5oo
Johne Doe

Answers:


247

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

  • 它捕获各种粘贴(Ctrl+ V,上下文菜单等)
  • 它使您可以直接以文本形式获取剪贴板数据,因此不必进行难看的修改即可替换HTML。

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

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:我错过了明显的事情。如果text包含HTML(例如,如果您将HTML代码复制为纯文本),则它将实际将其粘贴为HTML。这是一个解决方案,但不是很漂亮:jsfiddle.net/HBEzc/3
pimvdb

14
var text = (event.originalEvent || event).clipboardData.getData('text/plain');提供了更多的跨浏览器兼容性
Duncan Walker

10
这破坏了撤消功能。(Ctrl + Z)
Rudey 2015年

2
很好的解决方案,但这与默认行为不同。如果用户复制类似的<div></div>内容,则将内容添加为contenteditable元素的子元素。我是这样修复的:document.execCommand("insertText", false, text);
Jason Newell

5
我发现insertHTML并且insertText不能在IE11中工作,但是document.execCommand('paste', false, text);可以正常工作。尽管那似乎在其他浏览器> _>中似乎不起作用。
Jamie Barker

39

我在这里无法获得在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);
    }
});

1
谢谢你,我在同一个问题上苦苦挣扎... insertText在IE11或最新FF上都
不起作用

1
在某些情况下,是否有可能在Firefox和Chrome中两次粘贴文本?在我看来..
时髦

1
@Fanky创建它时我没有这个问题,但是我不再在创建此代码的地方工作,所以我无法告诉您它是否仍然有效!您能否描述两次粘贴的方式?
Jamie Barker

2
@Fanky查看是否可以在此处重新创建它:jsfiddle.net/v2qbp829
Jamie Barker

2
现在看来,我遇到的问题是由于从脚本本身加载的文件中调用了脚本。我既不能粘贴到textarea中,也不能粘贴到FF 47.0.1中的提琴中(可以用chrome做到),但是可以粘贴到div contenteditable中,这对我来说很关键。谢谢!
时髦

21

类似于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);
    }   
});

5
我喜欢短路content变量分配。我发现使用getData('Text')跨浏览器的作品,因此您可以像这样进行一次分配:var content = ((e.originalEvent || e).clipboardData || window.clipboardData).getData('Text');然后,只需要对跨浏览器的粘贴/插入命令使用逻辑。
gfullam 2014年

6
我不认为您可以编写document.selection.createRange().pasteHTML(content)...只是在IE11上进行了测试,因此无法正常工作。
vsync

3
document.execCommand('insertText', false, content)从IE11和Edge开始不起作用。另外,最新版本的Chrome现在支持document.execCommand('paste', false, content),这更加简单。他们可能会过时insertText
杀人剂'17

19

当然,这个问题已经回答了,主题很老,但是我想提供解决方案,因为它很干净:

这是在我的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);
}

else部分来自另一个我找不到的SO-post ...


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


2
我认为您指的是这篇文章:stackoverflow.com/questions/21257688/…–
gfullam

1
在Safari中似乎没有为我工作。可能出了点问题
Cannicide '17

8

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

  • insertTextIE不支持该命令
  • 使用该paste命令会导致IE11中的堆栈溢出错误

适用于我(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>

注意,自定义粘贴处理程序仅对于contenteditable节点是必需的/起作用。由于textarea普通input字段都不支持粘贴HTML内容,因此此处无需执行任何操作。


我必须摆脱.originalEvent事件处理程序(第3行)的约束才能使其正常工作。因此完整行如下所示:const text = ev.clipboardData ? ev.clipboardData.getData('text/plain') : window.clipboardData.getData('Text');。适用于最新的Chrome,Safari,Firefox。
Pwdr

3

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

2

我还从事纯文本粘贴的工作,我开始讨厌所有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);
});

带有我符号的代码可以在这里找到: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();
    }
}

上面的代码在IE10和IE11中对我适用,现在在Chrome和Safari中也适用。未在Firefox中测试。


1

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

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

我给出设置插入符号的超时时间,因为如果不设置超时,则插入符号将进入div的结尾。

并且您应该通过以下方式在IE11中阅读剪贴板数据。如果不这样做,换行符将无法正确处理,因此插入符号会出错。

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

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

0

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

要记住什么是重要的

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

确定,因为每个人都在尝试处理剪贴板数据,检查按键事件并使用execCommand。

我想到了

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>

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.