网页中内容比较多的话,有时候需要通过搜索快速查找特定的文本。当然,这可以通过浏览器的搜索功能实现,但是,象Anki的复习界面这样的场景,并没有搜索功能,我们就需要自己在网页上提供一个搜索框来实现。
下面先用原生JavaScript实现一个。基本思路是,用户在input中输入文字后,在页面上进行查找,找到匹配的内容,就用一个class属性为包含高亮格式的css类名的span标签包裹起来。代码如下:
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="utf-8">
<title>原生js搜索并高亮文本</title>
<style>
/* 用于高亮显示的css类 */
.highlight {
background-color: yellow;
font-weight: bold;
}
</style>
</head>
<body>
<div style="width:30%;text-indent: 2em;color:red;">
这个搜索用的是在innerHTML中搜索文本,因此,如果文本中包含html标签会导致搜索失败。
在innerText中搜索文本,可以排除html标签的影响,但高亮后可能破坏原有的文档标签结构。</div>
<pre>
下面的代码显示两行文本,参见灰色底纹处:
<div class="content">
<p>我爱北京<span>天安<i>门</i></span></p>
天安门上太阳升
</div>
</pre>
<div class="content" style="background-color: #ccc;width:20%;">
<p>我爱北京<span>天安<i>门</i></span></p>
天安门上太阳升
</div>
<input type="text" id="searchInput" placeholder="输入搜索文本..." oninput="highlightText()"/>
<script>
function highlightText() {
const searchInput = document.getElementById('searchInput');
const searchValue = searchInput.value.trim();
const contentElement = document.querySelector('.content');
const content = contentElement.innerHTML;
if (searchValue !== '') {
//先清除原有的高亮,即将用于高亮的<span>标签删除但保留原有内容
let highlightedContent = content.replace(
new RegExp('<span class="highlight">(.*)</span>', 'gi'),
'$1'
);
//高亮,即将需要高亮的内容用类名为包含高亮格式的css类span标签包裹起来
highlightedContent = highlightedContent.replace(
new RegExp(searchValue, 'gi'),
'<span class="highlight">$&</span>'
);
contentElement.innerHTML = highlightedContent;
} else {
//清除高亮
let highlightedContent = content.replace(
new RegExp('<span class="highlight">(.*)</span>', 'gi'),
'$1'
);
}
}
</script>
</body>
</html>
上面的代码显示的网页大致如下:
在搜索框输入文字,例如“天安”,两处天安均会高亮显示,如下:
再多输入一个“门”,这时候上面的天安门不高亮显示了,只有下面的天安门高亮显示:
这就是网页上部文字说明的意思。上一行的天安与门字中间多了个html标签<i>,导致匹配失败。所以要用原生JavaScript来实现高亮,还要想法消除可能存在的HTML标签的影响。这里涉及到的算法稍微有点复杂的,实现方法可参考利用原生JavaScript实现匹配搜索结果的网页内容高亮。此外,jquery有个mark.js库,用来干这个事完全是小儿科,而且健壮性兼容性都有保障。以上面的页面为例,只需要将input元素的oniput事件删除,将原有的原生JavaScript代码删除,引入jquery和mark库(我这里是下载到本地了,也可以用CDN方式引用),然后增加数行代码即可完成。参见如下代码:
<input type="text" id="searchInput" placeholder="输入搜索文本..."/>
<script src="jquery.min.js" ></script>
<script src="jquery.mark.es6.js"></script>
<script>
$(function() {
$("#searchInput").on("input.highlight", function() {
// 获取指定搜索的字符串
var searchTerm = $(this).val();
// 将解析中匹配的文本高亮
$(".content").unmark().mark(searchTerm,
{
"acrossElements": true,
"separateWordSearch": false,
"className":"highlight",
}
);
}).trigger("input.highlight");
});
</script>
用这个库,html标签就不影响文本的匹配了,参见下图:
将这个功能应用到Anki中,要能够离线使用,需要将上面两个JavaScript库下载下来,复制到“%APPDATA%/anki2”中的Anki登录用户名下的“collection.media”文件夹中,为防止被Anki当作未使用资源删除,应该将JavaScript库文件名修改为以英文状态的下划线开头。然后将最后那个代码片段复制到Anki卡片模板中的适当位置(注意JavaScript库的名称前加上下划线),修改一下相关的jquery选择符及相关类名就行了。