How can I make a plain text paste in a contentEditable span without breaking undo?
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 mycontentEditable
.
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 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.
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.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. :)
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:
Store the current selection.
var sel = window.getSelection(); var range = selObj.getRangeAt(0).cloneRange; // Store the range object somewhere.
Modify the selection object to point to your hidden textarea.
Set a timeout with a delay of 0 (occurs immediately after paste).
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 comments, but I suppose this is an attempt to answer anyway.)
精彩评论