如何撤消脚本对 contenteditable div 所做的更改
How to undo changes made from script on contenteditable div
让我们尽情享受 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' 在撤消时还原,第二个由脚本添加。
如果事件也被 preventDefault() 阻止(在这个例子中不需要,但在我的真实世界的例子中我很难避免它)那么一切都会更糟。
因为撤消会恢复之前的用户操作。
我可以想象整个 undo/redo 东西将由脚本管理,但这意味着整个 undo/redo 逻辑的实现。这太复杂了,可能很脆弱,而且可能有很多故障。
相反,我想告诉浏览器一些类似的事情,有一个原子更改应该由一个用户撤销来恢复。这可能吗?
将父级设置为 div(如果不是)并设置它以便每次用户点击时都会在其中添加跨度,因此新跨度并将其 ID 设置为 span-keyword
将具有 aa
作为值/文本。然后检查用户光标是否在它的开头,并检查它前面是否没有其他文本并且用户没有在其中执行其他操作。如果没有文本并且没有发生其他操作,请执行以下操作:
document.getElementById('span-keyword').remove();
您可以将“修订”存储在一个数组中,然后在您以编程方式更改它的 innerHTML
时将 div
的 innerHTML
推送给它。
然后,只要用户使用 Ctrl + [=,就可以将 div 的 innerHTML
设置为修订数组中的最后一项29=]Z 快捷方式.
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>
这个方案的好处是不会和浏览器原生的Ctrl + Z快捷键行为冲突
让我们尽情享受 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' 在撤消时还原,第二个由脚本添加。
如果事件也被 preventDefault() 阻止(在这个例子中不需要,但在我的真实世界的例子中我很难避免它)那么一切都会更糟。 因为撤消会恢复之前的用户操作。
我可以想象整个 undo/redo 东西将由脚本管理,但这意味着整个 undo/redo 逻辑的实现。这太复杂了,可能很脆弱,而且可能有很多故障。
相反,我想告诉浏览器一些类似的事情,有一个原子更改应该由一个用户撤销来恢复。这可能吗?
将父级设置为 div(如果不是)并设置它以便每次用户点击时都会在其中添加跨度,因此新跨度并将其 ID 设置为 span-keyword
将具有 aa
作为值/文本。然后检查用户光标是否在它的开头,并检查它前面是否没有其他文本并且用户没有在其中执行其他操作。如果没有文本并且没有发生其他操作,请执行以下操作:
document.getElementById('span-keyword').remove();
您可以将“修订”存储在一个数组中,然后在您以编程方式更改它的 innerHTML
时将 div
的 innerHTML
推送给它。
然后,只要用户使用 Ctrl + [=,就可以将 div 的 innerHTML
设置为修订数组中的最后一项29=]Z 快捷方式.
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>
这个方案的好处是不会和浏览器原生的Ctrl + Z快捷键行为冲突