使用 Puppeteer + Chrome 将 HTML 渲染为中文 PDF,自动处理图表等待、Tab 展开、动画、测高、白边消除、防分页,适用于看板、报表、网页和交互图表转 PDF。
直接用 selection api 获取光标字符偏移量:先检查 getselection().rangecount > 0,再取 collapsed range,调用 clonecontents().textcontent.length 得到光标前字符数,需兼容 ie 的 document.selection 及处理 startcontainer 类型差异。

contenteditable 元素内如何用 JS 获取光标在文本中的字符偏移量
直接获取光标在 contenteditable 元素内的字符位置,不能靠 selectionStart(它只对 <input> 和 <textarea></textarea> 有效),得依赖原生 Selection API。核心是:拿到当前选区(getSelection()),再用 getRangeAt(0) 提取范围,最后通过 range.toString() 或遍历 DOM 节点计算偏移。
常见错误是直接读 element.textContent.length 或假设光标总在文本末尾——实际光标可能在任意嵌套节点(如 <strong></strong>、<br>、空 <span></span>)内部,DOM 结构越复杂,偏移越容易算错。
- 必须先检查
getSelection().rangeCount > 0,否则getRangeAt(0)会报IndexSizeError - 光标未选中任何内容时,
range.startOffset是相对其range.startContainer的偏移,不是整个元素的字符索引 - 若
startContainer是文本节点,startOffset可直接用;若是元素节点(如<p></p>),需递归计算其前所有文本节点的字符长度
处理含 HTML 标签的 contenteditable 中的光标位置
当 contenteditable 内容包含内联标签(如 <em>hello</em> world),光标在 “hello” 后面、 和 “ world” 之间时,startContainer 可能是 <em></em> 元素本身,此时 startOffset 表示子节点索引(比如 1),而非字符数。必须把 DOM 树“扁平化”成纯文本流才能映射到用户看到的位置。
简单但可靠的方案是:用 range.cloneContents() 创建文档片段,再调用 textContent 获取从起点到光标的纯文本,长度即为字符偏移量(前提是 range 已设为光标点,即 collapsed)。
- 确保 range 是折叠的:
range.collapsed === true,否则要先用range.collapse(true)折叠到起点 -
range.cloneContents().textContent.length是最接近用户直觉的“光标前字符数”,但它不计入不可见字符(如 转义后变成空格,但原始在 HTML 中占位) - 如果需要精确匹配渲染后的视觉位置(比如做语法高亮定位),得用
range.getBoundingClientRect()获取坐标,而非字符偏移
兼容性与性能注意点:IE 和旧版 Safari 的差异
IE11 支持 document.selection 而非 getSelection(),且其 createRange() 返回的对象 API 不同;Safari 15.4 之前对 range.startContainer 在某些空节点场景返回异常值。不要假设所有浏览器都返回相同类型的 startContainer。
- 安全写法:先判断
typeof window.getSelection === 'function',再 fallback 到document.selection(仅 IE) - 避免高频监听
input或keyup来实时计算位置——DOM 查询开销大,建议用selectionchange事件,它只在光标或选区变化时触发 - 对长文本(>10KB innerHTML),
cloneContents()有内存压力,可改用深度优先遍历 + 累加node.textContent.length,跳过 script/style/comment 节点
Backspace 可能合并两个文本节点,导致上次缓存的偏移失效。别试图一次算准,每次需要时重新查。前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!










