改造 Combo Select支持服务器端模糊搜索
项目中使用了 combo select,为缺省的select增加模糊搜索的功能,一直运行得很好。
1 碰到的问题
但最近碰到一个大数据量的select:初始化加载的数据项有2000多个。我们采用的是ajax读取所有的option json,并由js在浏览器中遍历并最终生成完整的html。当数据量变大的时候,ajax读取数据和浏览器处理数据都会有比较明显的损耗,页面初始化时需要较长时间,降低了用户友好度。
2 备选解决方案
大家简单分析了一下这个问题,想到了三种可能的解决方案。
2.1 修改数据结构
目前的同级数据有2000多条,数据从逻辑上可以拆分为两级结构。这样,将数据拆分为两级结构后,使用两个联动Select,能大大减少每个select加载的option数量。
2.2 使用redis缓存数据
因为采用分布式部署,这些数据实际上经过了多次服务器之间的传输。数据量大,每一级传输耗时增加,导致最终的耗时难于接受。
可以在api server端利用nosql对数据进行缓存,能在一定程度上降低耗时。
2.3 修改combo select插件
从前端入手,select只显示少量数据,当用户输入关键字进行搜索时,实时从服务器加载。这种方式增加了调用次数,但可以大大降低数据量,缩短页面加载的耗时。
三种方案,都能在一定程度上解决问题。我们决定先从combo select插件尝试,如果达不到效果,再考虑redis缓存或修改数据结构的方案。
3 Combo Select代码分析
网址 https://github.com/PebbleRoad/combo-select ,感谢提供如此优秀的一个插件。
3.1 基本用法
首先在页面中构建一个select,并初始化option数据,然后调用脚本
|
$("#selectId").comboSelect(); |
其他更复杂的功能,请自行前往官网学习。
3.2 html结构

Combo Select在执行时,在原 select 外层套了一个 <div class=”combo-select”>,然后在select后面添加了三个element。
div.combo-arrow,是下拉箭头
ul.combo-dropdown是用来显示的下拉列表
input.combo-input 是用来输入模糊搜索内容的输入框
并通过修改原 select 的属性,隐藏掉。
3.3 js数据模型
combo select初始化时,经过一系列代码,最终构造几个属性:
$container : 生成一个新的div,将原来的select和新生成的ul等都放在其中。
$el : 初始的select element
$options : 所有的option 数据
$dropdown : 生成的 ul.combo-dropdown 对象
$items : 所有的options转成 li 格式后的数据。
下图是数据模型和html元素之间的对应关系。

3.4 插件初始化
在js插件的代码function Plugin ( element, options )会完成插件的初始化,根据select当前的数据,完成html元素的调整,以及js数据模型的初始化。初始化流程如下

3.5 模糊查询的逻辑
当用户在input中输入文字的时候,会触发 keydown和keyup事件,在keyup事件中,对 $items中的数据依次进行匹配,设置 visible属性,实现部分数据的展示。

在这个过程中,原始的select($el)及其所有的options($options)没有变化,下拉列表的变化,主要是将ul.li($items)设置为可见或不可见。
4 修改为Server端实时查询方案
整个修改方案,分别从Server API、js组件、前端调用三方面解决。
4.1 Server API 修改
Server端需要提供根据名称进行模糊搜索的接口。不赘述,需要注意的是返回数据要设置最大条数。避免根据查询条件返回了大量的数据,就失去了解决的优势。
限制最大条数后,需要跟产品介绍清楚这个实现逻辑,如果用户输入的关键字区分度不大时,可能无法查到真正需要的数据;此时需要用户输入更具有区分度的关键字。
4.2 ComboSelect组件修改
4.2.1 修改方案
修改keyup事件时的逻辑:原来是分别设置ul.li是否可见,修改为重新加载select的所有options,并根据options重建$items,并设置为所有ul.li都是可见的。

