基于jquery实现智能提示控件intellSeach.js


Posted in Javascript onMarch 17, 2016

一、需求

我们经常会遇到【站内搜索】的需求,为了提高用户体验,我们希望能做到像百度那样的即时智能提示。例如:某公司人事管理系统,想搜索李XX,只要输入“李”,系统自然会提示一些姓李的员工,这样方便用户使用。说白了,就是用户边输入,系统会提示相关的结果;或者,当用户点击搜索框时,就推荐一些内容,如360、百度都会提示今天的主要新闻或搜索量大的内容。

jquery 已经有一个这样的插件了,叫 autocomplete, 但我觉得不好用。关于autocomplete的介绍也很多,有兴趣的朋友可以去试试。

看标题就知道,这里只是分享一个插件,不会讨论后台搜索的相关算法和过程,也就是说,后台返回特定格式的数据,控件负责渲染结果呈现。ok,先看一下效果图:

基于jquery实现智能提示控件intellSeach.js

样式与控件无关,只需要一个 input text 就可以了。

二、参数说明

控件以json格式作为传输格式。参数比较多,大部分都有默认值(具体看源码),有些可能不常用,保持默认即可。如下:

  url: 请求地址。如:Handler.ashx, 后台获取数据的地址

      property: 要显示的json对象的属性。如果我们直接返回["tom","tom cat","tom2"] 这样的形式,那么该属性可以不用设置;但有时候我们会返回[{"Name":"tom","ID":"001"},{"Name":"tom cat","ID":"002"},{"Name":"tom2","ID":"003"}] 这样的形式,显示的是Name,那么设置该属性为"Name"即可。至于我们想在点击的时候获得点击的项的ID,通过点击事件即可。

      itemNumber: 显示的项数目。

      isEmptyRequest: focus时,空白是否发起请求。就像前面说的,如果点击搜索框时(此时没有内容),想要推荐一些内容,设置该属性为true,即会发起请求。

      defaultValue: 默认值。通常会是:“请输入关键词...” 这类的提示。

      width: 下拉列表宽度。

   aligner: 要对齐的元素。

      maxHeight: 最大高度。如果设置该高度,超过时就会出现滚动条。                    

      ajax:{
     

timeout: 超时时间
            cache: 是否缓存
      },      

event:{       

           setData: 发送请求前触发。用于设置参数
           itemClick: 点击项触发
           enterKeydown: 按下enter键触发
           beforeRender: 所有项呈现前触发
           endRender: 所有项呈现后触发
           itemBeforeRender: 项呈现前触发
           itemAfterRender: 项呈现后触发           
           beforeSend: 发送请求前触发。用户设置请求头部参数等,相当于jquery ajax 的 beforeSend。
     }

   event 里的方法都会在适当的时候触发,需要注意的是,所有方法都接收一个参数,该参数是一个对象,有4个属性,某些情况如果没有该属性的,则为空。包括如下属性:

   jthis: input 的 jQuery 对象。

   jItem: 项的 jQuery 对象。

     data: 返回的 json 字符串。如果在前台需要对返回 json 再进行处理,那么可以通过 data 属性获得,处理完成后,需要将 json 字符串 return。

   event: 事件对象,如按下 enter 时的事件对象。

三、例子

使用例子:

$("#search").intellSearch({
  url:"Handler.ashx",
  property:"Name",
  itemNumber:5,
  isEmptyRequest:false,
  defaultValue:"请输入关键字...",    
  width:$("#search").width() + 2,
  maxHeight:-1,
  event:{
    itemClick:function(obj){
      alert(obj.item.ID);
    },
    enterKeydown:function(obj){
      if(obj.item){
        alert("有当前项");
      }else{
        alert("没有当前项");
      }
    }
  }  
});

四、总结

如果你还有自己的逻辑需要处理,也支持链式调用,大可以这样写 $("#search").intellSearch({参数...}).focus(function(){你的处理...});

分享该插件希望能帮助到有需要的朋友,主要用于学习。由于是v1.0,可能还有一些bug,有发现的朋友也可以告诉我,我会及时修正。

附源代码

js代码

