首页 / 资讯中心 / 文章详情

textarea.my源码深度剖析:如何用原生API实现1万步撤销历史与光标位置精确恢复

textarea.my源码深度剖析:如何用原生API实现1万步撤销历史与光标位置精确恢复 ★ FEATURED ARTICLE
textarea.my源码深度剖析如何用原生API实现1万步撤销历史与光标位置精确恢复【免费下载链接】textareaA minimalist text editor that lives in URL项目地址: https://gitcode.com/gh_mirrors/texta/textareatextarea.my 是一款极简在线文本编辑器所有文字内容都存储在 URL hash 中复制链接即可分享。整个产品的 UI、编辑器、1万步撤销历史与光标位置精确恢复仅靠 index.html 中几百行原生 JavaScript 完成零第三方依赖。本文带你逐层拆解它的核心设计。 项目结构速览一个 HTML 文件就是一个完整编辑器仓库结构极其精简全部功能一览无余index.html —— 完整编辑器编辑区 UI、Editor 类、压缩与解压、文件导出sw.js —— Service Worker实现离线缓存manifest.json —— PWA 安装配置README.md —— 项目功能说明文档页面上只有一个编辑区带contenteditableplaintext-only的article标签index.html#L265加上右下角的悬浮菜单新建 / 分享链接 / 保存为 txt。浏览器原生的plaintext-only模式让输入法、换行、粘贴等行为全部由系统代劳开发者一行输入代码都不用写。 1万步撤销历史历史栈 300ms 防抖的实现方案历史快照{html, pos} 二元组撤销系统的核心是 Editor 工厂函数它维护两个关键状态history数组每一步存一份当时的innerHTML快照 光标位置at指针当前所处的历史位置执行撤销doUndo时只需把element.innerHTML还原为快照内容再调用restore(record.pos)把光标送回去两步完成。300ms 防抖把连续输入合并成一步打字时不能每按一个键就记一步否则 1 万步一下午就耗尽了。项目在 key 事件监听器 中用 300ms 防抖一次按键立刻记录一次历史之后 300ms 内没有新动作才把这次停顿视为独立一步——这样逐字打字会被自动合并而停顿后再输入则分成两步撤销粒度非常符合直觉。滚动缓冲区只保留最近 10000 步recordHistory 函数 里有三处巧思去重新快照与上一条的 html 和光标位置完全相同就跳过不浪费一步剪枝每次记录新历史后history.splice(at 1)清掉后面的重做分支撤销后再输入重做栈自然失效限量const maxHistory 10_000超出时从队头splice(0, 1)淘汰最老的一步实现恒定内存的滚动缓冲 光标位置精确恢复save() 与 restore() 的双向算法这是全项目最硬核的部分——DOM 结构经过 Markdown 高亮后是满屏的span而光标坐标基于纯文本偏移两者如何互转save()把 Selection 折算成文本偏移量save 函数 的换算流程从Selection对象读出anchorNode / focusNode / anchorOffset / focusOffset四个原始值若锚点落在元素节点上先插入一个空文本节点做锚定把元素锚点转成文本锚点用 visit 遍历器 按文档顺序走一遍 DOM逐段累加文本节点的nodeValue.length最终得到{start, end, dir}纯文本字符偏移量 选区方向-/-restore()用偏移量重建 Selectionrestore 函数 做完全相反的事再次遍历文本节点通过偏移量定位pos.start和pos.end各自落在哪个节点的第几个字符调用原生setBaseAndExtent(startNode, startOffset, endNode, endOffset)一步恢复选区若方向是-交换两端节点保证反向选中时高亮方向也正确visit()手写栈的迭代版 DFSvisit 遍历器 用普通数组模拟调用栈做深度优先遍历优先压入firstChild并支持访问函数返回stop提前终止——不递归、可中断在超大文档上同样稳定。 原生 API 盘点零依赖如何做到麻雀虽小五脏俱全压缩分享保存时经CompressionStream(deflate-raw)压缩再用Uint8Array.toBase64({alphabet: base64url})转成 URL 安全的 Base64 写入 hash打开链接时DecompressionStream反向解压compress / decompress。整套压缩能力浏览器原生提供无需任何压缩库统一撤销入口监听beforeinput拦截historyUndo / historyRedoindex.html#L641-L644把浏览器原生撤销、鼠标菜单撤销和 CtrlZ 全部收敛到同一套历史栈现代保存 API导出 txt / html 时优先showSaveFilePicker不支持则回退到 Blob 下载index.html#L389-L453离线可用sw.js 注册 Service Worker让编辑器断网也能打开 新手必看的 4 个设计要点防抖是撤销体验的命门合并连续输入才能让步数有实际意义内容与视图状态分离历史只存{html, pos}光标是独立于内容的视图状态这是编辑器设计的标准姿势文本偏移量是最稳定的光标坐标系DOM 节点随时可能被重建纯文本偏移量却永远不变能用原生 API 就不引库零依赖 无需构建、无需打包、无版本冲突整个仓库就是一个可静态托管的目录 总结textarea.my 把极简做到了极致一个 index.html 里同时装着 UI、编辑器内核与 URL 压缩配合原生CompressionStream、Selection、showSaveFilePicker等浏览器 API实现了 1 万步撤销历史与毫秒级光标位置恢复。想动手学习的话重点阅读两处即可编辑器内核 index.html#L592-L868压缩分享逻辑 index.html#L361-L379。【免费下载链接】textareaA minimalist text editor that lives in URL项目地址: https://gitcode.com/gh_mirrors/texta/textarea创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
阅读完成 · 觉得有帮助?
咨询建站