如何在contentEditable范围内制作纯文本粘贴而不破坏撤消?

时间:2021-12-31 08:42:03

Oddly specific question, but I have a solution already to paste plain text in a <span contentEditable="true"> by using a hidden textarea, which seems to work really well, except that it breaks the browser's undo feature. Right off the bat I'm not worried about a cross-browser solution; I only care about Chrome. My approach looks roughly like this:

奇怪的具体问题,但我已经有一个解决方案已经通过使用隐藏的textarea将纯文本粘贴到中,这似乎工作得非常好,除了它打破了浏览器的撤消功能。马上,我并不担心跨浏览器的解决方案;我只关心Chrome。我的方法大致如下:

$('.editable').live('paste', function()
{
    var $this = $(this);

    //more code here to remember caret position, etc

    $('#clipboard').val('').focus(); //put the focus in the hidden textarea so that, when the paste actually occurs, it's auto-sanitized by the textarea

    setTimeout(function() //then this will be executed immediately after the paste actually occurs
    {
        $this.focus();
        document.execCommand('insertHTML', true, $('#clipboard').val());
    });
});

So this works -- I can paste anything and it's reduced to plain text before going into the my contentEditable field -- but if I try to undo after pasting:

所以这样做 - 我可以粘贴任何内容,并且在进入我的contentEditable字段之前将其缩小为纯文本 - 但是如果我在粘贴后尝试撤消:

  • First undo undoes the paste.
  • 首先撤消撤消粘贴。
  • Second undo tries to undo the changes to #clipboard, moving the focus away from my contentEditable.
  • 第二次撤消尝试撤消对#clipboard的更改,将焦点从我的contentEditable移开。

I've tried everything I can think of to make the browser not try to undo the changes to #clipboard -- toggling display:none when it's not actively in use, toggling readonly and disabled state, destroying it at the end of and recreating it at the beginning of the event above, various other hacks -- but nothing seems to work.

我已经尝试了所有我能想到的东西,让浏览器不要尝试撤消对#clipboard的更改 - 切换display:none,当它没有主动使用时,切换readonly和disabled状态,在结束时销毁它并重新创建它在上面的事件开始时,各种其他黑客 - 但似乎没有任何工作。

Is this a terrible approach to sanitization? This is the first thing I've managed to really get working -- trying to clean up the markup after the paste occurs didn't work, as there are some things (entire HTML documents) which, when pasted, crash the browser, which I'd like to avoid.

这是一种可怕的消毒方法吗?这是我设法真正开始工作的第一件事 - 在粘贴发生后尝试清理标记不起作用,因为有些东西(整个HTML文档)在粘贴时会崩溃浏览器,我想避免。

Is there any way to make the #clipboard not undoable, or any other suggestions of how to get this working?

是否有任何方法可以使#clipboard不可撤消,或任何其他如何使其工作的建议?

Edit

I managed to improve things a little bit by adding the line

我设法通过添加线来改善一些事情

$('#clipboard').val('');

Right after the execCommand line. This seems to neutralize undo completely: the caret no longer leaves the contentEditable field, but nothing gets undone at all. A bit of an improvement, but I'm still searching for a proper solution.

紧跟execCommand行之后。这似乎完全抵消了撤销:插入符号不再离开contentEditable字段,但根本没有任何撤消。有点改进,但​​我仍在寻找合适的解决方案。

4 个解决方案

#1


1  

CodeMirror 1 does this by stripping away formatting after text is pasted. CodeMirror 2 does this by actually having an invisible textarea handle everything, and render the text and cursor manually.

CodeMirror 1通过在粘贴文本后去掉格式来完成此操作。 CodeMirror 2通过实际拥有一个不可见的textarea来处理所有内容,并手动渲染文本和光标。

CodeMirror's website describes how it works in more detail: http://codemirror.net/internals.html

CodeMirror的网站更详细地描述了它的工作原理:http://codemirror.net/internals.html

Beyond that, there's always the CodeMirror source code. You can decide for yourself whether CodeMirror 1 or CodeMirror 2's approach is more suitable for your purposes. :)

除此之外,始终存在CodeMirror源代码。您可以自己决定CodeMirror 1或CodeMirror 2的方法是否更适合您的用途。 :)

#2


0  

Do you try that?

你试试吗?

setTimeout(function() //then this will be executed immediately after the paste actually occurs
{
    $this.focus();
    document.execCommand('insertHTML', true, $('#clipboard').val());
    var t = document.body.innerHTML;
    document.execCommand("undo");
    document.body.innerHTML = t;
});

