需求
根据需求需要做一个,可以在一篇文章中,选择一段文字,给相应的文字打标签,同时相应的文字背景需要变色标签的颜色。如图这样:
由于选取标签避免麻烦,所以需要划出区域后立即弹出标签选择菜单,同时弹出菜单后可以支持快捷键的快速标注。在普通情况下,鼠标移入标签区域会浮现删除按钮,可以删除相应的标签,或者是点击标签区域,可以更换标签。
在其他一些阅读的场景中其实是有类似实现的,只是大部分是划线,而且并且没显示标签。
整理一下需求:
这个核心功能就是在相应的文字区域增加标记,标记对应选中标签,同时选中区域需要有与标签相同的颜色。
然后围绕此附加了几个功能:选区完成后弹出标签菜单
菜单支持快捷键响应
可以删除标签
可以更换标签
方案选型
获取选中区域
这个是核心,只有先能获取到选中区域,才有办法做后续的动作。查资料发现浏览器有提供一个window.getSelection接口,可以获取用户选中区域的范围,而且兼容性很好。
window.getSelection返回的是一个Selection对象,里面记录了光标的信息。{
anchorNode: DOM // 光标起始选区的DOM
anchorOffset: 0 // 光标起始选区的偏移量
focusNode: DOM // 光标结束选区的DOM
focusOffset: 0 // 光标结束选区的偏移量
baseNode: DOM // 与anchor的一致
baseOffset: 0
extentNode: text // 与focus的一致
extentOffset: 0
isCollapsed: false // 光标起始点与结束点是否处于同一处(即是否选取了一段文字)
rangeCount: 1 // 对象获取到多少个range 一般是0或1
type: "Range" // 类型 range为选取了一段文字 caret为点击了某处
}
如果HTML结构是一级的,则可以直接使用。但如果选区里HTML包含子标签,或者起始点和结束点落在了两个HTML块中,这时候,结束的索引会从当前HTML快重新计算,这时候可能会出现起始索引8、结束索引2的状况,可能是是用户反向选择内容,也可能是这里落在了两个HTML块中,这些是需要自己处理的部分。
渲染排版
剩下的就是排版问题,有三个实现方案。
surroundContents
surroundContents是Range对象的一个方法,可以将对象的内容移动到一个新的节点,利用这个可以很方便的将一段文字加上标签包裹住。
Range对象通过window.getSelection.getRangeAt(0)可以获取,获取到的是当前选取区间的文字的range对象。
这种操作可以很快速的实现所需功能,但如果遇到稍微复杂一些的HTML元素,就会出问题,而且无法支持标签交错的情况。
HTML
最简单的是利用HTML本身的流式布局,只要在相关段落增加标签,这样就能通过CSS给标签增加底色、鼠标经过效果,还能增加相应的鼠标操作功能。
主要复杂度就是标签准确的安插,鼠标选择段落因为在不同标签下,起始的索引不同,需要处理。
硬伤就是无法支持标签区域交错的情况。
SVG
需要支持标签区域交错只能用SVG了,SVG元素移动比较自由,可以将底色的块覆盖在文字上,做成图片的效果。
但随之而来的问题就是无法流式布局,导致文本的换行、字的间隔,所有都需要自己手动计算。
于是又查询了W3C