项目赶着十一上线,老是加班;已经连续个把月单休了。昨天加班今天没事干;居然跑到公司写插件,也是醉了;哈哈哈。废话不多少,下面进入正题;
为大家带来一个小插件叫“联想输入”十分类似jquery的jquery.autocomplete(没用过的赶紧去恶补一下,谢谢。)项目也用到了类似的功能;但是业务强相关的;autocomplete就显得有点不合适了;所以周末没事,写一个;下周刚好能用上。哈哈哈。
先上示例代码;然后讲讲为什么这样设计
/* *联想输入插件 *示例: * var config = {obj:input, * top:top + eHeight + 2, 上边距 * left:left,左边距 * width:740,下拉组件的长度 * skin:"",//定制皮肤 * selectHandler:selectHandler, * changeHandler:changeHandler,//监听对象值变动回调;该函数需要返回对象数组或者以拼接好的html片段(建议使用li标签); * contentType:1, * maxCount:0//最大展示数量,超过时,出现滚动条 * } * *var autoCompleteDialog = new window.AutoComplete().show(config); * * 组件支持两种自定义事件,change;select 同时,支持一个事件绑定多个回调函数,如下: * autoCompleteDialog.on("select",function(argument) { * console.log(" on select ") * }).on("change",function(argument) { * console.log(" on change ") *}); */
Obj,不用多言;就是监听的对象;
Top,联想输入下拉组件的上边距
Left,联想输入下拉组件左边,这个参数主要是针对有些地方,左边距不一定要与输入框做对齐的场景,比如:下图的的场景
width,联想输入下拉组件宽度,这个参数主要是针对有些地方,左边距不一定要与输入框做对齐的场景,比如:input设置了padding-left,或者right;或者如下图这样:(默认监听对象的宽度)
Skin:为定制皮肤,预留一个接口;防止有需求说默认下拉效果不合理;或者多套皮肤的场景。
selectHandler:null,下拉选中后,回调函数;一般场景都是将选中值,填充到输入框中;但是大家应该见过,类似邮箱的联想输入-下拉展示接收人昵称,选中后,输入框填充对应的邮箱地址。
changeHandler:null,//监听对象值变化后,回调函数;一般值变动,需要拿到关键字ajax查询,然后返回数据;这地方,用户可以返回两种数据类型
1.数组对象如:
var data = [{key:1,value:"aaa"},{key:2,value:"bbb"},{key:3,value:"ccc"}];
2.字符串,由于有些业务场景下拉选项要进行一些颜色的强调,或者显示默认的数据;比如,下图所示:
或者时,需要给每一项添加编号等业务场景,如:
contentType:这个属性和changeHandler的返回值一起使用;
结果集拼接方式:
0,自定义拼装;(建议使用li标签)
1,返回对象数组,格式[{key:1,value:"111"},{key:2,value:"222"}]
maxCount:/最大展示数量,超过时,出现滚动条(为0,不设置时,无限下拉,永远不出现滚动条)
源码如下:
/* *联想输入插件 create by jiaxiangjun 2015-9-20 *示例: * var config = {obj:input, * top:top + eHeight + 2, 上边距 * left:left,左边距 * width:740,下拉组件的长度 * skin:"",//定制皮肤 * selectHandler:selectHandler, * changeHandler:changeHandler,//监听对象值变动回调;该函数需要返回对象数组或者以拼接好的html片段(建议使用li标签); * contentType:1, * maxCount:0//最大展示数量,超过时,出现滚动条 * } * *var autoCompleteDialog = new window.AutoComplete().show(config); * * 组件支持两种自定义事件,change;select 同时,支持一个事件绑定多个回调函数,如下: * autoCompleteDialog.on("select",function(argument) { * console.log(" on select ") * }).on("change",function(argument) { * console.log(" on change ") *}); */ (function(){ function AutoComplete() { if (this instanceof AutoComplete) { this.config = { obj:null,//监听对象 top:0,//下拉上边距 left:0,//下拉左边距 width:0,//下拉组件宽度 skin:"",//皮肤 selectHandler:null,//下拉选中后,回调函数 changeHandler:null,//监听对象值变化后,回调函数 contentType:0, //结果集拼接方式:0,自定义拼装;1,返回对象数组,格式[{key:1,value:"111"},{key:2,value:"222"}] maxCount:0//最大展示数量,超过时,出现滚动条 } } else { return new AutoComplete(); } } AutoComplete.prototype = new window.Widget(); AutoComplete.prototype.hide = function(argument) { $(".u-autocom-resSelector").hide(); } AutoComplete.prototype.destroy = function(argument) { $(".u-autocom-resSelector").remove(); } AutoComplete.prototype.show = function(config) { var CFG = $.extend(this.config, config);//合并对象 if (!CFG.obj) { return; } var searchElement = CFG.obj; CFG.width = CFG.width ? CFG.width : CFG.obj.width();//默认宽度为监听对象宽度 var that = this; //input change兼容性处理 if(window.addEventListener){ searchElement[0].oninput = resultHandler; } else { searchElement[0].onpropertychange = resultHandler; } //点击页面其他部分隐藏 $("body").on("click", hideResHandler); function hideResHandler() { var $this = $(this); if($this.hasClass("res_li")){ return; } $(".u-autocom-resSelector").hide(); } $("body").on("click",".u-autocom-resSelector li" ,selectHandler); /*选择下拉选择值*/ function selectHandler(event) { if(CFG.selectHandler && "function" === typeof CFG.selectHandler){ CFG.selectHandler(); that.fire("select", "select data"); $(".u-autocom-resSelector").hide(); return; } var $this = $(this); var liVal = $this.text(); CFG.obj.val(liVal); $(".u-autocom-resSelector").hide(); } function resultHandler(event) { var $this = $(this); var val = $this.val(); var resStr = ""; if (0 === CFG.contentType) { resStr = CFG.changeHandler && CFG.changeHandler(val); } if (1 === CFG.contentType) { var data = CFG.changeHandler && CFG.changeHandler(val); if ("[object Array]" !== Object.prototype.toString.call(data)) {//type=1,时,返回数据必须是数组 return; } resStr = assembleData(data); } that.fire("change", "change data"); appendResult(resStr); } function appendResult(resStr) { if(!resStr || "" === resStr) { return; } var selector = $(".u-autocom-resSelector"); if(0 === selector.length) { selectorStr = ‘<div class="u-autocom-resSelector">‘ + resStr + ‘</div>‘; $("body").append(selectorStr); selector = $(".u-autocom-resSelector"); } else { selector.empty(); selector.append(resStr); } var maxHeight = "none"; var overflowY = "hidden"; //如果设置了最大展示数量,滚动显示 if(CFG.maxCount) { var liHeight =selector.children("li").eq(0).height(); maxHeight = liHeight*CFG.maxCount; overflowY = "scroll"; } selector.css({ "top":CFG.top, "left":CFG.left, "width":CFG.width, "maxHeight":maxHeight, "overflowY":overflowY }); "" !== CFG.skin && selector.addClass(CFG.skin); selector.show(); } //拼装数据成html片段 function assembleData(data) { var len = data.length; var resStr = ""; for(var i = 0; i < len; i++) { resStr = resStr + ‘<li class="res_li" data-value="‘ + data[i].key + ‘">‘ + data[i].value + ‘</li>‘ } return resStr; } /* if(CFG.selectHandler){ this.on("select", CFG.selectHandler); } if(CFG.selectHandler){ this.on("change", CFG.changeHandler); }*/ return this; }; window.AutoComplete = AutoComplete; })(window);代码一共提供了三个接口: 展示,隐藏,销毁;不断说,代码一看就懂。
另外需要说明的是,为了进一步分层和抽象;也为了后期为多组件开发提供基础,抽象出来Widget类。提供了四个接口:
On绑定自定义事情;可以为一个事件,绑定多个函数
Fire执行绑定自定义事情;一个事件,一次执行为其绑定的函数
另外的destroy,bulid等具体插件具体实现;
源码如下:
/** *Widget抽象类 create by jiaxiangjun 2015-9-20 * * @return {[Widget抽象类]} */ (function(){ function Widget() { if(this instanceof Widget) { this.handlers = {}; } else { return new Widget(); } } Widget.prototype = { //绑定自定义事件 on:function(type,handler) { if ("undefined" === typeof this.handlers[type]) { this.handlers[type] = []; } this.handlers[type].push(handler); return this; }, //依次触发自定义事件 fire:function(type, data) { if ("[object Array]" === Object.prototype.toString.call(this.handlers[type])){ var handlers = this.handlers[type]; var len = handlers.length; for (var i = 0; i < len; i++) { handlers[i](); } return this; } }, //销毁函数 destroy:function(argument) { // to do smething }, hide:function(argument) { // to do smething }, build:function(argument) { // to do smething } } window.Widget = Widget; })(window);
调用示例:
//联想输入 var input = $("#keywordInput"); var top = input.offset().top; var left = input.offset().left; var eHeight = input.height(); var config = {obj:input, top:top + eHeight + 2, left:left, width:740, selectHandler:selectHandler, changeHandler:changeHandler, contentType:1, maxCount:7 } var autoCompleteDialog = new window.AutoComplete().show(config); autoCompleteDialog.on("select",function(argument) { console.log(" on select ") }).on("change",function(argument) { console.log(" on change ") }); function selectHandler() { var $this = $(this); console.log(" selectHandler ") var e = event || window.event; var target = e.target || e.srcElement; var liVal = $(target).text(); input.val(liVal); } function changeHandler(keyword) { //ajax请求数据,此处只为演示效果,请自行实现ajax请求过程 console.log(" changeHandler "); var resStr = ‘<ul>‘+ ‘<li class="res_li">搜<span class="keyword-tips">‘ + keyword + ‘</span></li>‘+ ‘<li class="res_li">搜<span class="keyword-tips">‘ + keyword + ‘</span></li>‘+ ‘<li class="res_li">搜<span class="keyword-tips">‘ + keyword + ‘</span></li>‘+ ‘<li class="res_li">搜<span class="keyword-tips">‘ + keyword + ‘</span></li>‘+ ‘<li class="res_li">搜<span class="keyword-tips">‘ + keyword + ‘</span></li>‘+ ‘<li class="res_li">搜<span class="keyword-tips">‘ + keyword + ‘</span></li>‘+ ‘<li class="res_li">其他<span class="keyword-tips">@蝶讯网</span>其他</li>‘+ ‘</ul>‘; var data = [ {key:1,value:"aaa"},{key:2,value:"bbb"},{key:3,value:"ccc"}, {key:1,value:"aaa"},{key:2,value:"bbb"},{key:3,value:"ccc"}, {key:1,value:"aaa"},{key:2,value:"bbb"},{key:3,value:"ccc"} ] return data;//返回data或者,注意需要修改对应的contentType }
自我感觉这个插件还有很多优化的空间,后期不断完善。
对了,还有CSS代码;使用的时less;直接上less代码;没使用过的自己改成css.比较简单不解释。
//关键字,联想输入样式 .u-autocom-resSelector{ position:absolute; width:210px; min-height:100px; display:none; border:1px #DBDBDB solid; border-top:none; background: white; z-index: 999; li{ height:30px; line-height: 30px; } li:hover{ background: rgb(225, 225, 225); cursor: pointer; } .keyword-tips{color:#F4AA09;} }
本文出自 “shuizhongyue” 博客,请务必保留此出处http://shuizhongyue.blog.51cto.com/7439523/1696495
原文地址:http://shuizhongyue.blog.51cto.com/7439523/1696495