本身開發的select組件(經過input+li等 模擬select下拉效果)

說在前頭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事件會執行兩次)

如今尚未想到比較好的解決方案。

後面想到了,會告訴你們的。敬請期待!



還有以前一篇博客的聯想輸入插件還有不少優化空間;有興趣的能夠優化,並告知我,相互進步,謝謝。

相關文章
相關標籤/搜索