之前看到在eclipse、intellij等ide编辑工具在搜索代码片段时,可以用到模糊匹配的快捷搜索,搜索到的关键字还可以高亮显示,感觉挺方便,用户体验不错,今天自己也想实现一个属于自己的高亮搜索组件,我用js来实现,可以用于table、ul列表;使用该组件时,可以绑定keyup事件,这样在你一边打字的同时,高亮匹配搜索一边就在进行,这个功能在网页上还是挺有用的,下面是具体的代码:
/**
* Highlight show for list search,默认支持UL->LI列表,可以通过在TR元素中设置class:hiddenRootElementClassName用于table
* @author zhangDa
* @param settingOptions 参数设置
*/
function highLightShow(settingOptions){
this.rootElement = settingOptions.rootElement;
if(!this.rootElement){
console.log("The rootElement do not exisits");
return;
}
this.excludedElementsClassName = settingOptions.excludedElementsClassName; //忽略隐藏的元素
this.highLightColor = settingOptions.highLightColor?settingOptions.highLightColor:"yellow";
this.hiddenRootElementClassName = settingOptions.hiddenRootElementClassName;
this.highLightClassName = "zd-highlight";
var replaceDom = "" + "$1" + "";
var exp = "";
var exp2 = "";
/**
* 模糊搜索
* @param element 根元素
* @param matchObj 表示在某行元素中是否有与文本匹配上的关键字
* @return int 显示的行数
*/
this.search = function(queryContent){
this.queryContent = queryContent;
queryContent = queryContent.replace(/\./, "\\.");
exp = new RegExp("(" + queryContent + ")", "g");
exp2 = new RegExp(queryContent);
this.recursionSearch(this.rootElement, {ifMatched: false});
return $(this.rootElement).find("."+this.hiddenRootElementClassName+":visible").length;
}
/**
* 递归查找关键字
* @param element 相对根元素
* @return boolean true表示要删除下一级
*/
this.recursionSearch = function(element, matchObj){
if(typeof(this.excludedElementsClassName)!="undefined" && $.inArray(element.getAttribute("class"), this.excludedElementsClassName) != -1){
return false; // 对于不需匹配内容的元素直接返回,不删除行
}
if(element.childElementCount == 0){
var text = element.innerText;
// 若搜索内容为空的话也为true
if(!matchObj.ifMatched){
matchObj.ifMatched = exp2.test(text);
}
//如果在原来已经高亮的文本中没有匹配新的搜索内容,需要将高亮元素删掉重新加
if($(element).hasClass(this.highLightClassName)){
if(this.queryContent.length == text.length && matchObj.ifMatched){
return false;
}
return true; // 是指要将高亮元素删除
}
if(this.queryContent == ""){
return false; // 没有搜索内容,不需删除行
}
$(element).html(text.replace(exp, replaceDom));
}else{
var children = element.children;
for (var i = 0; i < children.length; i++) {
var shouldRemoveChildElement = this.recursionSearch(children[i], matchObj);
if(shouldRemoveChildElement){
var text = element.innerText;
element.removeChild(children[i]);
// 还要再匹配一次
if(!matchObj.ifMatched){
matchObj.ifMatched = exp2.test(text);
}
if(this.queryContent == ""){
element.innerText = text;
}else{
$(element).html(text.replace(exp, replaceDom));
}
}
}
}
// 若没有匹配上,确定是否要隐藏某行元素
if($(element).hasClass(this.hiddenRootElementClassName)){
var pattern = /\s+/;
if(!pattern.test(this.queryContent)){
if(!matchObj.ifMatched){
element.style.display = "none";
}else{
matchObj.ifMatched = false;
element.style.display = "";
}
}
}
return false;
}
}
这里举一个应用在table上的例子
1、这是table的HTML代码
故障类型
故障代码
故障别名
原因描述
操作
var searchBtn = document.getElementById("search-btn"); //搜索按钮
var searchInput = document.getElementById("search-input"); //搜索框
var tableLength = 2;
var tbodyList = [document.getElementById("testfault-table").getElementsByTagName("tbody")[0],document.getElementById("salefault-table").getElementsByTagName("tbody")[0]]; // 页面上有多个table,组成一个list
var hls = [new highLightShow({
rootElement : tbodyList[0],
hiddenRootElementClassName: "hidden-root",
highLightColor : "gold"
}), new highLightShow({
rootElement : tbodyList[1],
hiddenRootElementClassName: "hidden-root",
highLightColor : "gold"
})] //页面上有多个table,分别生成多个实例
该构造函数的配置参数主要有3个,rootElement是dom根元素,此处是tbody,hiddenRootElementClassName表示不需要参与搜索的元素class,highLightColor是高亮的颜色。
4、table样式处理及事件绑定
function initTbl(index){ // 该函数用于处理表格的隔行换色效果
var tables = document.getElementsByTagName("table");
for (var i = 0; i < tables.length; i++) {
if(index==-1){
var tbl = tables[i];
}else{
var tbl = tables[index];
}
var tbody = tbl.getElementsByTagName("tbody")[0];
var trs = tbody.getElementsByTagName("tr");
if(index==-1){
trs = tbody.getElementsByTagName("tr");
}else{
trs = $(tbody).find("tr:visible");
}
if(trs.length == 0){
var thLength = tbl.getElementsByTagName("th").length;
$(tbody).append("查无数据 ");
}else{
var separateColor = "rgb(240,240,240)";
for (var j = 0; j < trs.length; j++) {
if(j%2==0){
trs[j].style.backgroundColor=separateColor;
}else{
trs[j].style.backgroundColor="white";
}
}
}
if(index!=-1){
break;
}
}
}
function showTbl(index){ // 该函数用于绑定、解绑keyup、click事件
for (var i = 0; i < tableLength; i++) {
if(index == i){
var trs = tbodyList[index].getElementsByTagName("tr");
if(trs.length == 1 && trs[0].classList.contains("no-data")){
searchInput.onkeyup = function(){
return ;
}
searchBtn.onclick = function(){
return ;
}
return ;
}
searchInput.onkeyup = function(){
searchEventCbk(index, this.value);
}
searchBtn.onclick = function(){
searchEventCbk(index, searchInput.value);
}
}
}
}
function searchEventCbk(index, value){
hls[index].search(value);
var noDataTr = tbodyList[index].getElementsByClassName("no-data");
var trs = $(tbodyList[index]).find("tr:visible:not(.no-data)").length;
initTbl(index);
if(noDataTr.length > 0 && trs > 0){
noDataTr[0].parentElement.removeChild(noDataTr[0]);
}
}
5、通过ajax从后台获取table数据、渲染数据
function refreshFaultType(index){
$.get("setting/type",{index: index}, function(result){
var data = result.data;
var append = "";
for (var i = 0, len = data.length; i < len; i++) {
var reasonStr = isEmptyArray(data[i].faultReason)?" ":data[i].faultReason.join(",");
append += ""+
(index == 0 ? (""+(data[i].testType==1?"功能不良":"外观不良")+" ") : "")+
""+data[i].faultCode+" "+
""+data[i].desc+" "+
(index == 0 ? (""+data[i].faultReason+" ") : "")+
""+
""+
" ";
}
$(tbodyList[index]).empty().append(append);
if(index == currentType){
initTbl(index);//初始化table
showTbl(index);//
}
});
}
效果如下:这里是将其用于table,这是搜索之前的数据记录
下面这是打出“电”字以后,即放下键盘以后的效果,有“电”字的记录显示出来,其他被隐藏,而"电"字也被高亮显示出来
如果搜索不到则显示查无数据
这只是一个小小的不起眼的小组件,无非就是用到了一些基本的js功能、正则表达式匹配替换、递归等常见的东西,不过自己实现起来,还是花了一段时间,总的来说,比较有意思。