可编辑 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 详解

相关推荐
橙露7 分钟前
React Hooks 深度解析:从基础使用到自定义 Hooks 的封装技巧
javascript·react.js·ecmascript
Exquisite.9 分钟前
Nginx
服务器·前端·nginx
2501_9209317020 分钟前
React Native鸿蒙跨平台使用useState管理健康记录和过滤状态,支持多种健康数据类型(血压、体重等)并实现按类型过滤功能
javascript·react native·react.js·ecmascript·harmonyos
打小就很皮...25 分钟前
dnd-kit 实现表格拖拽排序
前端·react.js·表格拖拽·dnd-kit
Ulyanov29 分钟前
从静态到沉浸:打造惊艳的Web技术发展历程3D时间轴
前端·javascript·html5·gui开发
打小就很皮...39 分钟前
React 19 + Vite 6 + SWC 构建优化实践
前端·react.js·vite·swc
Highcharts.js41 分钟前
使用Highcharts与React集成 官网文档使用说明
前端·react.js·前端框架·react·highcharts·官方文档
这是个栗子42 分钟前
AI辅助编程(二) - 通译千问
前端·ai·通译千问
VT.馒头1 小时前
【力扣】2625. 扁平化嵌套数组
前端·javascript·算法·leetcode·职场和发展·typescript
数研小生1 小时前
Full Analysis of Taobao Item Detail API taobao.item.get
java·服务器·前端