划词功能必须监听 document 的 mouseup 事件获取选区,用 getSelection().toString().trim() 安全取词,空则直接 return;高亮须用 Range.surroundContents 创建 mark,每次先清除旧 mark;移动端需 touchend + preventDefault 阻断系统菜单。

划词必须监听 mouseup,不是 click
用户松开鼠标那一刻才确定选区,click 事件完全不感知选区变化,绑在按钮上也无效。真正该用的是 document.addEventListener('mouseup', ...),且必须挂载在 document 级——因为划词动作和按钮点击是分离的,用户可能先拖选再点按钮,也可能直接右键复制。
取词要用 getSelection().toString().trim()
这是唯一安全的取值方式:window.getSelection() 返回一个 Selection 对象,但直接读 .anchorNode 或 .focusNode 容易因 DOM 变动(比如高亮后节点被替换)抛出 InvalidStateError;.toString() 提取纯文本内容,.trim() 去首尾空白,避免空字符串触发无效搜索。
- 如果返回空字符串,说明没划词、划了空白,或划中了
user-select: none区域(如代码块),此时应直接return - 不要弹提示框、不要发请求、不要禁用按钮——这些都会打断用户自然操作流
高亮必须用 Range.surroundContents,不能 innerHTML 替换
用正则匹配 + innerHTML 替换会破坏 DOM 结构:实体编码(如 )、嵌套标签、换行符全乱套;更严重的是,它会全局匹配子串,导致“搜 <code>div”把 division 里的 div 也染黄。
生成可在 deepbi 沙箱 iframe 内运行或作为独立文件运行的交互式 HTML 页面,使用 `window.deepbi` JS SDK 实现数据查询、空间管理、文件浏览及用户操作。当用户提出以下需求时启用该能力: - 创建 KPI 仪表盘、数据表格、文件浏览器、成员列表、管理页面; - 明确要求“转成 dhtml”、“转译成 HTML”、“把 XX 页面转成……”、“控制台页面”、“管理后台”、“DeepBI 页面迁移”; - 提及位于 `apps/console/src/featur
- 正确做法是:拿到
window.getSelection()后调用getRangeAt(0)得到Range,再用document.createElement('mark')创建节点,最后range.surroundContents(markEl) - 每次高亮前,必须先清除旧
<mark></mark>:遍历document.querySelectorAll('mark')并el.replaceWith(el.textContent),否则新mark会嵌套进旧mark里,DOM 节点泄漏、样式错乱、后续getSelection()返回异常节点
移动端要额外处理 touchend 并 preventDefault
iOS 和 Android 在长按选词时默认弹系统菜单(“复制”“搜索”等),会干扰 getSelection() 获取结果。必须监听 touchend,并在回调里调用 e.preventDefault() 阻断默认行为。
立即学习“前端免费学习笔记(深入)”;
- 不能只靠
mouseup:移动端没有 mouseup,或触发延迟甚至不触发 - 不能只加
user-select: text:有些区域(如pre)设了user-select: none,得临时改pointer-events: none+z-index绕过,但代价是失去原生复制能力,需权衡
mark 嵌套是最隐蔽也最致命的问题——它不会立刻报错,但会让后续所有基于 Selection 的操作逐渐失准,直到某次 surroundContents 抛出 “Range boundary points are not equal” 才暴露。


