/*搜索智能提示 v1.0
 date:2015.09.08 
*/
;(function(w,$){
  $.fn.intellSearch = function(options){
    var jthis = this;
    var _dftOpts = {
      url:"",//请求地址或数组          
      property:"",//要显示的json对象的属性
      itemNumber:5,//显示的条数
      isEmptyRequest:false,//focus空白是否发起请求
      defaultValue:"",//默认值
      width:0,//列表宽度
      aligner:jthis,//要对齐的元素
      maxHeight:-1,//最大高度          
      ajax:{
        timeout:3000,//超时时间
        cache:true//是否缓存
      },
      event:{
        /*参数说明:parameter:{jthis:"jq input",jItem:"jq item",data:"json result",event:"event"}*/
        setData:null,//设置参数
        itemClick:null,//点击项触发
        enterKeydown:null,//按下enter键触发
        beforeRender:null,//所有项呈现前触发
        endRender:null,//所有项呈现后触发
        itemBeforeRender:null,//项呈现前触发
        itemAfterRender:null,//项呈现后触发
        beforeSend:null//发送请求前触发
      }
    };
    $.extend(_dftOpts,options);
    if(!_dftOpts.url){
      throw Error("url不能为空!");
    }
    var jResult;        
    var _value = "";    
    var _ajax = _dftOpts.ajax;
    var _event = _dftOpts.event;
    var _cache = [];
    var _focusCount = 0;//防止focus触发多次(sogou)
     
    /*on window*/
    window.intellObj = window.intellObj || {}; /*for global event*/
    window.intellDocumentClick = window.intellDocumentClick || function(e){
      if(!window.intellObj.jthis){
        return;
      }
      if(e.target !== window.intellObj.jthis[0]){
        setIntellObj(null);
      }
    }
    window.intellDocumentKeydown = window.intellDocumentKeydown || function(e){
      var jthis = window.intellObj.jthis;
      if(!jthis){
        return;
      }
      var code = e.keyCode;
      var value = window.intellObj.value;      
      var jResult,jCurItem,keyword;
      if(code === 13 || code === 38 || code === 40){
        jResult = window.intellObj.jResult;
        jItems = jResult.find("li");
        jCurItem = jResult.find("li.cur");
        if(code === 13){
          if(jCurItem.length > 0){
            jCurItem.click();
          }else{
            setIntellObj(null);           
            if(_event.enterKeydown){
              _event.enterKeydown({"jthis":jthis,"event":e});
            }
          }
          jthis.blur();
        }else if(jItems.length > 0){
          if(code === 38){
            if(jCurItem.length <= 0){
              jCurItem = jItems.last();
              jCurItem.addClass("cur");
              keyword = jCurItem.text();
            }else{
              var index = jCurItem.index();
              jCurItem.removeClass("cur");
              if(index <= 0){
                keyword = value;              
              }else{
                jCurItem = jItems.eq(index-1);
                jCurItem.addClass("cur");
                keyword = jCurItem.text();
              }
            }
            jthis.val(keyword);
          }else{
            if(jCurItem.length <= 0){
              jCurItem = jItems.first();
              jCurItem.addClass("cur");
              keyword = jCurItem.text();
            }else{
              var index = jCurItem.index();
              jCurItem.removeClass("cur");
              if(index + 1 >= jItems.length){
                keyword = value;
              }else{
                jCurItem = jItems.eq(index+1);
                jCurItem.addClass("cur");
                keyword = jCurItem.text();
              }
            }
            jthis.val(keyword);
          }
        }
      }
    }
    /*event handler*/
    $.fn.unintell = function(){
      remove();
    }
    $(document).unbind({click:window.intellDocumentClick,keydown:window.intellDocumentKeydown})
          .bind({click:window.intellDocumentClick,keydown:window.intellDocumentKeydown});
    jthis.focus(function(){
      _focusCount++;
      if(_focusCount > 1){
        return;
      }
      if(window.intellObj.jthis && jthis !== window.intellObj.jthis){
        setIntellObj(null);
      }
      var keyword = attrValue();
      if(keyword === _dftOpts.defaultValue){
        keyword = "";
        attrValue(keyword);
      }
      if(keyword || _dftOpts.isEmptyRequest){
        sendRequest();
      }
    })
    jthis.blur(function(){      
      _focusCount = 0;
      if(!attrValue()){
        attrValue(_dftOpts.defaultValue);
      }      
    })
    jthis.keyup(function(e){
      if(e.keyCode === 38 || e.keyCode === 40){
        return;
      }
      var keyword = attrValue();
      if(!keyword){
        remove();
        window.intellObj.value = _value = "";
        return;
      }
      if(keyword !== _value){
        window.intellObj.value = _value = keyword;
        sendRequest();
      }
    });
     
    return initBox();
     
    /*function*/
    function initBox(){
      attrValue(_dftOpts.defaultValue);
      return jthis;
    }    
    function initIntell(){      
      generate();
      register();
      setIntellObj({jthis:jthis,jResult:jResult});
    }
    function generate(){
      var offset = _dftOpts.aligner.offset();
      var width = _dftOpts.width ? _dftOpts.width : _dftOpts.aligner.width();
      jResult = $("<ul>",{"class":"intellResult"});
      jResult.width(width).css({"position":"absolute","left":offset.left,"top":offset.top + jthis.outerHeight()});
      $("body").append(jResult);
      if(_dftOpts.maxHeight > 0){
        jResult.height(_dftOpts.maxHeight).css("overflowY","scroll");
      }
    }
    function remove(){
      if(jResult){
        jResult.remove();
        jResult = null;
      }
    }
    function register(){
      jResult.on("click","li",function(){
        var jItem = $(this);
        var index = jItem.index();
        var keyword = jItem.text();
        attrValue(keyword);        
        _value = keyword;        
        if(_event.itemClick){
          _event.itemClick({"jthis":jthis,"jItem":jItem,"item":_cache[index]});
        }
      }).on("mouseenter","li",function(){
        $(this).siblings("li").removeClass("cur").end().addClass("cur");
      }).on("mouseleave","li",function(){
        $(this).removeClass("cur");
      });
    }
    function setIntellObj(obj){
      if(!obj){
        if(window.intellObj.jResult){
          window.intellObj.jResult.remove();
        }
        window.intellObj.jthis = null;
        window.intellObj.jResult = null;
      }else{
        window.intellObj.jthis = obj.jthis;
        window.intellObj.jResult = obj.jResult;
      }
    }
    function sendRequest(){
      var data;
      if(_event.setData){        
        data = _event.setData({"jthis":jthis});
      }
      $.ajax({
        url:_dftOpts.url,
        data:data,
        cache:_ajax.cache,
        timeout:_ajax.timeout,
        beforeSend:function(xhr){
          if(_event.beforeSend){
            _event.beforeSend(xhr);
          }
        },
        success:function(data){
          remove();
          showData(data);
        },
        error:null
      });
    }
    function showData(data){
      data = $.trim(data) ? $.parseJSON(data) : data;
      if(_event.beforeRender){
        var rs = _event.beforeRender({"jthis":jthis,"data":data});
        if(rs === false){
          return;
        }
        if(rs !== undefined){
          data = rs;
        }
      }
      if(!data){
        return;
      }
      var jItem,jA,jSpan,hasProp,item,text,otherTexts,isRender,index;
      var list = $.isArray(data) ? data : [data];
      var length = list.length;
      length = length > _dftOpts.itemNumber ? _dftOpts.itemNumber : list.length;
      if(length <= 0){
        return;
      }
      initIntell();
      _cache.length = 0;
      hasProp = list[0][_dftOpts.property];
      for(var i=0;i<length;i++){
        item = list[i];
        if(item === null || item === undefined){
          continue;
        }
        text = hasProp ? item[_dftOpts.property] : item;
        text = $.trim(text.toString());
        if(text === ""){
          continue;
        }
        jItem = $("<li>",{"class":"intellResult_item"});
        jA = $("<a>",{"title":text}).appendTo(jItem);
        jSpan = $("<span>").appendTo(jA);
        index = text.toLowerCase().indexOf(_value.toLowerCase());
        otherTexts = splitText(text,_value,index);
        if(otherTexts){
          jSpan.text(text.substr(index,_value.length));
          if(otherTexts.length > 1){
            $("<b>",{"text":otherTexts[0]}).insertBefore(jSpan);
            $("<b>",{"text":otherTexts[1]}).insertAfter(jSpan);
          }else{
            if(index === 0){
              $("<b>",{"text":otherTexts[0]}).insertAfter(jSpan);
            }else{
              $("<b>",{"text":otherTexts[0]}).insertBefore(jSpan);
            }
          }
        }else{
          jSpan.text(text);
        }
        isRender = true;
        if(_event.itemBeforeRender){
          isRender = _event.itemBeforeRender({"jthis":jthis,"jItem":jItem,"item":item});
        }
        if(isRender !== false){
          jResult.append(jItem);
          if(_event.itemAfterRender){
            _event.itemAfterRender({"jthis":jthis,"jItem":jItem,"item":item});
          }
        }
        _cache.push(item);
      }
      if(_event.endRender){
        _event.endRender({"jthis":jthis});
      }
      jResult.show();
    }
    function attrValue(value){
      if(!value && value != ""){
        return $.trim(jthis.val());
      }
      jthis.val(value);
    }
    function splitText(text,value,index){
      var tlength = text.length;
      var vlength = value.length;
      if(index === -1){
        return null;
      }
      if(index === 0){
        if(index + vlength >= tlength){
          return null;
        }
        return [text.substr(index + vlength)];
      }
      if(index + vlength >= tlength){
        return [text.substr(0,index)];
      }
      return [text.substr(0,index),text.substr(index + vlength)];
    }
  }
})(window,jQuery);

