在dom修改后允许contenteditable撤消

sim*_*ack 9 html javascript contenteditable

有没有办法使用javascript修改contenteditable的元素,以便撤消仍然有效?

Hallo似乎能够做到这一点,在选择一些文本后尝试单击粗体按钮,我通过它的源代码并且不知道他们是如何做到的,唯一提到的halloreundo是一些gui工具栏.

我已经看过undo.js,但这只是将html保存在一个数组中,这实际上会限制撤销堆栈的大小,所以我会在本机解决方案之后,如果可能的话.

小智 7

此代码将保存数组中的contenteditable的每个更改.您可以通过调用save_history()或将此函数附加到任何事件(例如 - on keydown)来手动保存当前状态.我编码检查状态是否相等,所以如果你将click_history绑定在click事件上 - 如果你在编辑器中没有任何更改就点击10次,它将不会保存10状态.此代码适用于能够运行jQuery的每个浏览器:

    //array to store canvas objects history
    canvas_history=[];
    s_history=true;
    cur_history_index=0; 
    DEBUG=true;

//store every modification of canvas in history array
function save_history(force){
    //if we already used undo button and made modification - delete all forward history
    if(cur_history_index<canvas_history.length-1){
        canvas_history=canvas_history.slice(0,cur_history_index+1);
        cur_history_index++;
        jQuery('#text_redo').addClass("disabled");
    }
    var cur_canvas=JSON.stringify(jQuery(editor).html());
    //if current state identical to previous don't save identical states
    if(cur_canvas!=canvas_history[cur_history_index] || force==1){
        canvas_history.push(cur_canvas);
        cur_history_index=canvas_history.length-1;
    }
    
    DEBUG && console.log('saved '+canvas_history.length+" "+cur_history_index);
    
    jQuery('#text_undo').removeClass("disabled");        
}


function history_undo(){
    if(cur_history_index>0)
    {
        s_history=false;
        canv_data=JSON.parse(canvas_history[cur_history_index-1]);
        jQuery(editor).html(canv_data);
        cur_history_index--;
        DEBUG && console.log('undo '+canvas_history.length+" "+cur_history_index);        
        jQuery('#text_redo').removeClass("disabled");    
    }
    else{
        jQuery('#text_undo').addClass("disabled");         
    }
}

function history_redo(){
    if(canvas_history[cur_history_index+1])
    {
        s_history=false;
        canv_data=JSON.parse(canvas_history[cur_history_index+1]);       
        jQuery(editor).html(canv_data);
        cur_history_index++;
        DEBUG && console.log('redo '+canvas_history.length+" "+cur_history_index); 
        jQuery('#text_undo').removeClass("disabled"); 
    }
    else{
        jQuery('#text_redo').addClass("disabled");         
    } 
}
jQuery('body').keydown(function(e){
    save_history();
});
jQuery('#text_undo').click(function(e){
    history_undo();
});
jQuery('#text_redo').click(function(e){
    history_redo();
});  
Run Code Online (Sandbox Code Playgroud)
#text_undo.disabled,#text_redo.disabled{
  color: #ccc;
  }
Run Code Online (Sandbox Code Playgroud)
<html>
<head>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
</head>
<body>
<button id="text_undo" class="disabled">Undo</button><button id="text_redo" class="disabled">Redo</button>
<div id="editor" contenteditable="true">Some editable HTML <b>here</b></div>
</body>
  </html>
Run Code Online (Sandbox Code Playgroud)


tec*_*bar 6

您可以通过document.execCommand()直接执行DOM操作来确保编辑操作的撤消功能。

检查此显示了粗体命令(当然不能撤消)的迷你演示:http : //jsfiddle.net/qL6Lpy0c/


gss*_*gss 6

正如其他人所说,简短的回答是使用 document.execCommand 来保留浏览器的撤消/重做。如果您需要以任何方式以编程方式取消编辑文本(例如支持多行制表符缩进或其他操作文本的快捷方式),则应在设置时使用 document.execCommand('insertHTML') 或 'insertText'新的文本状态。insertText 将在您编辑时创建新的 div 子项,这可能会很麻烦,而 'insertHTML' 不会(但 'insertHTML' 有一些您可能需要解决的 IE 支持问题,其他地方有详细介绍)。

这部分让我陷入了一个巨大的循环,这就是为什么我要写一个新答案,因为我没有发现任何地方提到它:

您可能还需要捕获粘贴事件并将其转换为 execCommand('insertHTML'),否则在粘贴之后您可能进行的任何编程选择更改(例如重置光标等)都有可能向您抛出错误提示该节点不够长,无法进行新选择,尽管很明显。粘贴后,DOM 以某种方式无法识别 yourDiv.firstNode 的新长度,但它会在您使用 execCommand('insertHTML') 后更新它。可能还有其他解决方案,但这是一个简单的解决方案:

$("#myDiv").on( 'paste', function(e) {
    e.preventDefault();
    var text = e.originalEvent.clipboardData.getData("text/plain");
    document.execCommand("insertHTML", false, text);
});
Run Code Online (Sandbox Code Playgroud)