4.2.2 为组件新增几个参数
|
entity: 'entity', itemName: 'itemName', curItemField: 'curItemCode', curItemValue: '', curItemName: '', url: '', limit: 7 |
- entity: 当前处理的数据类型,这是为了适应不同api返回的json定义的差异。更好的办法是要求所有数据类型使用相同的属性名;变通的方案就是增加这个entity,在js上做差异化处理。这样就减少了改造的通用性。
- itemName: 调用api时需要的用户输入值的参数名
- curItemField:在html中,item的input名称
- curItemValue: 当前已选中数据的value
- curItemName: 当前已选中数据的title
- limit: 服务器api模糊搜索返回值的分页大小
4.2.3 修改 _filter() 方法实现服务器端模糊查询
修改了原组件的这个方法,判断是否设置了服务器端刷新的url。如果没设置,沿用原来的逻辑;如果设置了,根据用户输入进行模糊查询,并重新生成浏览器中被隐藏的select的所有options,并更新到$dropdown中。
|
if(self.settings.url != ''){ // 准备调用api需要的json数据 var self = this; var ajaxData = { "paging": true, "offset": 0, "limit": self.settings.limit }; if(self.settings.itemName != '' && needle != ''){ ajaxData[self.settings.itemName] = needle; } if(self.settings.curItemField != '' && self.settings.curItemValue != ''){ ajaxData[self.settings.curItemField] = self.settings.curItemValue; } // 从服务器查询数据 $.ajax({ url : self.settings.url, type : 'post', data: ajaxData, success : function(data) { var obj = $.parseJSON(data); // 先删掉select原来的数据,并遍历查询结果生成option添加到select中 var dropdownHtml = '', k = 0, p = ''; self.$el.empty(); self.$el.append("<option value=''>请选择</option>"); var confirmedValue; self.$dropdown.html("<li class='option-item' data-index='0' data-value=''>请选择</li>"); for (var i = 0; i < obj.length; i++) { var itemCode; var itemName; var itemExtraCode; if(self.settings.entity == 'entity'){ itemCode = obj[i].entityCode; itemName = obj[i].entityName; itemExtraCode = obj[i].entityShortName; }else{ itemCode = obj[i].itemCode; itemName = obj[i].itemName; itemExtraCode = itemCode; } // 生成select option var oneOption = $("<option></option>"); $(oneOption).val(itemCode); $(oneOption).html(itemName); if(itemCode == self.settings.curItemValue || itemExtraCode == self.settings.curItemValue || itemName == self.settings.curItemName){ $(oneOption).attr("selected", "selected"); self.settings.curItemValue = itemCode; confirmedValue = itemCode; } self.$el.append(oneOption); if(confirmedValue != undefined && confirmedValue != ''){ self.$el.val(confirmedValue); } // 生成$dropdown 中的li var oneItem = $("<li></li>"); $(oneItem).attr("class",this.disabled? self.settings.disabledClass : "option-item"); $(oneItem).attr("data-index", (i+1)); $(oneItem).attr("data-value", itemCode); $(oneItem).html(itemName); self.$dropdown.append(oneItem); } // 为$items 重新赋值 self.$items = self.$dropdown.children(); // 触发后续的open方法 self.$container.trigger('comboselect:open') } }); } |
4.2.4 修改 init() 实现首次加载
代码类似_filter()。应该要独立出一个方法来在两个方法中调用,没做。
|
init: function () { var self = this; if(self.settings.url != ''){ //动态刷新代码 ... ... }else{ // 组件的原始逻辑 self._construct(); self._events(); } }, |
4.3 前端调用
4.3.1 html代码中添加参数
使用 comboselect- 前缀,如
|
<select class="list-filedV" id="entityCode" name="entityCode" comboselect-entity="entity" onchange="getBranch('')"> </select> <input type='hidden' name='entityName' id='entityName'> |
4.3.2 js生成没有缺省值的combobox
在js代码中完成初始化,代码
|
//获取数据 function getEntityData(){ $("#entityCode").comboSelect({ "itemName": "entityName", "url": contextPath+"/new/dictionary/searchEntityData.ajax", "limit": 7 }); } |
4.3.3 js生成有缺省值的combobox
在编辑界面比较常见
|
//获取数据_修改 function getEntityDataUp(curEntityCode, curEntityName){ $("#entityCode").comboSelect({ "itemName": "entityName", "curItemField": "includeEntityCode", "curItemValue": curEntityCode, "curItemName": curEntityName, "url": contextPath+"/new/dictionary/searchEntityData.ajax", "limit": 7 }); } |
5 降低调用服务器频度
注意看_keyup 的代码,每次按键(不包括该函数忽略的特殊字符),每次都会刷新数据。如果是在浏览器内部进行数据过滤,问题还不明显。但每次模糊查询都通过服务器查询,就会带来大量的api访问。
5.1 修改方案
在_keyup()中,调用_delayFilter(),由它触发前面修改后的 _filter()方法。

5.2 代码 _delayFilter()
|
this.filterTimer = 0; _delayFilter: function(search){ if(this.filterTimer > 0){ clearTimeout(this.filterTimer); this.filterTimer = 0; } var self = this; this.filterTimer = setTimeout(function(){ self._filter(search); }, 500); }, |
改造 Combo Select支持服务器端模糊搜索的更多相关文章
- jquery Combo Select 下拉框可选可输入插件
Combo Select 是一款友好的 jQuery 下拉框插件,在 PC 浏览器上它能模拟一个简单漂亮的下拉框,在 iPad 等移动设备上又能回退到原生样式.Combo Select 能够对选项进行 ...
- Combo Select – jQuery可搜索下拉框插件
今天用到另一款jq插件..插件就是无脑,,引入相关文件,开始撸吧 引入相关文件: <link href="~/Scripts/combo-select/combo.select.css ...
- 改造jQuery-Tagit 插件支持中文全角的逗号和空格
jQuery 的 tagit 插件效果还是不错的,今天用到该插件但发现不能自定义标签分隔符,只能是英文半角逗号或空格,于是想改造下 效果: 先研究了一番插件的代码,发现并不能通过插件自身的扩展方法来实 ...
- select 支持宽高(高度有兼容问题);
<select size=1(默认) size=2 没有下拉效果> <option selected>12</option> <option selected ...
- I/O复用(select)——回声服务器端/客户端
一.select 使用select函数可以将多个文件描述符集中到一起统一监视,监视事件如下: 是否存在待读取数据. 是否可传输无阻塞传输数据. 是否发生异常. 将关心上述3种事件的文件描述发分别注册到 ...
- select支持多查询,获取分页count
在我们使用mybatis 时,当我们根据分页去查询数据集时,需要传入的参数有page(页码)和size(每页的条数),而我们期望获取到的数据有,小于或等于size的list数据集,同时我们还需要得到一 ...
- Sqoop-1.4.6 Merge源码分析与改造使其支持多个merge-key
Sqoop中提供了一个用于合并数据集的工具sqoop-merge.官方文档中的描述可以参考我的另一篇博客Sqoop-1.4.5用户手册. Merge的基本原理是,需要指定新数据集和老数据集的路径,根据 ...
- c# datatable.select() 支持group by
不支持group by ,支持order by.如果要使用group by的话,可以使用linq,这是C#3.0的内容.给你个示例static void Main(string[] args){ Da ...
- jquery combo.select. 下拉选择插件
演示地址:http://www.dowebok.com/demo/179/index5.html 引入js.css 即可使用,效果如图所示: 这个插件的好处是可以在输入框里面输入数据 自动检索内容. ...
随机推荐
- Winform控件Tag使用规范
背景 Tag在WinForm控件中经常被用来存储临时数据,类型为object,但是当程序中多个地方使用到Tag时,容易造成Tag使用的混乱,Tag是如此重要的一个属性,应该要好好考虑下如何有效的使用T ...
- 玩转html
简介 CSS 是什么? CSS是Cascading Style Sheets的简称,中文称为层叠样式表. 作用 能够对网页中元素位置的排版进行像素级精确控制,支持几乎所有的字体字号样式,拥有对网页对象 ...
- 装饰模式(Decorator)
装饰模式(Decorator) 顾名思义,装饰模式就是给一个对象增加一些新的功能,而且是动态的,要求装饰对象和被装饰对象实现同一个接口,装饰对象持有被装饰对象的实例,关系图如下: Source类是被装 ...
- centos7 简单搭建lnmp环境
1:查看环境: 1 2 [root@10-4-14-168 html]# cat /etc/redhat-release CentOS release 6.5 (Final) 2:关掉防火墙 1 [r ...
- windows10 使用gitblit搭建git服务器
今天在win10上使用gitblit搭建git服务器时被坑了下,因为安装的java9出现不兼容问题,果断卸载重装了jdk8.废话不多说直接进入正题吧: 第一章 前言 使用gitblit搭建git se ...
- google guava cache缓存基本使用讲解
代码地址:https://github.com/vikde/demo-guava-cache 一.简介 guava cache是google guava中的一个内存缓存模块,用于将数据缓存到JVM内存 ...
- labview生成可执行文件
labview生成可执行文件可以分为两种情况. 第一种,是电脑中有labview软件开发环境的情况 第二种,是电脑中没有安装labview软件开发环境 下面是一个简单的labview代码: 程序解释: ...
- 导入mysql数据的时候提示Field * doesn't have a default value解决方法
项目使用django+mysql 在linux中使用的是mysql5.7,导入数据提示:Field * doesn't have a default value 想要解决问题就需要知道在mysql5. ...
- 剑指Offer_12_矩阵中的路径(参考问题:马踏棋盘)
题目描述 请设计一个函数,用来判断在一个矩阵中是否存在一条包含某字符串所有字符的路径.路径可以从矩阵中的任意一个格子开始,每一步可以在矩阵中向左,向右,向上,向下移动一个格子.如果一条路径经过了矩阵 ...
- HDU 1013.Digital Roots【模拟或数论】【8月16】
Digital Roots Problem Description The digital root of a positive integer is found by summing the dig ...