Web 技术研究所

我一直坚信着,Web 将会成为未来应用程序的主流

列表即时搜索效果

  这个效果貌似没有什么专业的术语来称呼它,我们就称它为列表即时搜索效果吧。搜索功能本身是非常耗费资源的一个功能,但是在这个效果中,我使用了上一篇文章中提到的正则表达式的方法来搜索。搜索的部分我就不说了,这个效果还剩下一个难点,那就是CSS的渲染效率。如果你的列表只有几百项,那你随便这么做都行。但是如果一个大列表有成千上万项,那你就不能那么随便了。CSS本身的效率虽然很高,但是在大数据量的处理上,效果就非常不尽人意。所以我们不能把渲染的工作全部交给浏览器处理,我们只能让浏览器处理当前展示的那一部分,其它的部分丢着不处理,这样可以大大节约资源提高渲染效率。但是这种做法有一个缺点,就是用户直接用鼠标拖动滚动条的时候列表会闪动,这是因为鼠标拖动滚动条时候触发渲染太频繁,浏览器处理不过来。如果只是用鼠标滚轮滚动就不会有什么问题。
  首先,我们需要一个很大的数据列表,这个数据用JSON保存。这里我用PHP自动生成这个数据,数据的内容是GB2312的所有汉字和相应的代码。文章的最后我会提供这个程序生成的JSON,所以如果不懂PHP可以跳过这个部分。 header('Content-Type:text/javascript; charset=UTF-8');
//创建空数组
$o=array();
//创建id计数器
$id=0;
//循环所有汉字
for($i=0xB0;$i<=0xF7;$i++)
  for($j=0xA1;$j<=0xFE;$j++)
    $o[]=array(
      'id'=>$id++,
      'char'=>urlencode(iconv('GB2312','UTF-8',pack('C2',$i,$j))),
      'code'=>($i<<8)+$j
    );//创建对象放入数组
//数组转换成JSON并输出
echo urldecode(json_encode($o));
  接着是前端的部分 <style>
