可编辑 div,名称染色交互方案

需求描述

用户在一段文本输入过程中,如果遇到系统内存在的姓名就用蓝色标识出来,也可以在光标处通过点击姓名的方式把姓名加进去。

交互细节,拆分

  • 中括号文本内容,匹配到姓名,颜色标蓝色,不符合条件则不标记颜色
  • 在鼠标焦点位置,可以粘贴文本
  • 在鼠标焦点位置,可以点击姓名插入
  • 复制粘贴姓名后,蓝色样式还在

首先了解下 window.getSelection() 是什么 ?

它返回一个 Selection 对象,表示用户选择的文本范围或光标的当前位置。

如果一个用户光标在这里:

此时可编辑 div 内部有四个子节点

此时获取 window.getSelection() 返回的结果是:

可以看到现在焦点所在的位置是第二个文本节点内(展开 anchorNode),#text 类型,位置在第二个文本节点的第二个元素后面,因为 anchorOffset: 2

方案步骤描述(onInput)

初期方案是,维护各自的块内容,比如如果用户操作的是第二个 text 内容块,那么就根据当前块的内容和位移,进行格式化当前文本,这样焦点不会有问题。但是如果用户跨 childNode 如果形成了新的内容,复制或者粘贴半个块,就会比较难处理了,需要控制的交互边界比较多,所以这里采用了现在的方案。

  1. 监听 div 的 onInput 事件,每次变动后获取最新的文本 e.target.innerText
  2. 记录此时光标在纯文本的位置,比如上面的例子,记录的位置应该是 6
  3. 此时将文本格式化,对的名称用 font 包裹
python 复制代码
# before
 [张三]去了一趟理塘,[李四]去了一趟丽江。
 
 # after, 用 font 包裹
 <font color="#1E90FF">[张三]</font>去了一趟理塘,<font color="#1E90FF">[李四]</font>去了一趟丽江。
  1. 将格式化后的内容重新塞到 div 的 innerHTML 中,dom 重绘,获取新的 childNodes;
  2. 根据记录的焦点位置,对应到新的 nodes 上面,并且存为新的焦点,设置到 selection 上
js 复制代码
// 将光标塞到计算出的新的位置
const selection = window.getSelection();
selection.removeAllRanges(); // 将现在的 selection 选区中光标的选择范围全部清除

const range = document.createRange(); // 生成一个光标选择范围
range.selectNodeContents(divRef.current); // 将选区设置到当前的 div
range.collapse(true); // 折叠选取,光标处于开始位置
range.setEnd(newAnchorNode, newAnchorOffset); // 设置光标结束点,到选区中的某个 node 的 offset 位置
range.setStart(newAnchorNode, newAnchorOffset); // 设置开始点

selection.addRange(range); // 将创建的新的光标 塞入当前的 selection 中
  1. 此时焦点正确,内容也格式化完成。

点击插入名称的实现

  1. 在 div onBlur 的时候记录下当时的焦点信息,位置,比如 6
  2. 点击时候,获取到焦点信息,没有就取末尾
  3. 将当前 div 内部的文本按照焦点位置分割,分为开头和结尾
  4. 将姓名插入形成新的文本,比如在张三后面插入李四,新文本是 [张三][李四]去了一趟理塘,[李四]去了一趟丽江。
  5. 重新格式化上述文本,之后塞到 div 的 innerHTML 里面,失焦处理

代码实现

demo 代码:github

参考

可编辑 div ,window.getSelection() api 详解

相关推荐
前端摸鱼匠26 分钟前
Vue 3 的v-bind合并行为:讲解v-bind与普通属性合并的规则
前端·javascript·vue.js·前端框架·ecmascript
REDcker1 小时前
浏览器端Web程序性能分析与优化实战 DevTools指标与工程清单
开发语言·前端·javascript·vue·ecmascript·php·js
donecoding2 小时前
一个 sudo 引发的血案:npm 全局包权限错乱彻底修复
前端·node.js·前端工程化
风骏时光牛马2 小时前
Raku正则匹配与数据批量处理实操案例
前端
nbwenren2 小时前
2026实测:Gemini 3 镜像站视觉能力实践——拍照原型图,一键生成 HTML+CSS 代码
前端·css·html
Lee川2 小时前
Prisma 实战指南:像搭积木一样设计古诗词数据库
前端·数据库·后端
Linsk2 小时前
Java和JavaScript的关系真是雷峰和雷峰塔的关系吗?
java·javascript·oracle
当时只道寻常3 小时前
浏览器文本复制到剪贴板:企业级最佳实践
javascript
jinanwuhuaguo3 小时前
(第二十九篇)OpenClaw 实时与具身的跃迁——从异步孤岛到数字世界的“原住民”
前端·网络·人工智能·重构·openclaw