说在前头
由于原生的select比较丑陋,而且在不同浏览器下的表现不尽相同;所以为了保持与实际项目中的ul风格保持一致,需要开发一款类似的组件。
目前该组件已基本开发结束(还会用到上一篇博客的Widget抽象类,同时新增了几个方法);但仍有一个问题,我也是刚发现的,目前还没有比较好的方案,后续找到好的方案会已博客形式指出。
重点
css代码:
.u-select{
width:120px;
height: 30px;
line-height: 30px;
border: 1px #DBDBDB solid;
}
.u-selector{
position: absolute;
top: 30px;
width: 120px;
padding: 0px;
border: 1px #DBDBDB solid;
display: none;
}
.u-opt{
list-style: none;
height: 20px;
line-height: 20px;
cursor: pointer;
}
.u-opt:hover{background:#DBDBDB; }
.selectorContainer,.selectorContainer2{ display: inline-block; position: relative;margin: 0px 10px;}//父元素请使用相对定位
.selector-icon{
background: url(../img/down-icon.png) center center;
width: 9px;
height: 5px;
display: inline-block;
right: 10px;
position: absolute;
top: 15px;
}
插件主要js代码:
/** *Uselect组件 create by jiaxiangjun 2015-9-27 * */ (function (w) { function Uselect() { if(this instanceof Uselect) { this.config = { container:null,//组件的父容器 skin:null,//皮肤 readOnly: true,//输入框是否只读 maxCount:5,//最大展示数量,超过时,出现滚动条 selectHandler:null,//选中回调 dataType:0,//数据类型0,1 data:"",//数据,如果自定义拼装,请使用li标签 top:15,//下拉距顶位置,主要用于微调 width:120,//input,以及下拉的宽度 liHeight:30//下拉li的高度 }; this.isShow = false;//局部变量,仅限组件内部使用 } else { return new Uselect(); } } Uselect.prototype = new Widget(); //隐藏组件 AutoComplete.prototype.hide = function(config) { config.container && config.container.hide(); } //销毁组件 AutoComplete.prototype.destroy = function(config) { config.container && config.container.empty(); } Uselect.prototype.show = function(config) { var CFG = $.extend(this.config, config);//合并对象 if(!CFG.container) { consle.log("组件的父容器为空"); return; } this.renderUI(CFG); this.syncUI(CFG); this.bindUI(CFG); return this; }; Uselect.prototype.renderUI= function(CFG) { var listStr = constrStr(CFG); CFG.container.append(listStr); var selector = CFG.container.find(".u-selector"); CFG.container.find("input").css("width", CFG.width); selector.find("li").css({"height":CFG.liHeight,"lineHeight":CFG.liHeight + "px"}); "" !== CFG.skin && selector.addClass(CFG.skin);//皮肤 var maxHeight = "none"; var overflowY = "hidden"; //如果设置了最大展示数量,滚动显示 if(CFG.maxCount) { var liList = selector.find("li"); var liLen = liList.length; if (liLen > CFG.maxCount) {//如果设置的最大数量大于结果集的总数量,无限下拉 var liHeight = liList.eq(0).height(); maxHeight = liHeight*CFG.maxCount; overflowY = "scroll"; } } selector.css({ "top":CFG.top, "width":CFG.width, "maxHeight":maxHeight, "overflowY":overflowY }); } Uselect.prototype.syncUI= function(CFG) { var selector = CFG.container.find(".u-selector"); CFG.container.find("input").css("width", CFG.width); selector.find("li").css({"height":CFG.liHeight,"lineHeight":CFG.liHeight + "px"}); "" !== CFG.skin && selector.addClass(CFG.skin);//皮肤 var maxHeight = "none"; var overflowY = "hidden"; //如果设置了最大展示数量,滚动显示 if(CFG.maxCount) { var liList = selector.find("li"); var liLen = liList.length; if (liLen > CFG.maxCount) {//如果设置的最大数量大于结果集的总数量,无限下拉 var liHeight = liList.eq(0).height(); maxHeight = liHeight*CFG.maxCount; overflowY = "scroll"; } } selector.css({ "top":CFG.top, "width":CFG.width, "maxHeight":maxHeight, "overflowY":overflowY }); Uselect.prototype.bindUI = function(CFG) { var that = this; //选择下拉选项 CFG.container.find(".u-selector li").on("click", function(event) { if(CFG.selectHandler) { CFG.selectHandler(); } that.fire("select", $(this)); CFG.container.find(".u-selector").hide(); }); CFG.container.blur(function() { console.log("blur"); }) $("body").on("click", function(event) { var e = event || window.event; var target = e.target || e.srcElement; //点击输入框区域,下拉收缩展开效果 if(CFG.container[0].contains($(target)[0])) {//很多人不知道这个方法,主要是组件被一个页面多处使用时,冲突 if(!that.isShow) { if(CFG.clickHandler) { CFG.clickHandler(); } that.fire("click", $(this)); CFG.container.find(".u-selector").show(); that.isShow = true; return; } CFG.container.find(".u-selector").hide(); that.isShow = false; return; } //点击时,如果isshow为true,收缩下拉 if(that.isShow) { CFG.container.find(".u-selector").hide(); that.isShow = false; } }); } function constrStr(CFG) { var data = CFG.data; var listStr = '<input type="text" class="u-select"'+ (CFG.readOnly ? "readonly" :"") +' index=""><i class="selector-icon"></i><ul class="u-selector">'; if(0 === CFG.dataType) { listStr += CFG.data; return listStr + "<ul>"; } if(1 === CFG.dataType) { var len = data.length; for (var i = 0; i < len; i++){ listStr += '<li class="u-opt" index="' + data[i].index + '">' + data[i].value + '</li>'; } } return listStr + "<ul>";; } w.Uselect = Uselect; })(window);
公用抽象类widget新增了一个方法
/** *Widget抽象类 create by jiaxiangjun 2015-9-20 * modify by jiaxiangjun 2015-9-27 * @return {[Widget抽象类]} */ (function(w){ 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](data); } return this; } }, //销毁函数 destroy:function(argument) { // to do smething }, //隐藏函数 hide:function(argument) { // to do smething }, //dom操作相关 renderUI:function(argument) { // to do smething }, //设置组件属性相关 syncUI:function(argument) { // to do smething }, //事件绑定相关 bindUI:function(argument) { // to do smething } } w.Widget = Widget; })(window);
使用示例:
html代码
<div class="selectorContainer"> </div> <div class="selectorContainer2"> </div>
js代码
$(function() { var config = { container:$(".selectorContainer"), dataType:1, data:[{index:1,value:"scott"}, {index:2,value:"Jhon"}, {index:3,value:"Tom"}, {index:4,value:"scott4"}, {index:5,value:"scott5"}, {index:6,value:"scott6"}, {index:7,value:"scott7"}, {index:8,value:"scott8"}, {index:9,value:"scott9"} ], readOnly:false, top:17 }; var uselect = (new window.Uselect()).show(config); uselect.on("select", function(data) { // console.log(data); alert("select"); }).on("click", function(data) { // console.log(data); alert("click"); }); var config2 = { container:$(".selectorContainer2"), dataType:1, data:[{index:1,value:"scott2"},{index:2,value:"Jhon2"}, {index:3,value:"Tom2"}], readOnly:true, width:240, top:17 }; var uselect2 = (new window.Uselect()).show(config2); uselect2.on("select", function(data) { // console.log(data); alert("select2"); }).on("click", function(data) { //console.log(data); alert("click2"); }); });
说在最后
开发过程中发一下了一个严重的问题:
如果一个页面不知一处使用该组件;将会出问题的。
之前,在绑定事件和设置css时,我没有考虑到这一点,在本次开发中我特意在绑定事件和设置css等时,使用CFG.container来隔离多个相同组件的冲突问题。
但是,很不幸,又遇到了一个问题,我们这个地方使用了on,fire来绑定和执行自定义事件;但是由于多个相同组件的相同事件的type相同(比如2个组件的click自定义事件的type都为click;但在抽象类widget中执行时,一个组件的click事件会执行两次)
现在还没有想到比较好的解决方案。
后面想到了,会告诉大家的。敬请期待!
还有之前一篇博客的联想输入插件还有很多优化空间;有兴趣的可以优化,并告知我,相互进步,谢谢。
转载于:https://blog.51cto.com/shuizhongyue/1698291