样式

.intellResult{margin:0;padding:0;background:#fff;border:1px solid #b6b6b6;clear:both;z-index:999;display:none;}
.intellResult li{margin:0;padding:0;padding:5px 15px;height:20px;line-height:20px;overflow:hidden;text-overflow:ellipsis;cursor:pointer;white-space:nowrap;}
.intellResult li.cur{background:#E5E0E0;}

以上就是本文的全部内容,希望对大家的学习有所帮助。

Javascript 相关文章推荐
关于js中alert弹出窗口文本换行问题简单详细说明
Dec 11 Javascript
Ext JS 4实现带week(星期)的日期选择控件(实战二)
Aug 21 Javascript
快速解决FusionCharts联动的中文乱码问题
Dec 04 Javascript
js获取当前地址 JS获取当前URL的示例代码
Feb 26 Javascript
使用vue.js实现checkbox的全选和多个的删除功能
Feb 17 Javascript
vue中的非父子间的通讯问题简单的实例代码
Jul 19 Javascript
详解webpack进阶之loader篇
Aug 23 Javascript
JS中的多态实例详解
Oct 15 Javascript
Vim快速合并行及vim 将文件所有行合并到一行
Nov 27 Javascript
npm 更改默认全局路径以及国内镜像的方法
May 16 Javascript
JS编写兼容IE6,7,8浏览器无缝自动轮播
Oct 12 Javascript
vue的for循环使用方法
Feb 12 Javascript
JavaScript函数内部属性和函数方法实例详解
Mar 17 #Javascript
基于jquery实现简单的分页控件
Mar 17 #Javascript
jQuery实现图片局部放大镜效果
Mar 17 #Javascript
jQuery实现放大镜效果实例代码
Mar 17 #Javascript
node.js使用cluster实现多进程
Mar 17 #Javascript
使用pcs api往免费的百度网盘上传下载文件的方法
Mar 17 #Javascript
Fullpage.js固定导航栏-实现定位导航栏
Mar 17 #Javascript
You might like
三种php连接access数据库方法
2013/11/11 PHP
强制PHP命令行脚本单进程运行的方法
2014/04/15 PHP
php验证码的制作思路和实现方法
2015/11/12 PHP
php封装的pdo数据库操作工具类与用法示例
2019/05/08 PHP
比Jquery的document.ready更快的方法
2010/04/28 Javascript
jquery固定底网站底部菜单效果
2013/08/13 Javascript
js 数组操作之pop,push,unshift,splice,shift
2014/01/29 Javascript
javascript实现 百度翻译 可折叠的分享按钮列表
2015/03/12 Javascript
JQuery通过AJAX从后台获取信息显示在表格上并支持行选中
2015/09/15 Javascript
jQuery中ScrollTo用法示例
2016/09/04 Javascript
bootstarp modal框居中显示的实现代码
2017/02/18 Javascript
今天,小程序正式支持 SVG
2019/04/20 Javascript
vue-cli脚手架引入弹出层layer插件的几种方法
2019/06/24 Javascript
JavaScript实现tab栏切换效果
2020/03/16 Javascript
vue-router为激活的路由设置样式操作
2020/07/18 Javascript
vue 组件简介
2020/07/31 Javascript
[36:29]2018DOTA2亚洲邀请赛 4.1 小组赛 A组加赛 LGD vs TNC
2018/04/02 DOTA
Python set常用操作函数集锦
2017/11/15 Python
python多行字符串拼接使用小括号的方法
2020/03/19 Python
Django+JS 实现点击头像即可更改头像的方法示例
2018/12/26 Python
使用python批量化音乐文件格式转换的实例
2019/01/09 Python
深入了解如何基于Python读写Kafka
2019/12/31 Python
python实现控制台输出彩色字体
2020/04/05 Python
使用keras和tensorflow保存为可部署的pb格式
2020/05/25 Python
python给视频添加背景音乐并改变音量的具体方法
2020/07/19 Python
django中ImageField的使用详解
2020/12/21 Python
HTML5 实战PHP之Web页面表单设计
2011/10/09 HTML / CSS
兰蔻俄罗斯官方网站:Lancome俄罗斯
2019/12/09 全球购物
教师年终个人自我评价
2013/10/04 职场文书
护士找工作求职信
2014/07/02 职场文书
小学生十佳少年事迹材料
2014/08/20 职场文书
国博复兴之路观后感
2015/06/02 职场文书
退休教师追悼词
2015/06/23 职场文书
Python实现8种常用抽样方法
2021/06/27 Python
如何解决goland,idea全局搜索快捷键失效问题
2022/04/03 Golang
Win11黑色桌面背景怎么办?Win11黑色壁纸解决方法汇总
2022/04/05 数码科技