当前位置: 首页 > Web前端 > HTML

HTML高亮关键词真麻烦

时间:2023-04-02 15:01:09 HTML

有这么一个功能:在网页中高亮关键词。本以为简单的innerHTMLreplace操作就可以实现,结果遇到了很多问题。本文记录这些问题以及最终的完美解决方案,希望对有相同经历的朋友有所帮助。如果只对结果感兴趣,忽略过程,直接跳过看结果~常用方法:正则替换思路:如果要突出元素,需要提取关键词包裹在标签中,然后调整样式的标签。使用innerHTML或outHTML,但不使用innerText、outText。constregex=newRegExp(keyword,"g")element.innerHTML=element.innerHTML.replace(regex,""+keyword+"")element.classList.add("highlight")这样做的隐患是:如果关键字是()\等正则对象的关键字,则正则对象的构造会失败。(可以通过转义解决)如果关键字是一些html标签,比如div,innerHTML会被错误替换。如果关键字与某些DOM属性名称和值相同,也会导致异常替换。如下,当关键字为test时,类名会被错误替换为:test

关键字的父节点元素来了通过class的背景着色过程会在一定程度上污染原DOM,可能会影响元素的重新定位。(作为插件,希望尽量少改动原有的DOM)常规优化一:只处理位于标签内的元素varformatKeyword=text.replace(/[-\/\\^$*+?.()|[\]{}]/g,'\\$&')//对关键字中包含的特殊字符进行转义,例如/.varfinder=newRegExp(">.*?"++".*?<")//提取标签里面的文本,避免误用class,id等element.innerHTML=element.innerHTML.replace(finder,function(matched){returnmatched.replace(text,"
"+text+
)})//将提取出来的标签中的文本替换成关键字可以解决大部分问题,但是还有一个问题就是只要标签属性中有<符号,它会破坏匹配规则并导致常规提取内容出错。HTML5数据集可以自定义任意内容,所以这些特殊字符是无法避免的。d">Replace
正则优化2:清除可能受影响的标签keyword
="用变量替换关闭的标签[replaced1]keyword[replaced2]//不处理关闭标签中的id="keyword"="[replaced1]keyword[replaced2]="替换为原标签替换的临时变量keyword这个思路和源码都出自这里,但是有一个问题:如果[replaced1]中包含keyword,那么replace时会出现异常。最重要的是,当标签值中包含<>符号时,这种方法无法正确提取标签。总之,经过N次尝试,正则化未能有效处理各种情况。然后我改变了主意,不通过字符串,而是通过节点。element.childNodes可以最有效地清理标签中的干扰信息。【完美解决】通过DOM节点处理关键字1关键字2,通过parent.childNodes获取所有子节点。可以将子节点替换为innerText.replce(keyword,result)得到想要的高亮效果,如下:keyword2(递归处理:当子节点不包含子节点时执行替换操作)。但是关键字1是文本节点,只能修改文本内容,不能添加HTML,不能独立控制其样式。而且文本节点无法转换为普通节点,这也是最苦恼的事情。最后~,本文的重点来了,因为这个功能,我第一次认真接触文本节点。从这里找到Text对象,通过切割文本节点替换文本节点实现高亮显示。查看源代码并恢复高亮constreg=newRegExp(keyword.replace(/[-\/\\^$*+?.()|[\]{}]/g,'\\$&'))高亮=function(node,reg){if(node.nodeType==3){//只处理文本节点constmatch=node.data.match(newRegExp(reg));如果(匹配){consthighlightEl=document.createElement("b");highlightEl.dataset.highlight="y"constwordNode=node.splitText(match.index)wordNode.splitText(match[0].length);//在第一个关键字文本节点之后切成三个constwordNew=document.createTextNode(wordNode.data);highlightEl.appendChild(wordNew);//高亮节点构建成功wordNode.parentNode.replaceChild(highlightEl,wordNode);//替换文本节点}}elseif(node.nodeType==1&&node.dataset.highlight!="y"){for(vari=0;i