直接用contenteditable搭配原生DOM操作是最轻量可控的Markdown编辑器方案,但需自行处理选区判断、文本包裹、光标管理及默认行为拦截,禁用execCommand以确保输出纯Markdown源码。

直接用 contenteditable 搭配原生 DOM 操作,是最轻、最可控、也最容易出问题的方案——别碰第三方编辑器库,除非你真需要数学公式或 Mermaid 图。
如何让工具栏按钮真正触发 Markdown 格式化
工具栏按钮本身不干任何事,它只负责调用格式函数。关键在「选区上下文判断」和「文本包裹逻辑」:
- 点击「加粗」前,先用
document.getSelection()拿到当前选区;如果没选中文字,就默认包裹光标所在词(需手动提取前后空白边界) - 不要直接
execCommand('bold')—— 它插入的是,不是**text**;你要的是 Markdown 源码,不是 HTML 渲染结果 - 对空行或光标在行首/行尾的场景做兜底:比如输入
**|**(|为光标),应自动补全为**|**而非****| - 所有按钮点击后,必须立即调用
editor.focus(),否则移动端容易失焦、iOS Safari 会吞掉第一次输入
为什么不能用 document.execCommand 处理 Markdown 输入
document.execCommand 是为富文本设计的,输出 HTML,和 Markdown 编辑器目标冲突:
- 它无法生成
**text**或[link](url)这类纯文本结构,只能吐或@#@#@#@#@#@#@#@#@#@0 - 对列表操作(如
insertUnorderedList)会强制创建,而 Markdown 需要的是- item行,二者语义不等价 - 在
contenteditable区域里混合使用 execCommand 和手动字符串替换,极易导致光标错位、DOM 节点断裂(尤其 Chrome 120+ 对Range的处理更严格) - 如果你硬要用,至少把
execCommand限制在「预览模式切换」或「HTML 导出」环节,而不是编辑主流程
工具栏多行布局怎么适配不同屏幕宽度
别手写 CSS Grid 或 Flex wrap 去控制按钮换行——浏览器原生支持更稳:
生成可在 deepbi 沙箱 iframe 内运行或作为独立文件运行的交互式 HTML 页面,使用 `window.deepbi` JS SDK 实现数据查询、空间管理、文件浏览及用户操作。当用户提出以下需求时启用该能力: - 创建 KPI 仪表盘、数据表格、文件浏览器、成员列表、管理页面; - 明确要求“转成 dhtml”、“转译成 HTML”、“把 XX 页面转成……”、“控制台页面”、“管理后台”、“DeepBI 页面迁移”; - 提及位于 `apps/console/src/featur
- 给工具栏容器设
display: flex+flex-wrap: wrap,每个按钮用flex: 0 0 auto,避免被压缩变形 - 用
resize事件监听窗口变化,但别每次 resize 都重排;加个setTimeout防抖,延迟 100ms 再执行布局检查 - 移动端优先隐藏低频按钮(如「表格」「引用块」),用
@media (max-width: 768px)控制display: none,别靠 JS 动态增删 DOM - 注意 Safari iOS 的
flex-wrap在contenteditable父级下偶发失效,此时可临时加min-width: 0到按钮上破除 shrink bug
Markdown 工具栏必须拦截的三个默认行为
浏览器对 contenteditable 的原生行为会破坏 Markdown 语法结构,必须显式拦截:
- 回车键:
event.key === 'Enter'时event.preventDefault(),然后手动插入\n或\n\n,否则会插入 - Tab 键:默认缩进 8 字符,Markdown 只认 2 或 4 空格;拦截后插入对应空格数,或用于焦点在工具栏按钮间跳转(需配合
tabindex) - 粘贴内容:
paste事件里取event.clipboardData.getData('text/plain'),丢弃 HTML 片段;否则用户从 Word 粘贴会带一堆
最易被忽略的一点:工具栏按钮状态同步必须基于 Markdown 源码分析,而非 DOM 结构。比如光标在 **hello** 中,不能靠查父节点是否有 来激活「加粗」按钮——因为用户可能刚删了两个星号,DOM 还没重绘,但源码已变。得实时解析光标位置附近的 token。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!










