让我们拥有 contenteditable div。浏览器本身管理其撤消。 但是,当从脚本(除了用户操作之外)进行其他内容更改(或触摸选择范围)时,它会停止按用户预期运行。
换句话说,当用户按下 Ctrl+Z 时,div 内容不会恢复到之前的状态。
参见以下简化的仿真示例:
https://codepen.io/farin/pen/WNEMVEB
const editor = document.getElementById("editor")
editor.addEventListener("keydown", ev => {
if (ev.key === 'a') {
const sel = window.getSelection()
const range = window.getSelection().getRangeAt(0)
const node = range.startContainer;
const value = node.nodeValue
node.nodeValue = value + 'aa'
range.setStart(node, value.length + 2)
range.setEnd(node, value.length + 2)
ev.preventDefault()
}
})
所有书写的“a”字母都是双写的。
只要不输入“a”就可以撤消。 当用户输入“a”(作为双“aa”附加到文本中)并按 Ctrl+Z 时,他预计“a”将被删除并且光标移回原始位置。
相反,撤消时仅恢复一个“a”,并保留脚本添加的第二个“a”。
如果事件也被 PreventDefault() 阻止(在本示例中不需要,但在我的现实世界示例中我几乎无法避免它),那么一切都会更糟。 因为撤消会恢复之前的用户操作。
我可以想象整个撤消/重做内容将由脚本管理,但这意味着整个撤消/重做逻辑的实现。这太复杂了,可能很脆弱,而且可能有很多故障。
相反,我想告诉浏览器诸如存在原子更改之类的信息,该更改应该由一个用户撤消恢复。这可能吗?
最佳答案
您可以将“修订版本”存储在数组中,然后每当以编程方式更改 innerHTML
时,将 div
的 innerHTML
推送到该数组中其中。
然后,每当用户使用 Ctrl + Z 时,您都可以将 div 的 innerHTML
设置为修订数组中的最后一项快捷方式。
const previousRevisions = []
function saveState() {
previousRevisions.push(editor.innerHTML)
}
function undoEdit() {
if (previousRevisions.length > 0) {
editor.innerHTML = previousRevisions.pop();
}
}
const editor = document.getElementById("editor")
editor.addEventListener("keydown", ev => {
if (ev.key === 'a') {
saveState()
const sel = window.getSelection()
const range = window.getSelection().getRangeAt(0)
const node = range.startContainer;
const value = node.nodeValue
node.nodeValue = value + 'a'
range.setStart(node, value.length + 1)
range.setEnd(node, value.length + 1)
} else if (ev.ctrlKey && ev.key == 'z') {
undoEdit()
}
})
#editor{width:600px;min-height:250px;border:1px solid black;font-size:24px;margin:0 auto;padding:10px;font-family:monospace;word-break:break-all}
<div id="editor" contenteditable="true">type here </div>
此解决方案的好处是,它不会与浏览器的 native Ctrl + Z 快捷键行为发生冲突。
关于html - 如何撤消对 contenteditable div 上的脚本所做的更改,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/69857400/