說在前頭css
因爲原生的select比較醜陋,並且在不一樣瀏覽器下的表現不盡相同;因此爲了保持與實際項目中的ul風格保持一致,須要開發一款相似的組件。html
目前該組件已基本開發結束(還會用到上一篇博客的Widget抽象類,同時新增了幾個方法);但仍有一個問題,我也是剛發現的,目前尚未比較好的方案,後續找到好的方案會已博客形式指出。瀏覽器
重點app
css代碼:
dom
.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代碼:ssh
/** *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新增了一個方法ide
/** *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代碼this
$(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事件會執行兩次)
如今尚未想到比較好的解決方案。
後面想到了,會告訴你們的。敬請期待!
還有以前一篇博客的聯想輸入插件還有不少優化空間;有興趣的能夠優化,並告知我,相互進步,謝謝。