前言
擴展自$.fn.validatebox.defaults。使用$.fn.combo.defaults重寫默認值對象。下載該插件翻譯源碼
自定義下拉框顯示一個可編輯的文本框和下拉面板在html頁面。這是構建其他復雜的組合部件(如:combobox,combotree,combogrid等)之前需要構建的最基本的組件
依賴關系
-
validatebox
-
panel
源碼

/** * jQuery EasyUI 1.3.2 * *翻譯:qq 1364386878 自定義下拉框 */ (function ($) { //調整組件寬度 function _resize(jq, width) { var opts = $.data(jq, "combo").options; var combo = $.data(jq, "combo").combo; var panel = $.data(jq, "combo").panel; if (width) { opts.width = width; } if (isNaN(opts.width)) { var c = $(jq).clone(); c.css("visibility", "hidden"); c.appendTo("body"); opts.width = c.outerWidth(); c.remove(); } combo.appendTo("body"); var combotext = combo.find("input.combo-text"); var comboarrow = combo.find(".combo-arrow"); var arrowWidth = opts.hasDownArrow ? comboarrow._outerWidth() : 0;//下拉箭頭寬度 combo._outerWidth(opts.width)._outerHeight(opts.height); combotext._outerWidth(combo.width() - arrowWidth); combotext.css({ height: combo.height() + "px", lineHeight: combo.height() + "px" }); comboarrow._outerHeight(combo.height()); panel.panel("resize", { width: (opts.panelWidth ? opts.panelWidth : combo.outerWidth()), height: opts.panelHeight }); combo.insertAfter(jq); }; //是否顯示下拉箭頭 function setDownArrow(jq) { var opts = $.data(jq, "combo").options; var combo = $.data(jq, "combo").combo; if (opts.hasDownArrow) { combo.find(".combo-arrow").show(); } else { combo.find(".combo-arrow").hide(); } }; // 渲染組件 function renderCombo(target) { $(target).addClass("combo-f").hide(); var combo = $("<span class=\"combo\"></span>").insertAfter(target); var text = $("<input type=\"text\" class=\"combo-text\">").appendTo(combo);//將text框添加到combo $("<span><span class=\"combo-arrow\"></span></span>").appendTo(combo);//將下來箭頭添加到combo $("<input type=\"hidden\" class=\"combo-value\">").appendTo(combo);//將隱藏域添加到combo以存放combo的value var combpanel = $("<div class=\"combo-panel\"></div>").appendTo("body");//將下來面板添加到body //設置下拉面板 combpanel.panel({ doSize: false, closed: true, cls: "combo-p", style: { position: "absolute", zIndex: 10 }, onOpen: function () { $(this).panel("resize"); } }); var name = $(target).attr("name"); if (name) { combo.find("input.combo-value").attr("name", name); $(target).removeAttr("name").attr("comboName", name); } text.attr("autocomplete", "off");//關閉自動完成 return { combo: combo, panel: combpanel }; }; //銷毀Combo function _destroy(jq) { var textBox = $.data(jq, "combo").combo.find("input.combo-text"); textBox.validatebox("destroy"); $.data(jq, "combo").panel.panel("destroy"); $.data(jq, "combo").combo.remove(); $(jq).remove(); }; //綁定事件 function bindEvents(jq) { var combo = $.data(jq, "combo"); var opts = combo.options; var combo2 = $.data(jq, "combo").combo; var panel = $.data(jq, "combo").panel; var combotext = combo2.find(".combo-text"); var comboarrow = combo2.find(".combo-arrow");//下拉箭頭 $(document).unbind(".combo").bind("mousedown.combo", function (e) { //鼠標點擊combo外,關閉選擇面板 var p = $(e.target).closest("span.combo,div.combo-panel"); if (p.length) { return; } var combopanel = $("body>div.combo-p>div.combo-panel"); combopanel.panel("close"); }); //移除事件處理器 combo2.unbind(".combo"); panel.unbind(".combo"); combotext.unbind(".combo"); comboarrow.unbind(".combo"); //若組件未禁用,添加以下事件處理器 if (!opts.disabled) { combotext.bind("mousedown.combo", function (e) { $("div.combo-panel").not(panel).panel("close"); ////該方法將停止事件的傳播,阻止它被分派到其他 Document節點, e.stopPropagation(); }).bind("keydown.combo", function (e) { switch (e.keyCode) { case 38://小鍵盤上箭頭 opts.keyHandler.up.call(jq); break; case 40://小鍵盤下箭頭 opts.keyHandler.down.call(jq); break; case 13://Enter鍵 e.preventDefault(); opts.keyHandler.enter.call(jq); return false; case 9://Tab鍵 case 27://Esc鍵 hidePanel(jq); break; default: if (opts.editable) { if (combo.timer) { clearTimeout(combo.timer); } combo.timer = setTimeout(function () { var q = combotext.val(); if (combo.previousValue != q) { combo.previousValue = q; $(jq).combo("showPanel"); opts.keyHandler.query.call(jq, combotext.val()); _validate(jq, true); } }, opts.delay); } } }); //下拉箭頭綁定事件 comboarrow.bind("click.combo", function () { if (panel.is(":visible")) { hidePanel(jq); } else { $("div.combo-panel").panel("close"); $(jq).combo("showPanel"); } combotext.focus(); }).bind("mouseenter.combo", function () { $(this).addClass("combo-arrow-hover"); }).bind("mouseleave.combo", function () { $(this).removeClass("combo-arrow-hover"); }).bind("mousedown.combo", function () { }); } }; //showPanel function _showPanel(jq) { var opts = $.data(jq, "combo").options; var combo = $.data(jq, "combo").combo; var panel = $.data(jq, "combo").panel; if ($.fn.window) { //若放在窗口里面,則顯示在窗口之上 panel.panel("panel").css("z-index", $.fn.window.defaults.zIndex++); } panel.panel("move", { left: combo.offset().left, top: getOffsetTop() }); if (panel.panel("options").closed) { panel.panel("open"); opts.onShowPanel.call(jq); } (function () { if (panel.is(":visible")) { panel.panel("move", { left: getOffsetLeft(), top: getOffsetTop() }); setTimeout(arguments.callee, 200); } })(); //* 獲取Left位置 function getOffsetLeft() { var left = combo.offset().left; if (left + panel._outerWidth() > $(window)._outerWidth() + $(document).scrollLeft()) { left = $(window)._outerWidth() + $(document).scrollLeft() - panel._outerWidth(); } if (left < 0) { left = 0; } return left; }; // 獲取TOP位置 function getOffsetTop() { var top = combo.offset().top + combo._outerHeight(); if (top + panel._outerHeight() > $(window)._outerHeight() + $(document).scrollTop()) { top = combo.offset().top - panel._outerHeight(); } if (top < $(document).scrollTop()) { top = combo.offset().top + combo._outerHeight(); } return top; }; }; // 隱藏下拉選擇面板 function hidePanel(jq) { var opts = $.data(jq, "combo").options; var panel = $.data(jq, "combo").panel; panel.panel("close"); opts.onHidePanel.call(jq); }; //校驗值 function _validate(jq, tag) { var opts = $.data(jq, "combo").options; var textBox = $.data(jq, "combo").combo.find("input.combo-text"); textBox.validatebox(opts); if (tag) { textBox.validatebox("validate"); } }; // 設置禁用/啟用組件樣式 function _disable(jq, disabled) { var opts = $.data(jq, "combo").options; var combo = $.data(jq, "combo").combo; if (disabled) { opts.disabled = true; $(jq).attr("disabled", true); combo.find(".combo-value").attr("disabled", true); combo.find(".combo-text").attr("disabled", true); } else { opts.disabled = false; $(jq).removeAttr("disabled"); combo.find(".combo-value").removeAttr("disabled"); combo.find(".combo-text").removeAttr("disabled"); } }; // 清空combo值 function _clear(jq) { var opts = $.data(jq, "combo").options; var combo = $.data(jq, "combo").combo; if (opts.multiple) { combo.find("input.combo-value").remove(); } else { combo.find("input.combo-value").val(""); } combo.find("input.combo-text").val(""); }; //獲取text值 function _getText(jq) { var combo = $.data(jq, "combo").combo; return combo.find("input.combo-text").val(); }; //設置text值 function _setText(jq, text) { var combo = $.data(jq, "combo").combo; combo.find("input.combo-text").val(text); _validate(jq, true); $.data(jq, "combo").previousValue = text; }; //獲取值(多選) function _getValues(jq) { var values = []; var combo = $.data(jq, "combo").combo; combo.find("input.combo-value").each(function () { values.push($(this).val()); }); return values; }; //設置值(多選) function _setValues(jq, values) { var opts = $.data(jq, "combo").options; var nowValues = _getValues(jq);//獲取當前值數組 var combo = $.data(jq, "combo").combo; combo.find("input.combo-value").remove();//清空原來的值 var name = $(jq).attr("comboName"); for (var i = 0; i < values.length; i++) { var _4c = $("<input type=\"hidden\" class=\"combo-value\">").appendTo(combo); if (name) { _4c.attr("name", name); } _4c.val(values[i]); } var tmp = []; for (var i = 0; i < nowValues.length; i++) { tmp[i] = nowValues[i]; } var aa = []; for (var i = 0; i < values.length; i++) { for (var j = 0; j < tmp.length; j++) { if (values[i] == tmp[j]) { aa.push(values[i]); //splice()方法用於插入、刪除或替換數組的元素,詳細參考http://www.w3school.com.cn/js/jsref_splice.asp tmp.splice(j, 1); break; } } } ////若設置值數組與原值數組不相等,則將設置值數組、原值數組返回給onChange事件作為參數,並響應事件 if (aa.length != values.length || values.length != nowValues.length) { if (opts.multiple) { opts.onChange.call(jq, values, nowValues); } else { opts.onChange.call(jq, values[0], nowValues[0]); } } }; //獲取值(單選) function _getValue(jq) { var values = _getValues(jq); return values[0]; }; //設置值(單選) function _setValue(jq, value) { _setValues(jq, [value]); }; //初始化combo值 function initValue(jq) { var opts = $.data(jq, "combo").options; var fn = opts.onChange; opts.onChange = function () { }; //如果設置多選 if (opts.multiple) { if (opts.value) { if (typeof opts.value == "object") { _setValues(jq, opts.value); } else { _setValue(jq, opts.value); } } else { _setValues(jq, []); } opts.originalValue = _getValues(jq); } else { _setValue(jq, opts.value); opts.originalValue = opts.value; } opts.onChange = fn; }; //實例化下拉框 $.fn.combo = function (target, parm) { if (typeof target == "string") { return $.fn.combo.methods[target](this, parm); } target = target || {}; return this.each(function () { var combo = $.data(this, "combo"); if (combo) { $.extend(combo.options, target); } else { var r = renderCombo(this); combo = $.data(this, "combo", { options: $.extend({}, $.fn.combo.defaults, $.fn.combo.parseOptions(this), target), combo: r.combo, panel: r.panel, previousValue: null }); $(this).removeAttr("disabled"); } $("input.combo-text", combo.combo).attr("readonly", !combo.options.editable); setDownArrow(this);//設置是否顯示下拉箭頭 _disable(this, combo.options.disabled);//設置是否禁用 _resize(this); bindEvents(this); _validate(this); initValue(this);//初始化combo值 }); }; //默認方法 $.fn.combo.methods = { //返回屬性對象 options: function (jq) { return $.data(jq[0], "combo").options; }, //返回下拉面板對象 panel: function (jq) { return $.data(jq[0], "combo").panel; }, //返回文本框對象 textbox: function (jq) { return $.data(jq[0], "combo").combo.find("input.combo-text"); }, //銷毀該組件 destroy: function (jq) { return jq.each(function () { _destroy(this); }); }, // 調整組件寬度 resize: function (jq, width) { return jq.each(function () { _resize(this, width); }); }, //顯示下拉面板 showPanel: function (jq) { return jq.each(function () { _showPanel(this); }); }, //隱藏下拉面板 hidePanel: function (jq) { return jq.each(function () { hidePanel(this); }); }, //禁用組件 disable: function (jq) { return jq.each(function () { _disable(this, true); bindEvents(this); }); }, //啟用組件 enable: function (jq) { return jq.each(function () { _disable(this, false); bindEvents(this); }); }, //驗證輸入的值 validate: function (jq) { return jq.each(function () { _validate(this, true); }); }, //返回驗證結果 isValid: function (jq) { var combo = $.data(jq[0], "combo").combo.find("input.combo-text"); return combo.validatebox("isValid"); }, //清除控件的值 clear: function (jq) { return jq.each(function () { _clear(this); }); }, //重置控件的值 reset: function (jq) { return jq.each(function () { var opts = $.data(this, "combo").options; if (opts.multiple) { $(this).combo("setValues", opts.originalValue); } else { $(this).combo("setValue", opts.originalValue); } }); }, //獲取輸入的文本 getText: function (jq) { return _getText(jq[0]); }, //設置輸入的文本 setText: function (jq, text) { return jq.each(function () { _setText(this, text); }); }, //獲取組件值的數組 getValues: function (jq) { return _getValues(jq[0]); }, //設置組件值的數組 setValues: function (jq, values) { return jq.each(function () { _setValues(this, values); }); }, //獲取組件的值 getValue: function (jq) { return _getValue(jq[0]); }, //設置組件的值 setValue: function (jq, value) { return jq.each(function () { _setValue(this, value); }); } }; //解析器 定義屬性轉化為options $.fn.combo.parseOptions = function (target) { var t = $(target); return $.extend({}, $.fn.validatebox.parseOptions(target), $.parser.parseOptions(target, ["width", "height", "separator", { panelWidth: "number", editable: "boolean", hasDownArrow: "boolean", delay: "number" }]), { panelHeight: (t.attr("panelHeight") == "auto" ? "auto" : parseInt(t.attr("panelHeight")) || undefined), multiple: (t.attr("multiple") ? true : undefined), disabled: (t.attr("disabled") ? true : undefined), value: (t.val() || undefined) }); }; //默認屬性和事件 $.fn.combo.defaults = $.extend({}, $.fn.validatebox.defaults, { width: "auto",//組件的寬度 height: 22,//組件的高度 panelWidth: null,//下拉面板寬度 panelHeight: 200,//下拉面板高度 multiple: false,//定義是否支持多選 separator: ",",//在多選的時候使用何種分隔符進行分割 editable: true,//定義用戶是否可以直接輸入文本到字段中 disabled: false,//定義是否禁用字段 hasDownArrow: true,//定義是否顯示向下箭頭按鈕。 value: "",//字段的默認值 delay: 200,//最后一次輸入事件與執行搜索之間的延遲間隔(執行自動完成功能的延遲間隔) //在用戶按下鍵的時候調用一個函數 keyHandler: { up: function () {}, down: function () {}, enter: function () {}, query: function (q) {} }, //當下拉面板顯示的時候觸發 onShowPanel: function () {}, //當下拉面板隱藏的時候觸發 onHidePanel: function () {}, //當字段值改變的時候觸發 onChange: function (newValue, oldValue) {} }); })(jQuery);
示例代碼

<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>Basic Combo - jQuery EasyUI Demo</title> <link rel="stylesheet" type="text/css" href="../../themes/default/easyui.css"> <link rel="stylesheet" type="text/css" href="../../themes/icon.css"> <link rel="stylesheet" type="text/css" href="../demo.css"> <script type="text/javascript" src="../../jquery-1.8.0.min.js"></script> <script src="../../plugins2/jquery.parser.js"></script> <script src="../../plugins2/jquery.validatebox.js"></script> <script src="../../plugins2/jquery.panel.js"></script> <script src="../../plugins2/jquery.combo.js"></script> </head> <body> <h2>Basic Combo</h2> <div class="demo-info" style="margin-bottom:10px"> <div class="demo-tip icon-tip"></div> <div>Click the right arrow button to show drop down panel that can be filled with any content.</div> </div> <select id="cc" style="width:150px"></select> <div id="sp"> <div style="color:#99BBE8;background:#fafafa;padding:5px;">Select a language</div> <input type="radio" name="lang" value="01"><span>Java</span><br/> <input type="radio" name="lang" value="02"><span>C#</span><br/> <input type="radio" name="lang" value="03"><span>Ruby</span><br/> <input type="radio" name="lang" value="04"><span>Basic</span><br/> <input type="radio" name="lang" value="05"><span>Fortran</span> </div> <script type="text/javascript"> $(function(){ $('#cc').combo({ required:true, editable:false }); $('#sp').appendTo($('#cc').combo('panel')); $('#sp input').click(function(){ var v = $(this).val(); var s = $(this).next('span').text(); $('#cc').combo('setValue', v).combo('setText', s).combo('hidePanel'); }); }); </script> </body> </html>
插件效果