基于jQuery打造智能的人员选择输入框UPDATE:修正了在FireFox下显示的问题,重新copy CSS即可
写完这个名字忽然觉得有点标题党的嫌疑,但是又不知道什么样的名字比较合适,那就暂且这样吧。
今天要讲的东西比较简单,其中会用到另外的一个jQuery控件,是一个人员选择输入框。那到底是个什么东西呢?
那好还是先来看最后的效果,有个直接的了解。
是不是和时下流行的SNS网站的选择人员控件很像?对比一下,哈哈是的,其实目的是差不多的。
其实还有很多这样应用,如邮件系统中发件人,在输入时的自动补全,它是一个类似于Autocomplete的功能,但是又要比Autocomplete多那么一些功能的插件
基于这个情况,我所编写的这个控件时基于一个Autocomplete控件的,它就是jQuery.autocomplete,它的官方网址是:http://bassistance.de/jquery-plugins/jquery-plugin-autocomplete/
大家可以通过访问以下来获取它的使用方法,资料和demo还是比较详细,我这边就不重复写了。所以我们还是直接开始扩展的部分
第一我们还是先确定HTML
从html上分析autocomplete和选人控件的所查的就是
容器我们用Div就可以了,小方块也比较简单 ,来看一下,最外层是div包裹,然后是嵌套一个a标签(为了方便以后做hover效果),在a标签中是一个span放置文字,
input[type=hidden]来防止这个节点的数据,img就似乎删除按钮的载体,那控件的载体又是什么呢?其实从设计来说可以是个input的,但是我更偏向于div,在实例化的时候往div中动态添加一个input来附加autocomplete的属性,如果反过来在input外包裹容器的话,在一些特殊情况下定位会是问题。
那html既然已经定义好了,接着就是CSS了,这个css其实很简单,就是容器的边框还有就是item的样子而已,来看下代码
01..bbit-usbox 02.{ 03. border: solid 1px #3C7FB1; 04. margin:20px; 05. padding:2px; 06. display:block; 07. background-color:#f3fefe; 08.} 09..bbit-usbox .bbit-usbox-item 10.{ 11. width:auto; 12. margin-left:4px; 13. margin-top:2px; 14. background-color:#e0e5ee; 15. border: solid 1px #ccd5e4; 16. float:left; 17. white-space : nowrap; 18.} 19..bbit-usbox .bbit-usbox-item a 20.{ 21. color:#000; 22. text-decoration:none; 23. padding-left:2px; 24.} 25..bbit-usbox .bbit-usbox-item a span 26.{ 27.} 28..bbit-usbox .bbit-usbox-del 29.{ 30. background:url("images/usbox/del.gif") no-repeat 50% 80%; 31. width:10px; 32. height:5px; 33. cursor:pointer; 34. border:none; 35. vertical-align:middle; 36. margin-left:2px; 37.} 38..bbit-usbox-boxc 39.{ 40. margin-left:4px; 41. clear:left; 42.} 43..bbit-usbox-box 44.{ 45.} 46..bbit-usbox-boxc input 47.{ 48. background-color:#f3fefe; 49. width:100%; 50. height:17px; 51. display:block; 52. border:none; 53.}第二 开始编写Javascript
还是老规矩,先来个完整代码,非常简单只有不到70行代码
01.; (function($) { 02. if (!$.Autocompleter) { 03. alert("请先引用jquery.autocomplete.js"); 04. return; 05. } 06. $.fn.usbox = function(o) { 07. var def = { 08. urlOrData: "ResponseAutoComplete.ashx", 09. width: "90%", //宽度 10. addItem: false, 11. removeItem: false, 12. clickItem: function() { }, 13. completeOp: {} 14. }; 15. $.extend(def, o); 16. var co = $.extend({ scroll: false, formatItem: function(row, i, max) { return row[0] + "[" + row[1] + "]"; } }, def.completeOp); 17. 18. var temp = "<div class='bbit-usbox-item'><a href='javascript:void(0);'><span>${text}</span><input type='hidden' value='${value}'/><img src='../Themes/Shared/images/s.gif' alt='点击删除' class='bbit-usbox-del'/></a></div>"; 19. 20. return this.each(function(e) { 21. var me = $(this); 22. var id = me.attr("id"); 23. if (id == null || id == "") { 24. id = "usbox_" + new Date().getTime(); 25. } 26. var inc = $("<div class='bbit-usbox-boxc'/>"); 27. var input = $("<input type='text' id='" + id + "_inbox' class='bbit-usbox-box' />").appendTo(inc); 28. me.addClass("bbit-usbox").width(def.width).append(inc); 29. input.autocomplete(def.urlOrData, co).result(function(event, data, formatted) { 30. $(this).val(""); 31. additem(this, data); 32. }); 33. me.bind("addboxitem", function(e,data) { additem(input,data); }); 34. function additem(inc, data) { 35. var tp = $(temp.replace(/\$\{([\w]+)\}/g, function(s1, s2) { 36. if (s2 == "text") { 37. return data[0]; 38. } 39. else if (s2 == "value") { 40. return data.join("|"); 41. } 42. else { 43. return s1; 44. } 45. })); 46. tp.click(def.clickItem).find("img.bbit-usbox-del").click(removeitem); 47. $(inc).parent().before(tp); 48. if (def.addItem) { 49. def.addItem(data); 50. } 51. } 52. function removeitem() { 53. var p = $(this).prev() 54. var v = p.val(); 55. var arr = v.split("|"); 56. if (def.removeItem) { 57. def.removeItem(arr); 58. } 59. $(this).parent().parent().remove(); 60. } 61. return me; 62. }); 63. }; 64. $.fn.addboxitem = function(op) { 65. $(this).trigger("addboxitem", [op]); 66. }; 67.})(jQuery)接着我们来一步一步来分析我的实现,开始还是编写jQuery控件的“模板”,关于为什么要这么写,请参考这篇的说明
1.; (function($) { 2. if (!$.Autocompleter) { 3. alert("请先引用jquery.autocomplete.js"); 4. return; 5. } 6. $.fn.usbox = function(o) { 7. } 8.})(jQuery)这次加了一个判断,因为我们这个控件是依赖于autocomplete的,如果你看了前两篇,那么就一定知道接着就是编写默认参数
1.var def = { 2. urlOrData:false,//必须!请求数据的url和或者直接是数据,格式参考autocomplete的说明 3. width: "90%", //宽度 4. addItem: false,//当从下拉选项中选择一个人员后触发的函数 5. removeItem: false,//当从已选择的人员删除一个人时触发的函数 6. clickItem: function() { },//点击人员小方块时触发的时间 7. completeOp: {}//autocomplete的参数,格式参考它自身的说明 8. };参数也是比较简单,添加和移除的两个函数比较重要,在demo中会讲到,autocomplete的参数因为太多,大家只有自己参考一下官方的说明, 默认不管也可以,因为我会给大家默认来一下.
1. 2.$.extend(def, o); 3.//这才是默认的Complete的参数 4. var co = $.extend({ scroll: false, formatItem: function(row, i, max) { return row[0] + "[" + row[1] + "]"; } }, def.completeOp); 5.//定义小方块的模板,其中s.gif是个空图片,位置可根据实际情况进行调整 6. var temp = "<DIV class=bbit-usbox-item><A href="javascript:void(0);"><SPAN>${text}</SPAN><INPUT type=hidden value=${value}><IMG class=bbit-usbox-del alt=点击删除 src="../Themes/Shared/images/s.gif"></A></DIV>";formatItem函数是下拉中显示的格式 ,接着是生成HTML,注册事件,详细的步骤我已经注释到代码中了如下所示
01. 02.return this.each(function(e) { 03. var me = $(this); 04. var id = me.attr("id"); 05. //获取唯一的ID 06. if (id == null || id == "") { 07. id = "usbox_" + new Date().getTime(); 08. } 09. //input的容器 10. var inc = $("<div class='bbit-usbox-boxc'/>"); 11. //生成一个input用于附加autocomplete控件 12. var input = $("<input type='text' id='" + id + "_inbox' class='bbit-usbox-box' />").appendTo(inc); 13. //设置样式,并把input添加到对象中 14. me.addClass("bbit-usbox").width(def.width).append(inc); 15. //给input注册autocomplete功能,并设置回调函数 16. input.autocomplete(def.urlOrData, co).result(function(event, data, formatted) { 17. $(this).val("");//选择人员了则把输入框自己清空 18. additem(this, data);//生成小方块 19. }); 20. //注册一个自定义的事件,事件名addboxitem 21. me.bind("addboxitem", function(e,data) { additem(input,data); }); 22. function additem(inc, data) { 23. //小方块的模板替换成正确的值 24. var tp = $(temp.replace(/\$\{([\w]+)\}/g, function(s1, s2) { 25. if (s2 == "text") { 26. return data[0]; //返回的第一个值是displayname 27. } 28. else if (s2 == "value") { 29. return data.join("|");//其他全部放到input[type=hidden]中 30. } 31. else { 32. return s1; 33. } 34. })); 35. //触发小放开的click事件,并且在内部查找删除按钮,注册点击事件,jQuery的链式哦 36. tp.click(def.clickItem).find("img.bbit-usbox-del").click(removeitem); 37. //把小方块放到input之前! 38. $(inc).parent().before(tp); 39. if (def.addItem) { //如果additem存在则触发 40. def.addItem(data); 41. } 42. } 43. //移除的方法 44. function removeitem() { 45. var p = $(this).prev()//获取input[type=hidden],this指向delete img 46. var v = p.val(); 47. var arr = v.split("|"); //拼成一个数组 48. if (def.removeItem) {//触发移除函数 49. def.removeItem(arr); 50. } 51. //小方块移除本身 52. $(this).parent().parent().remove();// 53. } 54. return me; 55. });最后是公开一个函数来方便外面调用additem,如我有一个弹出界面可以一次选择n个人回来,那么就可以调用这个函数了
1. 2.$.fn.addboxitem = function(op) { 3. $(this).trigger("addboxitem", [op]);//想到我在之前注册的自定义事件了吗? 4. };至此我们的控件其实已经分析完成了,但是为了让大家更好的理解这个控件,我把对应的服务器端代码也顺便写写因为Demo是Asp.NET MVC的,那么就以asp.net mvc 为例来讲吧 先来看看Demo的调用代码,urlOrData是个Action,additem和removeitem分别处理将选中的人员添加到指定的隐藏域或从中删除,以便表单提交。
01. 02.$(document).ready(function() { 03. $("#usbox").usbox({ 04. width: 430, 05. urlOrData:"<%=Url.Action("QueryComplete")%>", 06. addItem: function(data) { 07. var t = $("#hdtext").val(); 08. var v = $("#hdvalue").val(); 09. var t1 = t != "" ? t.split(",") : []; 10. var v1 = v != "" ? v.split(",") : []; 11. t1.push(data[0]); 12. v1.push(data[2]); 13. $("#hdtext").val(t1.join(",")); 14. $("#hdvalue").val(v1.join(",")); 15. }, 16. removeItem: function(data) { 17. var t = $("#hdtext").val(); 18. var v = $("#hdvalue").val(); 19. var t1 = t.split(","); 20. var v1 = v.split(","); 21. var index = -1; 22. for (var i = v1.length - 1; i >= 0; i--) { 23. if (data[2] == v1[i]) { 24. index = i; 25. break; 26. } 27. } 28. if (index > -1) { 29. t1.splice(index, 1); 30. v1.splice(index, 1); 31. $("#hdtext").val(t1.join(",")); 32. $("#hdvalue").val(v1.join(",")); 33. } 34. 35. } 36. }); 37. var tempdata = ["假正经哥哥", "xuanye","001"]; 38. $("#usbox").addboxitem(tempdata); 39. });HTML代码
1.<div id="usbox" class="bbit-usbox"> 2.</div> 3.<input id="hdtext" type="text"/> 4.<input id="hdvalue" type="text"/> 5.输入框在实际项目中可能是隐藏域,默认我加上了假正经哥哥再来看看Action的代码,默认接受两个参数,一个是q,即input中的输入框,一个是限制条数(默认是10,可通过修改complete的参数来改变)
01. 02.public ContentResult QueryComplete(string q, int limit) 03. { 04. string ret = ""; 05. if (q != "" && limit >0) 06. { 07. //根据关键字搜索数据库或缓存,这个就比较简单不深入了 08. List<Person> list=_respository.QueryCompletePerson(q, limit); 09. if (list != null) 10. { 11. StringBuilder sb = new StringBuilder(); 12. foreach (Person person in list) 13. { 14. //以|分割的数据格式,可以是多个,这里是三个。当然也可以吧ID作为特殊的 15. sb.AppendLine(person.FullName + "|" + person.PY+"|"+person.ID); 16. } 17. ret = sb.ToString(); 18. } 19. } 20. return Content(ret); 21. }本示例的地址是:http://jscs.cloudapp.net/ControlsSample/usbox
另外抱怨一下:部署到window azure 真的是有点麻烦哟。。搞了几次
最后 你的支持就是我继续写作的动力!