getselection() 返回空 selection 实例是规范行为,因页面未聚焦或选区在跨源 iframe 中;需确保元素 focus、同源下访问 iframe.contentdocument.getselection(),并监听 document 上的 selectionchange 事件。

getSelection() 返回的对象为什么经常是 null?
页面没聚焦或选区在 iframe 里时,getSelection() 会返回空对象(实际是空 Selection 实例,但 toString() 为空、rangeCount 为 0)。不是 bug,是规范行为。
- 确保目标元素已获得焦点(比如
contenteditable元素需先focus()) - 跨 iframe 操作必须在同源前提下,且显式访问
iframe.contentDocument.getSelection() - 监听
selectionchange事件比轮询更可靠,但注意该事件不冒泡,需在document上监听
Range API 修改文本时为何光标乱跳或内容错位?
直接用 range.deleteContents() 或 range.insertNode() 后未重置光标位置,浏览器会按默认策略恢复选区——通常落在修改后节点末尾,而非你期望的位置。
- 操作前用
range.cloneRange()备份起始/结束位置(特别是startOffset和endOffset) - 插入文本优先用
range.insertNode(document.createTextNode('xxx')),而非 innerHTML;后者会触发重排,干扰选区计算 - 操作完成后调用
selection.removeAllRanges()再addRange()恢复,避免残留 range 影响后续判断
contenteditable 中 replaceText() 不生效的三个常见原因
原生没有 replaceText() 方法,多数人是自己封装,但容易忽略 DOM 结构碎片化问题——比如选区横跨 <strong></strong> 和文本节点,range.toString() 虽能读出完整文本,但 range.commonAncestorContainer 可能只是父 <div>,直接替换会破坏嵌套结构。
<ul>
<li>用 <code>range.extractContents() 拿到文档片段后,再用 textContent 替换,最后 range.insertNode() 插入新片段
document.body 直接调用 execCommand()(已废弃),它在 Chrome 98+ 中对非 focus 状态完全失效range.collapsed:为 true 时说明是光标点,不是选区,此时不应触发“替换”逻辑移动端 iOS Safari 的 Selection API 行为差异
iOS Safari 对 Selection 的控制粒度更粗,比如双击选词后,range.startOffset 常常不准,且 getSelection().getRangeAt(0) 在输入法上屏瞬间可能返回过期 range。
- 不要依赖
startOffset计算字符位置,改用range.getBoundingClientRect()获取视觉位置做 UI 对齐 - 监听
input事件比selectionchange更及时,尤其在中文输入场景下 - 设置
user-select: text到目标容器,否则 iOS 可能禁用原生选区(尤其在touchstart阻止默认行为后)
document.activeElement、getSelection().rangeCount、range.intersectsNode(),缺一不可。











