const hl = new Highlight(range); // 创建高亮对象(参数 range 为前面创建的范围)
CSS.highlights.set('hl-keyword', hl); // 为高亮对象注册CSS定义的伪元素(使用该高亮样式)
下面将上述所有步骤的代码整合成一个可运行的演示,该演示示例通过按钮的点击事件创建高亮效果并可取消高亮:
<style>
::highlight(hl-keyword) { background: tan; }
</style>
<p id="p">花朝论坛使用 Discuz! X3.4 创建</p>
<p>
<button>创建高亮</button>
<button>取消高亮</button>
</p>
<script>
// 高亮函数(设定范围并高亮显示)
function hLight() {
const p = document.getElementById('p'); // 获取要操作的元素
const range = new Range(); // 创建 Range 范围
range.setStart(p.firstChild, 0); // 范围开始位置
range.setEnd(p.firstChild, 4); // 范围结束位置
const hl = new Highlight(range); // 创建高亮对象(参数 range 为前面创建的范围)
CSS.highlights.set('hl-keyword', hl); // 注册CSS定义的伪元素(应用高亮样式)
}
// 注销高亮函数
function clearHLight() {
CSS.highlights.clear(); // 使用CSS.highlights.clear() 方法清除高亮样式注册
}
</script>
【创建高亮小结】 1. 在CSS中使用 ::highlight() 伪元素定义高亮样式 2. 创建 Range(范围)对象 3. 为 Range 范围创建高亮对象(new Highlight) 4. 将CSS高亮样式注册给高亮对象(即高亮对象应用 CSS 高亮伪元素样式)
二、使用 TreeWalker 遍历节点 前面的示例待操作元素的HTML结构是单纯文本节点所构造,但真正的 Web 页节点构成可能是非常复杂的,文本可能存在于深层嵌套的元素。为此,创建 Range 范围会跟着变成复杂化,我们需要对之进行遍历、找出最里层的文本节点(#text)。遍历指定元素内的子节点有很多方法,其中借助 JS 内置的 TreeWalker API 加以实现最为简洁高效。以下函数演示了如何找出指定元素 pa 包含的所有文本,不论这些文本如何被各种子元素所嵌套:
function findText(pa) {
// 创建 TreeWalker 对象,节点过滤器仅接收#text
const walker = document.createTreeWalker(pa, NodeFilter.SHOW_TEXT);
// 遍历所有 walker 找到的节点
while(walker.nextNode()) {
const textNode = walker.currentNode; // 处理当前文本节点
const text = textNode.textContent; // 拿到文本内容
//console.log(text); // 打印节点文本内容(或处理其它业务逻辑)
}
}
应该认为 TreeWalker 处理节点的能力相当强大,至少,它轻而易举就能拿到所有被各种嵌套元素分割、包裹的具体文本内容。
高亮指定元素内所有匹配的字符接下来的工作是在指定元素内找到匹配查找关键字的所有字符,然后高亮它们。以下示例可以在线预览:
<style>
#papa { margin: 20px auto; width: 600px; border: 1px solid gray; padding: 10px; }
.tMid { text-align: center; }
::highlight(hl-keyword) { background: tan; }
</style>
<h3 class="tMid">请在下面方框选择文本(不支持跨行选择)</h3>
<div id="papa" class="mdiv">
9876543210123456789
<p>abcABCEFG<b>aaa</b></p>
<p>efgAAA<i>ABC</i>abc</p>
<div>
<p>啦啦啦……啊啊啊……</p>
<p>0123456789876543210</p>
<p><span style="color: blue">啊啊啊……啦啦啦……</span></p>
</div>
</div>
<script>
const papa = document.getElementById('papa'); // 获取待操作元素
// 高亮函数
function hlight(pa, keyword) {
const ranges = []; // Range 范围数组(存储所有范围)
// 创建 TreeWalker 对象(指定 pa 元素、仅处理 #text 节点)
const walker = document.createTreeWalker(pa, NodeFilter.SHOW_TEXT);
// 遍历拿到的节点并逐一匹配
while(walker.nextNode()) {
const textNode = walker.currentNode; // 拿到当前节点
const reg = new RegExp(keyword, 'gi'); // 创建关键字正则表达式
let matches = textNode.textContent.matchAll(reg); // 使用 matchAll 匹配正则
if (!matches) return; // 若无匹配跳过
// 遍历匹配返回的 matches 数组
matches.forEach(match => {
const range = new Range(); // 创建 Range 范围
range.setStart(textNode, match['index']); // Range 开始位置
range.setEnd(textNode, match['index'] + match[0].length); // Range 结束位置
ranges.push(range); // 范围存入数组
});
const hl = new Highlight(...ranges); // 为所有匹配的范围创建高亮对象
CSS.highlights.set('hl-keyword', hl); // 注册高亮样式并应用到高亮对象
}
}
// 指定元素选择文本处理(鼠标松开事件)
papa.onmouseup = (e) => {
CSS.highlights.clear(); // 清空此前的高亮注册
const findText = window.getSelection().toString(); // 获取选中的文本
if (!findText || !e.target.closest('#papa')); // 无选中文本或操作对象元素不是指定元素忽略
hlight(papa, findText); // 调用高亮函数
};
</script>
以上示例综合运用了 CCHA 自定义高亮接口、TreeWalker 遍历接口、Selection 和 Range 对象、正则构造以及 CSS ::highlight() 伪元素实现了文章命题即高亮显示指定元素内所有关键词。其中的正则可能需要扩展,如果不满足于仅查找普通关键词的话。