dl {width:200px;}
dd {margin:0px;}
ul {padding:0px;position:relative;height:500px;overflow:auto;}
li {
  display:block;overflow:hidden;
  width:100%;height:20px;
  font:14px/20px 微软雅黑;
  position:absolute;

span {display:inline-block;width:3em;}
</style>
<dl>
  <dt><input id="word" autocomplete="off" /></dt>
  <dd><ul id="list"></ul></dd>
</dl>
<script>
//AJAX获取数据
var xhr=window.XMLHttpRequest
  ?new XMLHttpRequest
  :new ActiveXObject("Microsoft.XMLHTTP");
xhr.open("GET","test.php",true);
xhr.onreadystatechange=function(){
  xhr.readyState==4&&onDataReady(xhr.responseText);
};
xhr.send();
//AJAX回调函数
function onDataReady(json){
  var word,list,data,items,ow,vs,vo;
  //获取元素
  word=document.getElementById("word");
  list=document.getElementById("list");
  data=eval("("+json+")");
  //把数据写入列表
  var s="",i=0,o;
  while(o=data[i++])s+=
    "<li style=\"top:"+((i-1)*20)+"px\">"+
      "<span>"+o.id+"</span>"+
      "<span>"+o.char+"</span>"+
      "<span>"+o.code+"</span>"+
    "</li>";
  list.innerHTML=s;
  //变量初始化
  items=list.children;
  i=items.length,vo=[];
  while(i--)vo[i]=null;
  //输入框事件
  word.onkeyup=function(){
    var w,i,j,o,s;
    //输入框无变化则不做动作
    w=word.value;
    if(w==ow)return;
    //从JSON中搜索数据项
    o={},s=[];
    search(w,json,function(e,id){
      s.push(id);o[id]=null;
    });
    //设置项目位置
    for(i in s)items[s[i]].style.top=i*20+"px";
    //隐藏多余的项目
    for(i in vo)if(!(i in o)){
      items[i].style.display="none";
      delete vo[i];
    };
    //显示最后一个,以保证滚动条长度
    if(s.length)items[s[s.length-1]].style.display="block";
    //保存变量
    vo=o;vs=s;ow=w;
    //调用滚动事件
    list.onscroll();
  };
  //列表滚动时触重绘
  list.onscroll=function(){
    var v,o,l;
    //计算当前滚动条位置
    v=list.scrollTop/20|0,l=v+25+10,i=v-10;
    for(i=i<0?0:i;o=items[vs[i]],i<l;i++)
      if(o)o.style.display="block";
      else break;
  };
};
//从JSON中查找
function search(w,s,f){
  var c='(?:\\\\"|[^"])';
  s.replace(
    RegExp(
      '"id":(\\d+)(?:,"\\w+":("?)'+c+
      '+?\\2(?=,|}))*(?:,"(\\w+)":("?)'+c+
      '*'+w+c+'*?\\4(?=,|}))',
      "g"
    ),f
  );
};
</script>
  前面的CSS和HTML部分我就不说了,从JS部分开始,我们首先使用AJAX加载之前在服务器端生成的JSON,在把这个JSON转换成对象的时候我们要保留原来的JSON,因为后面的程序是使用正则的方式来搜索的,所以必须通过JSON才能搜索。得到这个JSON转换成的对象以后我们要拼接HTML字符串,之所以使用innerHTML是因为这个数据量很大,如果用JS自己的循环去操作DOM节点一个个appendChild效率太低了。这里还要注意我们要先拼成字符串,然后一次性写入innerHTML中。千万别使用innerHTML+=这样的操作,那样还不如使用appendChild的效率高。为了减少对DOM节点属性的读写,我们使用JS的变量来保存列表项的显示或隐藏的状态。由于低版本的浏览器中的数组对象不给力,我们就直接把这个显示或隐藏的状态保存两份。一份是用数组保存的,如果需要其它排序可以对这个数组排序。另一个是用对象的键保存的,这是因为键可以直接in运算符来判断某个属性是否存在。这些就是这个效果的准备部分了。
  接着是输入框的事件,其实键盘事件不太保险,因为用户可以从鼠标进行粘贴操作来改变里面的字符。你可以换成setInterval来扫描变化,就像实现PlaceHoder效果的时候一样。不过为了方便演示,我就使用键盘事件了。然后就是搜索,这个我们在昨天的文章中已经说过就不重复了。搜索到的结果我们保存的两个变量中,它们就是这次查询需要显示的项了。现在,我们还不能急着显示它,因为如果显示它就变成把所有操作都堆进CSS的处理中,那样会变得非常卡。我们可以先计算项目的坐标,因为是绝对定位的所以如果不计算坐标列表就会乱掉。然后要做的是隐藏掉原来已经显示着但是现在不需要显示的项目。做完这些之后我们要显示需要的项目,但也不是一次把全部都显示出来,那样CSS的处理速度跟不上用户的输入速度,会变得很卡。我们先显示我们需要显示的最后一个项,因为它必须显示。如果它不显示,那么滚动条的可滚动长度就会出问题。最后保存一些变量到我们在父级作用域声明的变量中,再触发一个列表滚动事件来让浏览器渲染。到这里输入框事件做的事情就基本完成了。
  最后就是这个列表滚动事件,其实很简单的,也没多少代码。就是计算滚动条位置,找到当前显示在用户屏幕上的项。显示它们就可以了,但是只显示那么多还不够。我们需要在当前显示的范围的前后添加若干个项的显示来做缓冲,要不然用户鼠标滚轮滚动时会感觉有点闪。我的代码中是在上下各添加10个项,如果有必要可以添加更多。
  
  列表即时搜索效果.rar
网名:
3.84.186.*
电子邮箱:
仅用于接收通知
提交 悄悄的告诉你,Ctrl+Enter 可以提交哦
神奇海螺
[查看全部主题]
各类Web技术问题讨论区
发起新主题
本模块采用即时聊天邮件通知的模式
让钛合金F5成为历史吧!
次碳酸钴的技术博客,文章原创,转载请保留原文链接 ^_^