最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - How can I make a plain text paste in a contentEditable span without breaking undo? - Stack Overflow

programmeradmin2浏览0评论

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:

$('.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:

  • First undo undoes the paste.
  • Second undo tries to undo the changes to #clipboard, moving the focus away from my 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.

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.

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

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 pletely: 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.

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:

$('.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:

  • First undo undoes the paste.
  • Second undo tries to undo the changes to #clipboard, moving the focus away from my 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.

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.

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

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 pletely: 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.

Share Improve this question edited Feb 24, 2011 at 6:51 Ian Henry asked Feb 23, 2011 at 3:28 Ian HenryIan Henry 22.4k4 gold badges53 silver badges61 bronze badges 10
  • Why reinvent the wheel? Since you're already using jQuery, why not just use a plugin like markItUp! or TinyMCE? – Matt Ball Commented Feb 23, 2011 at 5:20
  • @Matt - neither of those do what I'm asking. markItUp! uses a textarea -- nothing contentEditable there -- and TinyMCE allows pasting all kinds of markup. I'm not trying to make a rich text editor -- just the opposite, in fact. I'm trying to make a plain text contentEditable element. Just use a textarea, right? Would love to, but I need the content to wrap around other elements in the page. – Ian Henry Commented Feb 23, 2011 at 7:04
  • "markItUp! uses a textarea" — erm... It is much simpler to change it, than writing own from scratch, isn't it? – kirilloid Commented Feb 23, 2011 at 9:11
  • 2 @kirilloid: Not really. The mechanisms for dealing with selections and updating content are so pletely different in <textareas> and contenteditable elements that switching between the two would require a plete rewrite. – Tim Down Commented Feb 23, 2011 at 9:22
  • 3 Sounds to me like you might have to create your own undo mechanism. – Tim Down Commented Feb 23, 2011 at 9:45
 |  Show 5 more ments

4 Answers 4

Reset to default 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's website describes how it works in more detail: http://codemirror/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. :)

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.

In 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.

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

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

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

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 ments, but I suppose this is an attempt to answer anyway.)

发布评论

评论列表(0)

  1. 暂无评论