需求
现在有这样一个需求:
根据后端返回的起始位置和结束位置的下标高亮多个指定位置的文本
下面我们分析一下这个需求需要用到的技术有哪些:
-
高亮
高亮效果比较简单, 一般都是给指定的文本包裹一个标签 ( 如
<span>
), 在给这个标签设置一个样式 ( 比如添加背景色 ), 就可以实现高亮的效果了. -
找到指定位置的文本
这个问题很有迷惑性, 最初给人的感觉很简单: ” 这不就是一个切割字符串的问题吗?
slice()
,
substr()
和
substring()
这些方法随便用一个不就可以了吗 ” 起初我也是这么想的, 于是用了一个简答的替换方法:
function replacePos(strObj, pos, replacetext)
{
var str = strObj.substr(0, pos-1) + replacetext + strObj.substring(pos, strObj.length);
return str;
}
var text="abcdefg"
var mystr = replacePos(text, 3, "n");
mystr = replacePos(mystr, 5, "m");
// 输出text="abcndmfg"
这时发现了一个问题: 因为需求是高亮
多个
文本, 所以后端返回的始末位置也是多个. 但是当我们给第一个文本加上包裹的标签后, 文本的下标发生了变化, 整段文本被分成了三部分:
- 高亮标记前面的文本
- 高亮标记的文本
- 高亮标记后面的文本
这么说比较抽象, 举个例子:
这里有一段文本:
电动汽车是指全部或部分由电机驱动的汽车。电动汽车分为纯电动汽车、混合动力汽车、燃料电池电动汽车。
高亮标记前, 文本的下标0 是最前端 “电动汽车…” 那个位置;
开始第一次文本高亮标记, 后端传来的开始位置是2, 结束位置是4, 我们标记这一段的文本 “汽车”;
第一次高亮标记后, 文本被分成了三部分:
- 第一个孩子是 “电动”
-
第二个孩子是高亮标签
<span>
包裹的文本 - 第三个孩子是 “是指…”往后的部分
如果我们再进行第二次标记, 后端传给我们的开始位置和结束位置分别是5和8, 但要知道这个5和8是针对整个文本, 也就是从 “电动” 为下标0开始的, 而此时下标已经变了, 5和8已经不能直接用了. 也有几个办法可以解决这个问题:
-
算好
<span class="highlight"></span>
这个标签的长度, 把这个长度加到原来的下标上, 参考:
前端,修改指定的多个位置的字符颜色
, 但是这个方法个人觉得比较局限. - 用后端返回的下标减去上一个高亮位置的末位置. 相当于是从 “是指…” 开始重新计算下标. 因为这个操作也比较繁琐, 而且与这个项目中其他的需求有冲突, 所以也没有考虑这种方法.
除了考虑字符串本身以外, 还有一种方法也可以考虑, 就是
把字符串转换成数组处理 , 处理完后再转回字符串
. 综合考虑后, 我在项目中采取了这种方式高亮指定文本 (主要是考虑到对下标没有影响, 项目后续有从文本中鼠标划词的需求, 如果用计算下标的方式, 逻辑太复杂).
下面是一个小的demo, 比较好理解, 可以参考一下:
效果
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta content="width=device-width,initial-scale=1.0,maximum-scale=1.0,user-scalable=no" name="viewport">
<title>标题</title>
</head>
<style>
.highlight{
background-color: yellow;
}
</style>
<body>
<div id="myDiv">电动汽车是指全部或部分由电机驱动的汽车。电动汽车分为纯电动汽车、混合动力汽车、燃料电池电动汽车。</div>
<script>
var div = document.getElementById("myDiv");
var textNode = div.firstChild;
var text = textNode.nodeValue
var lists = [[0,3],[5,8]]
div.innerHTML = repp(lists,text)
function repp(idxList,text){
var textList = text.split("")
lists.forEach(list => {
var [startNum, endNum] = list
console.log(startNum, endNum)
if(startNum == endNum - 1){
textList[startNum] =`<span class="highlight">${textList[startNum]}</span>`
} else {
textList[startNum] = `<span class="highlight">${textList[startNum]}`
textList[endNum - 1] = `${textList[endNum-1]}</span>`
}
})
return textList.join("");
}
</script>
</body>
</html>