给编辑器实现基本的 撤销/重做 功能有多难?
选择 Tiny-Markdown-Editor1 作为文章编辑器已经一年了。虽然前一周提了近🔟个Bugs,好在作者修复积极,一个不剩。
但是这款编辑器有一个比较致命的缺陷:不支持 撤销(Undo) 与 重做(Redo) 功能。 鉴于我现在编辑器日益完善、日常牢骚多了起来,打开 VSCode 写文章有点麻烦。所以,对编辑器的要求就更多了;进而,无法再忍受一个编辑器不支持撤销与重做。
鉴于作者希望实现一个高效的撤销/重做,而且比较复杂2,暂无计划增加此功能,所以我需要自己增加此功能。 实现一个高效的基于 diff 的变更记录确实很麻烦(需要维护文档结构、仅处理脏掉的部分),而开发富文本编辑器素来又有前端地狱之称,所以我觉得我也应该放弃这种想法💭。
可是 Markdown 就一个纯文本编辑器啊,搞不定复杂的“脏矩形”,还不能一把梭全文替换掉吗? 谁说不行呢?!几十行代码就搞定了,真的。
需要的步骤大概如下:
数据结构:
- 一个数组:用来模拟 Undo Stack 和 Redo Stack。左边是 Undo,右边是 Redo。
- 一个指针:用于记录当前的 Undo Stack 位置,最开始在最左边。
编辑与栈操作:
-
✍️ 编辑时(save)
- 把最新的编辑数据入 Undo 栈,指针右移;
- 清空 Redo 栈。
-
↩️ 撤销时(undo)
- 指针左移,使用前一个 Undo 数据;
- 左移之前的数据因为指针移动变成 Redo 数据。
-
↪️ 重做时(redo)
- 指针右移,使用后一个 Redo 数据;
- 左移之前的 Redo 数据变成 Undo 数据。
是的,就是6️⃣个操作。写作 JavaScript,也只有6️⃣行:34
1 2 3 4 5 6 7 8 9 10 11 12 |
|
上面的 start
和 end
还提供了光标位置、选区记录恢复功能,你就说这糊得能不能用吧?!
现代的浏览器真高级,完全替换 DOM 元素的 innerHTML 居然还能精准保持滚动条位置!