说明
在输入框输入搜索内容后,对页面相应内容进行高亮显示。本文主要针对由pdfjs、mergely等工具生成的html实现搜索高亮
方案
将生成的html与搜索内容进行比对,将匹配到的内容replace为带有高亮样式标签的内容
问题
- 获取html直接进行替换:会替换到原本的标签内容,如输入a,会替换到原本的span标签中的a,导致内容和样式错误
- 递归获取文本节点,替换文本节点匹配的内容:替换后的节点依旧是文本节点,会按照文本进行解析,无法渲染出新增的带高亮样式的标签
- 页面原本渲染的字符被各个标签拆分,没法进行跨标签内容匹配
最终实现
以mergely代码比对结果为例
// 清除上次高亮结果
this.cancleTextHighLight();
if(this.filterText && this.filterText.length) {
// mergely代码比对工具结果分为左右两块,需要分别进行高亮,避免内容一和内容二头尾相接的部分进行错误匹配
// 获取所有包含需要高亮内容的标签,用于获取所有文本内容
// pdfjs需手动创建文本层,后续处理方法类似
let leftTextLayer = document.querySelectorAll('#compare-lhs+.cm-s-default .CodeMirror-line>span');
let rightTextLayer = document.querySelectorAll('#compare-rhs+.cm-s-default .CodeMirror-line>span');
// 高亮展示 param1:包含需要高亮内容的标签;param2:高亮关键词;param3:
this.highLightWord(leftTextLayer, this.filterText, $('.cm-s-default').eq(0));
this.highLightWord(rightTextLayer, this.filterText, $('.cm-s-default').eq(1));
}
cancleTextHighLight() {
if($('.text-normal').length) {
$('.text-normal').removeClass('text-highlight');
}
}
// 高亮关键词
highLightWord() {
let _divList = divList;
const _aim = aim;
if (!_divList.length) {
return false;
}
// 将所有文本合并为一个字符串
let allStr = '';
// 处理所有包含文本的内容的节点,将所有文本合并为一个字符串,并且将每一个字符包裹一层标签进行控制(用于解决跨标签无法匹配高亮问题)
// 这里可以根据需要加判断是否需要对dom进行再次处理
for (let i = 0; i < _divList.length; i++) {
const item = _divList[i];
// 如果是不是文本,则跳出
if (item.innerText === undefined) {
return;
}
// 全部拆成一个字一个单位
let newHtml = '';
const str = item.innerText;
allStr = `${allStr}${str}`;
for (let j = 0; j < str.length; j++) {
newHtml = `${newHtml}<span class="text-normal">${str[j]}</span>`;
}
item.innerHTML = newHtml;
}
// 获取包裹好后所有的单字节点
const doms = container.find('.text-normal');
// 通过完整文本字符串,获取一共能匹配到的高亮字符个数
const total = allStr.split(_aim).length - 1;
// 设置起始匹配位置
let start = 0;
// 如果总体匹配结果大于0,进行高亮
if (total > 0) {
for (let t = 0; t<total; t++) {
// 第一个匹配到的位置
const index = allStr.indexOf(_aim, start);
// 高亮个数为filterText的长度
for (let i = 0; i<_aim.length; i++) {
doms.eq(index + i).addClass('text-hightlight');
}
// 下一个高亮文本开始匹配的位置
start = index + _aim.length;
}
}
}