最近做项目碰着这样的一个功能:在网页中高亮关键字。

本以为一个 innerHTML replace 就能实现的大略操作,却碰着了许多的问题。
本文就记录这些问题和终极的完美办理办法, 希望能对有同样遭遇的小伙伴有所帮助。
只对结果感兴趣的,忽略过程,直接跳过当作果吧~

常用做法:正则更换

html的关键代码HTML高亮症结字的完善解决计划 Node.js

思路:要想高亮元素,那么须要将关键字提取出来用标签包裹,然后对标签进行样式调度。
利用 innerHTML,或 outHTML, 而不能利用 innerText,outText。

const regex = new RegExp(keyword,\公众g\公众)element.innerHTML = element.innerHTML.replace(regex,\公众<b class=\公众a\"大众>\"大众+keyword+\"大众</b>\公众)element.classList.add(\公众highlight\"大众)

这样做存在的隐患有如下:

()\div<div id=\"大众parent\"大众> <div class=\"大众test\公众>test</div> </div>

关键字父节点 element 通过 class 来进行背景染色处理,对原始DOM有一定程度污染,可能对 element 再次定位造成影响。
(作为插件希望尽可能少改变原始DOM)

正则优化一:仅处理位于标签内的元素

var formatKeyword = text.replace(/[-\/\\^$+?.()|[\]{}]/g, '\\$&') // 转义处理keyword包含的分外字符,如 /.var finder = new RegExp(\"大众>.?\"大众++\"大众.?<\公众) // 提取位于标签内的文本,避免误操作 class、id 等 element.innerHTML = element.innerHTML.replace(finder,function(matched){ return matched.replace(text,\公众<br>\公众+text+</br>)})// 对提取的标签内文本进行关键字更换

以能办理大多数问题,但依旧存在的问题是,只要标签属性存在类似 < 符号,将会冲破匹配规则导致正则提取内容缺点, HTML5 dataset 可以自定义任意内容,故这些分外字符是无法避免的。

<div dataset=\"大众p>d\"大众>更换</div>

正则优化二:打消可能影响的标签

<div id=\"大众keyword\公众>keyword</div> =》将闭合标签用变量更换 [replaced1]keyword[replaced2]//闭合标签内 id=\"大众keyword\公众 不会被处理 =》 [replaced1]<b>keyword</b>[replaced2] =》将暂存变量 replaced 更换为原来标签 <div id=\"大众keyword\"大众><b>keyword</b></div>这种思路及源码从这里来, 但存在问题是:如果 [replaced1] 包含 keyword, 那么更换时将发生非常

最主要的,当标签值中包含 <> 符号时,此方法也不能精确的提取标签

总之在经由了N多考试测验之后,通过正则都没能有效的处理各种情形。
然后换了个思路,不通过字符串的办法,通过节点处理。
element.childNodes 可以最有效的清理标签内的滋扰信息。

[完美办理方案]通过 DOM 节点处理

<div id=\公众parent\公众> keyword 1 <span id=\公众child\"大众> keyword 2 </span> </div>

通过 parent.childNodes 得到所有子节点。
child 节点可以通过 innerText.replce(keyword,result) 的办法更换得到想要的高亮效果,如下: <span id=\"大众child\公众><b>keyword</b> 2</span> (递归处理:当child节点不含子节点时进行replace操作)。

但是 keyword 1 是属于文本节点,只能修正文本内容,无法增加 HTML,更无法单独掌握其样式。
而文本节点也不能转换为普通节点,这也是最苦恼的事情。

末了~,本文的重点来了,由于这个功能,让我第一次负责打仗到了文本节点这个东西。
从这里创造了Text,利用切割文本节点并更换的办法实现高亮。

源码以及还原高亮见源码

const reg = new RegExp(keyword.replace(/[-\/\\^$+?.()|[\]{}]/g, '\\$&'))highlight = function (node,reg){ if (node.nodeType == 3) { //只处理文本节点 const match = node.data.match(new RegExp(reg)); if (match) { const highlightEl = document.createElement(\公众b\"大众); highlightEl.dataset.highlight=\"大众y\公众 const wordNode = node.splitText(match.index) wordNode.splitText(match[0].length); // 切割成前 关键词 后三个Text 节点 const wordNew = document.createTextNode(wordNode.data); highlightEl.appendChild(wordNew);//highlight 节点构建成功 wordNode.parentNode.replaceChild(highlightEl, wordNode);// 更换该文本节点 } } else if (node.nodeType == 1 && node.dataset.highlight!=\"大众y\"大众 ) { for (var i = 0; i < node.childNodes.length; i++) { highlight(node.childNodes[i], reg); i++ } } }

总结

以上所述是

学习从来不是一个人的事情,要有个相互监督的伙伴,想要学习或互换前端问题的小伙伴可以私信“学习”小明加群获取2019web前端最新入门资料,一起学习,一起发展!