直接用无法在富文本编辑器中可靠工作,因浏览器原生execCommand行为不一致、光标与缩进逻辑不可控,且需手动处理选区状态、手写插入逻辑及HTML清洗。

为什么直接用 <ol></ol> 无法在富文本编辑器中可靠工作
浏览器原生的 document.execCommand('insertOrderedList') 行为不一致:Chrome 可能插入带 start 属性的 <ol></ol>,Firefox 常生成空 <ol></ol>,Safari 甚至可能把列表项塞进 <div> 而非 <code><li>。更关键的是,用户回车后光标位置、缩进逻辑、嵌套列表展开方式全由浏览器私有实现决定,无法控制。
execCommand 插入有序列表前必须检查选区状态
盲目调用命令会导致格式错乱或 DOM 损坏。实际使用时需先验证上下文:
- 确保编辑容器有焦点:
editor.focus(),否则命令静默失败 - 检查当前选区是否为空:
getSelection().toString().trim() === '',空选区触发insertOrderedList会生成孤立<ol></ol></li> <li>判断光标是否已在列表内:<code>document.queryCommandState('insertOrderedList')返回true时,应优先执行outdent或indent而非重复插入 - 对齐命令(如
justifyLeft)在 Firefox 中queryCommandState不可靠,建议改用getComputedStyle(anchorNode.parentNode).textAlign
手写 <ol></ol> 插入逻辑比依赖 execCommand 更可控
当需要精确控制 start、type、嵌套层级或自定义 li 内容结构时,必须绕过 execCommand,用 Range + DocumentFragment 手动插入:
- 获取当前选区:
const selection = window.getSelection(),确认rangeCount > 0 - 创建有序列表:
const ol = document.createElement('ol'),设置ol.start = 3或ol.type = 'A' - 将选中内容包裹进
li:const li = document.createElement('li'); li.append(range.extractContents()); ol.append(li) - 用
range.insertNode(ol)插入,而非execCommand - 注意:移动端 iOS 上
Range定位易偏移,插入后需手动selection.collapseToEnd()重置光标
保存和导出时 <ol></ol> 的 HTML 必须清洗
直接取 editor.innerHTML 会混入浏览器私有标签(如 <meta>、<span class="Apple-tab-span"></span>)或冗余 style,导致后端解析失败。导出前必须遍历节点标准化:
立即学习“前端免费学习笔记(深入)”;
- 过滤掉无意义的
<ol></ol>:若子节点不含<li>或仅含空文本节点,整个<ol></ol>应被移除 - 统一
li结构:把<p><span>text</span></p>归一为<li>text</li>,避免嵌套段落 - 剥离危险属性:删除
ol上的style、data-等非标准属性,只保留start、type、reversed - 嵌套列表需递归处理:外层
<ol></ol>下的<ol></ol>不能简单扁平化,要保留层级语义
真正难的不是生成 <ol></ol>,而是用户在第三级列表里按了三次回车后,光标停在哪、缩进是否继承、导出 JSON 时怎么映射嵌套关系——这些细节不会出现在 API 文档里,但会在线上环境突然崩掉。



















