JS列表的下拉菜单组件(仿美化控件select)
今天是农历23 也是⼩年,在这祝福⼤家新年快乐!今天给⼤家分享的是:JS列表的下拉菜单组件,因为⽬前项⽬正好要⽤到这个,所以提前研究了下,看到KISSY也有这么⼀个组件,所以⾃⼰也封装了⼀个,KISSY
KISSY组件名字叫 "⼀个解决⼤数据列表渲染效率的下拉菜单组件。", 他对这个组件做了⼀次⼩优化。(假如服务器返回10000条数据或者更多的话,那么我们前端⼀次性操作10000条数据的话很会影响性能,他们做的优化是:将数组拆分,根据浏览器本⾝的脚本执⾏能⼒进⾏分批渲染。),但是⽬前kissy demo上有加载2000条数据的demo,在⽕狐下还是会有卡住的现象,如果稍不好的话有可能会导致浏览器重启的可能。⽽我今天做的demo和他们的功能类似,但是唯⼀不同点就是:假如返回10000条数据的话我没有对数组分批渲染,⽽是循环10000次把数据保存到⼀个变量⾥然后⼀次性动态加载进来,或许这么做和他们那种操作效率可能会低那么点(具体的我没有测试过)。所以我今天的标题没有和他们那样⼀起叫。所以今天的标题上:"JS列表的下拉菜单组件". ⾸先要说明的是:⼀般的需求肯定是满⾜的,⼀个下拉框也不可能有那么多数据(⼀般情况下!)。
下⾯是我做的demo(JS列表的下拉菜单组件)。JSFiddle地址如下:
满⾜的基本功能是:⼀个基本下拉框,但是他与下拉框不同的是:他既可以输⼊精确匹配到某⼀项,也
可以点击下拉,也⽀持键盘上下移操作。但同时当我在输⼊框输⼊时候没有匹配到某⼀项时候,点击⽂档document 那么下拉框隐藏掉,input值为空。同时且⽀持静态数据渲染⼜⽀持post请求渲染数据。
如上⾯配置:其中dataSource如果初始化为空数组的话,那么直接在内部发post请求渲染数据,否则的话也可以渲染静态数据:如下
dataSource: [
{text: "列表项1", value: 1},
{text: "列表项2", value: 2},
{text: "列表项3", value: 3},
{text: "列表项4", value: 4},
{text: "列表项5", value: 5},
{text: "列表项6", value: 6},
{text: "列表项7", value: 7},
{text: "列表项8", value: 8},
html下拉菜单的制作方法{text: "列表项9", value: 9},
{text: "列表项10", value: 10},
{text: "列表项11", value: 11}
]
如果dataSource 的长度⼤于0 的话那么他会按照静态数据渲染,不会发post请求否则的话 (如果数组为空,⽀持发post请求) 去渲染数据。
对外提供的⽅法有:
setValue()
在外部实例话后可以调⽤此⽅法设置初始化值。⽐如demo页⾯设置的格式如下:
// 设置初始化选择项。
selectedItem: {
value: "4",
text: "列表项4"
}
getValue(); 获取输⼊框的值。
⾸先初始化init⽅法:代码如下:
init: function(options) {
var self = this,
_config = fig,
_cache = self.cache;
$('.drop-trigger').css({"left":_config.inputWidth - 20 + 'px'});
/*
* ⿏标点击输⼊框时渲染数据
*/
$(_config.inputElemCls).each(function(index,item){
// 对input定义宽度其⽗节点div也是根据input宽度定义的。
$(item).css({'width':_config.inputWidth});
var tagParent = $(item).closest(_config.parentCls);
$(tagParent).css({'width':_config.inputWidth});
$(item).bind('keyup',function(e){
e.preventDefault();
var targetVal = $.trim($(this).val()),
keyCode = e.keyCode,
elemHeight = $(this).outerHeight();
var targetParent = $(this).closest(_config.parentCls);
$(targetParent).css({'position':'relative'});
// 删除标识
self._removeState(targetParent);
var curIndex = self._keyCode(keyCode);
if(curIndex > -1) {
// 除了列举那些键码不发请求
self._keyUpAndDown(targetVal,e,targetParent);
}else {
// 渲染数据
self._renderHTML(targetVal,targetParent,elemHeight);
// 如果值为空的话那么下拉列表隐藏掉
if(targetVal == '') {
self._hide(targetParent);
_cache.currentIndex = -1;
_cache.oldIndex = -1;
}else {
self._show(targetParent);
}
}
});
var targetParent = $(item).closest(_config.parentCls);
$(_config.selectCls,targetParent).unbind('click');
$(_config.selectCls,targetParent).bind('click',function(){
var targetVal = $.trim($(item,targetParent).val()),
elemHeight = $(item,targetParent).outerHeight();
// 渲染数据
self._renderHTML(targetVal,targetParent,elemHeight);
});
});
/
*
* 点击document 不包括input输⼊框时候隐藏下拉框
*/
$(document).unbind('click');
$(document).bind('click',function(e){
e.stopPropagation();
var target = e.target,
targetParent = $(target).closest(_config.parentCls);
var reg = _place(/^\./,''),
selectCls = _place(/^\./,'');
if($(target,targetParent).hasClass(reg) || $(target,targetParent).hasClass(selectCls)) {
return;
}else {
self._hide(targetParent);
}
$(_config.inputElemCls).each(function(index,item){
if(!$(item).hasClass('state')) {
$(item).val('');
}
});
});
},
其中上⾯的// 对input定义宽度其⽗节点div也是根据input宽度定义的。 $(item).css({'width':_config.inputWidth}); var tagParent = (item).closest(c onfig.parentCls);(tagParent).css({'width':_config.inputWidth});
这⼏句代码的意思是:
1 初始化时候动态的设置input框的宽度其中⽗元素的宽度也是根据input宽度来设置的,且下⾯的代码下拉框的宽度也是根据input宽度渲染的。
2. 分别对input绑定keyup事件及下拉框⼩箭头绑定点击click事件做相应的操作。⾸先keyup操作时,调⽤这个⽅法 self._removeState(targetParent);删除相应的class (state),因为下⾯有当我⽤键盘下拉移到某⼀项时或者⿏标点击下拉框某⼀项时候会增加class(state),这样做的⽬的是当我点击document时候会判断input输⼊框是否有这个class(state),如果没有的话清空input输⼊框的值。否则的话,反之!接着判断键码 var curIndex = self._keyCode(keyCode);这个⽅法.⽬的是为了当⽤上⾯那些键盘在输⼊框操作时候不发post请求(也就是说除了那些常见的键码外发post请求)。如果键码等于40的话那么执⾏下移操作,如果等于38的话那么是上移操作。否则的话调⽤_renderHTML⽅法渲染数据。(同样当点击下拉⼩箭头时候也调⽤此⽅法渲染数据。),下⾯的代码是点击⽂档document时候⾸先判断是否是输⼊框或者是⼩箭头的话,下拉框不做任何处理,否则的话隐藏掉。点击document时候做了另外⼀件事,就是说如果此input没有state类名时候清空输⼊框数据。
_renderHTML⽅法代码如下:
_renderHTML: function(targetVal,targetParent,elemHeight) {
var self = this,
_config = fig,
_cache = self.cache;
// 如果已经渲染了先清空数据
if($('ul',targetParent).length > 0) {
self._show(targetParent);
$('ul',targetParent).html('');
}
if(_lyCreate) {
$(targetParent).append($('<ul></ul>'));
_lyCreate = false;
}
var html = '';
/*
* 如果设置了静态数据的话那么直接使⽤静态数据否则的话发post请求
* 由于代码没有⽤模板所以直接for循环
*/
if(_config.dataSource.length > 0) {
for(var i = 0, ilen = _config.dataSource.length; i < ilen; i+=1) {
if(_config.dataSource[i].text.indexOf(targetVal) >= 0) {
html+= '<li class="dropmenu-item p-index'+i+'" data-value="'+_config.dataSource[i].value+'" data-title="'+_config.dataSource[i].text+'">'+_config.dataSource[i].text+'</li>'; }else {
$('ul',targetParent).css({'border':'none'});
}
}
$('ul',targetParent).append(html);
}else {
// 发post请求
/**$.ajax({
type: 'post'
});**/
// 假如返回的数据如上所⽰的格式
var result = [
{text: "列表项1", value: 1},
{text: "列表项2", value: 2},
{text: "列表项3", value: 3},
{text: "列表项4", value: 4},
{text: "列表项5", value: 5},
{text: "列表项6", value: 6},
{text: "列表项7", value: 7},
{text: "列表项8", value: 8},
{text: "列表项9", value: 9},
{text: "列表项10", value: 10},
{text: "列表项11", value: 11}
];
for(var i = 0, ilen = result.length; i < ilen; i+=1) {
if(result[i].text.indexOf(targetVal) >=0) {
html+= '<li class="dropmenu-item p-index'+i+'" data-value="'+result[i].value+'" data-title="'+result[i].text+'">'+result[i].text+'</li>';
}else {
$('ul',targetParent).css({'border':'none'});
}
}
$('ul',targetParent).append(html);
}
$('ul',targetParent).css({
"width":_config.inputWidth,
'overflow':'hidden','border':'1px solid #ccc','border-top':'none'});
$('ul,li',targetParent).css({'cursor':'pointer'});
var len = $('li',targetParent).length;
if(len >= 10) {
$('ul',targetParent).css({'height':'220px','overflow':'scroll'});
}else {
$('ul',targetParent).css({'height':'auto','overflow':'hidden'});
}
// hover事件
self._hover(targetParent);
// 渲染后回调函数
_derHTMLCallback && $.isFunction(_derHTMLCallback) && _derHTMLCallback();
// 点击下来框某⼀项
self._clickItem(targetParent);
},
代码做了如下事情:
1. 如果ul已经创建了(只创建⼀次) 则显⽰且清空之前的数据。
2.如果设置了静态数据的话(dataSource.length > 0) 那么直接使⽤静态数据否则的话发post请求.
3. 如果下拉框数据渲染时候长度⼤于10的话添加滚动条,否则的话不添加。
接着就调⽤如下⽅法:
// hover事件
self._hover(targetParent);
// 渲染后回调函数
_derHTMLCallback && $.isFunction(_derHTMLCallback) && _derHTMLCallback();
// 点击下来框某⼀项
self._clickItem(targetParent);
下⾯是所有的代码如下:
HTML依赖的结构如下:
<div class="parentCls">
<div class="drop-trigger"><i class="caret"></i></div>
<input type="text" class="inputElem" autocomplete="off"/>
</div>
其中⽗级元素class默认为 parentCls,可以根据⾃⼰⾃定义如有需要可以根据具体的值进⾏传,input的类名class 默认为inputElem 也可以⾃定义。CSS代码我就不贴了。可以根据⾃⼰的需要⾃⼰写。如有需要或者可以看看JSfiddle源码看看css代码。
下⾯是所有JS代码如下:
/**
* ⼀个解决⼤数据列表渲染效率的下拉菜单组件。
* @author tugenhua
* @time 2014-01-21
*/
function DropList(options) {
parentCls : '.parentCls', // ⽗元素class
inputElemCls : '.inputElem', // 当前input标签input的class
inputWidth : 100, // ⽬标元素的宽度
selectCls : '.caret', // 下来⼩箭头class
hoverBg : 'hoverBg', // ⿏标移上去的背景
isSelectHide : true, // 点击下拉框是否隐藏
timeId : 100, // 默认多少毫秒消失下拉框
// 数据源返回的格式如下:静态数据否则的话(如果数组为空的话) 在内部发post请求
dataSource: [
{text: "列表项1", value: 1},
{text: "列表项2", value: 2},
{text: "列表项3", value: 3},
{text: "列表项4", value: 4},
{text: "列表项5", value: 5},
{text: "列表项6", value: 6},
{text: "列表项7", value: 7},
{text: "列表项8", value: 8},
{text: "列表项9", value: 9},
{text: "列表项10", value: 10},
{text: "列表项11", value: 11}
],
renderHTMLCallback : null, // keyup时渲染数据后的回调函数
callback : null// 点击某⼀项提供回调
};
this.cache = {
onlyCreate : true, // 只渲染⼀次代码
currentIndex : -1,
oldIndex : -1,
timeId : null// setTimeout定时器
};
this.init(options);
}
DropList.prototype = {
constructor: DropList,
init: function(options) {
var self = this,
_config = fig,
_cache = self.cache;
$('.drop-trigger').css({"left":_config.inputWidth - 20 + 'px'});
/*
* ⿏标点击输⼊框时渲染数据
*/
$(_config.inputElemCls).each(function(index,item){
/
/ 对input定义宽度其⽗节点div也是根据input宽度定义的。
$(item).css({'width':_config.inputWidth});
var tagParent = $(item).closest(_config.parentCls);
$(tagParent).css({'width':_config.inputWidth});
$(item).bind('keyup',function(e){
e.preventDefault();
var targetVal = $.trim($(this).val()),
keyCode = e.keyCode,
elemHeight = $(this).outerHeight();
var targetParent = $(this).closest(_config.parentCls);
$(targetParent).css({'position':'relative'});
/
/ 删除标识
self._removeState(targetParent);
var curIndex = self._keyCode(keyCode);
if(curIndex > -1) {
// 除了列举那些键码不发请求
self._keyUpAndDown(targetVal,e,targetParent);
}else {
// 渲染数据
self._renderHTML(targetVal,targetParent,elemHeight);
// 如果值为空的话那么下拉列表隐藏掉
if(targetVal == '') {
self._hide(targetParent);
_cache.currentIndex = -1;
_cache.oldIndex = -1;
}else {
self._show(targetParent);
}
}
});
var targetParent = $(item).closest(_config.parentCls);
$(_config.selectCls,targetParent).unbind('click');
$(_config.selectCls,targetParent).bind('click',function(){
var targetVal = $.trim($(item,targetParent).val()),
elemHeight = $(item,targetParent).outerHeight();
// 渲染数据
self._renderHTML(targetVal,targetParent,elemHeight);
});
});
/*
* 点击document 不包括input输⼊框时候隐藏下拉框
*/
$(document).unbind('click');
$(document).bind('click',function(e){
e.stopPropagation();
var target = e.target,
targetParent = $(target).closest(_config.parentCls);
var reg = _place(/^\./,''),
selectCls = _place(/^\./,'');
if($(target,targetParent).hasClass(reg) || $(target,targetParent).hasClass(selectCls)) {
return;
}else {
self._hide(targetParent);
}
$(_config.inputElemCls).each(function(index,item){
if(!$(item).hasClass('state')) {
$(item).val('');
}
});
});
},
// 键码判断
_keyCode: function(code) {
var arrs = ['17','18','38','40','37','39','33','34','35','46','36','13','45','44','145','19','20','9'];
for(var i = 0, ilen = arrs.length; i < ilen; i++) {
if(code == arrs[i]) {
return i;
}
}
return -1;
},
_renderHTML: function(targetVal,targetParent,elemHeight) {
var self = this,
_config = fig,
_cache = self.cache;
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论