html无原生撤销重做,依赖javascript实现;document.execcommand已废弃,contenteditable隐式栈脆弱易清空,浏览器行为不一致;命令模式适合富文本精确还原光标样式,状态快照适合简单表单但无法恢复焦点选区;光标、选区、focus序列化与还原才是最大难点。

HTML 本身不提供撤销重做能力,所有可用功能都依赖 JavaScript 实现逻辑控制;用 document.execCommand 已不可行,Chrome 91+ 完全移除,调用直接报 TypeError: document.execCommand is not a function。
为什么不能依赖 contenteditable 的原生 undo/redo
浏览器对 contenteditable 元素确有隐式撤销栈,但极其脆弱:
- 只要执行过
element.innerHTML = ...或element.textContent = ...,栈立刻清空 -
beforeinput中调用event.preventDefault()会中断原生链 - 中文输入法下,
input事件在compositionend后才触发,漏监听就丢操作 - Safari 支持最差,常跳步;Chrome 合并连续输入,Firefox 更敏感——行为完全不一致
该用命令模式还是状态快照
二者适用场景不同,选错会导致光标丢失或内存暴涨:
- 命令模式适合富文本:每个操作封装为含
execute()和undo()的对象,比如{ type: 'insert', node: editor, offset: 12, text: 'hello' },能精确还原光标和样式 - 状态快照适合表单或简单编辑器:存
{ username: 'a', email: 'b' }这类纯数据,用JSON.parse(JSON.stringify(state))浅拷贝即可,但无法恢复焦点或选区 - 若需兼容 Safari structuredClone();含
Date、Map时得手写克隆逻辑
如何正确同步按钮禁用状态与快捷键行为
只靠 redoStack.length > 0 判断按钮是否可点,大概率出错:
- 用户撤销后立即输入新内容,
redoStack被清空,但按钮未及时设disabled - Ctlr+Y / Cmd+Y 快捷键触发
redo()后,按钮状态没更新,视觉与实际不一致 - 异步操作(如远程保存成功后自动 push redo)未等 DOM 渲染完成就检查栈长
- 多个编辑区共用同一套栈,但按钮只监听了局部状态,导致点击无效
真正难的不是存状态,而是光标、选区、focus 状态的序列化与还原——漏掉任意一项,用户就会“找不着北”。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











