网页中内容比较多的话,有时候需要通过搜索快速查找特定的文本。当然,这可以通过浏览器的搜索功能实现,但是,象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标签的影响。这里涉及到的算法还是比较复杂的。好在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选择符及相关类名就行了。