I think it can help. But I think you must use event object. Unfortunately there may be a problem cuz security reasons.

我认为它可以提供帮助。但我认为你必须使用事件对象。不幸的是,由于安全原因可能存在问题。

#3


0  

In onpaste:

在onpaste:

  1. Store the current selection.

    存储当前选择。

    var sel = window.getSelection();
    var range  = selObj.getRangeAt(0).cloneRange;
    // Store the range object somewhere.
    
  2. Modify the selection object to point to your hidden textarea.

    修改选择对象以指向隐藏的textarea。

  3. Set a timeout with a delay of 0 (occurs immediately after paste).

    设置延迟为0的超时(粘贴后立即发生)。

  4. In the timeout function, grab the data from the hidden textarea, then:

    在超时功能中,从隐藏的textarea中获取数据,然后:

    var sel = window.getSelection();
    sel.removeAllRanges();
    var range = // restore the range object from before.
    sel.addRange(range);
    
    document.execCommand("insertHTML", false, /* contents of your textarea here */);
    

Now if you wanted to do this for actual HTML content, you'd be in a world of hurt....

现在如果你想为实际的HTML内容做这件事,那么你将处于一个受伤的世界......

#4


-2  

Insert a <pre contenteditable="true">...</pre>. As I recall that's exactly what I understand you want. (Unfortunately I'm not yet allowed to join everyone in the comments, but I suppose this is an attempt to answer anyway.)

插入

 ... 
 
 。我记得这正是我理解你想要的。 (不幸的是,我还没有被允许加入评论中的所有人,但我想这无论如何都试图回答。)

#1


1  

CodeMirror 1 does this by stripping away formatting after text is pasted. CodeMirror 2 does this by actually having an invisible textarea handle everything, and render the text and cursor manually.

CodeMirror 1通过在粘贴文本后去掉格式来完成此操作。 CodeMirror 2通过实际拥有一个不可见的textarea来处理所有内容,并手动渲染文本和光标。

CodeMirror's website describes how it works in more detail: http://codemirror.net/internals.html

CodeMirror的网站更详细地描述了它的工作原理:http://codemirror.net/internals.html

Beyond that, there's always the CodeMirror source code. You can decide for yourself whether CodeMirror 1 or CodeMirror 2's approach is more suitable for your purposes. :)

除此之外,始终存在CodeMirror源代码。您可以自己决定CodeMirror 1或CodeMirror 2的方法是否更适合您的用途。 :)

#2


0  

Do you try that?

你试试吗?

setTimeout(function() //then this will be executed immediately after the paste actually occurs
{
    $this.focus();
    document.execCommand('insertHTML', true, $('#clipboard').val());
    var t = document.body.innerHTML;
    document.execCommand("undo");
    document.body.innerHTML = t;
});

I think it can help. But I think you must use event object. Unfortunately there may be a problem cuz security reasons.

我认为它可以提供帮助。但我认为你必须使用事件对象。不幸的是,由于安全原因可能存在问题。

#3


0  

In onpaste:

在onpaste:

  1. Store the current selection.

    存储当前选择。

    var sel = window.getSelection();
    var range  = selObj.getRangeAt(0).cloneRange;
    // Store the range object somewhere.
    
  2. Modify the selection object to point to your hidden textarea.

    修改选择对象以指向隐藏的textarea。

  3. Set a timeout with a delay of 0 (occurs immediately after paste).

    设置延迟为0的超时(粘贴后立即发生)。

  4. In the timeout function, grab the data from the hidden textarea, then:

    在超时功能中,从隐藏的textarea中获取数据,然后:

    var sel = window.getSelection();
    sel.removeAllRanges();
    var range = // restore the range object from before.
    sel.addRange(range);
    
    document.execCommand("insertHTML", false, /* contents of your textarea here */);
    

Now if you wanted to do this for actual HTML content, you'd be in a world of hurt....

现在如果你想为实际的HTML内容做这件事,那么你将处于一个受伤的世界......

#4


-2  

Insert a <pre contenteditable="true">...</pre>. As I recall that's exactly what I understand you want. (Unfortunately I'm not yet allowed to join everyone in the comments, but I suppose this is an attempt to answer anyway.)

插入

 ... 
 
 。我记得这正是我理解你想要的。 (不幸的是,我还没有被允许加入评论中的所有人,但我想这无论如何都试图回答。)