/** * jQuery EasyUI 1.3.2 * * Copyright (c) 2009-2013 www.jeasyui.com. All rights reserved. * * Licensed under the GPL or commercial licenses To use it on other terms please * contact us: [email protected].com http://www.gnu.org/licenses/gpl.txt * http://www.jeasyui.com/license_commercial.php * 注释由小雪完成,更多组件源码内容到www.easyui.info上搜索"easyui源码分析"关键字 * 该源码完全由压缩码翻译而来,并非网络上放出的源码,请勿索要。 */ (function($) { /** * 滚动选项到合适位置: * 如果item被卷在panel上方,则滚动到panel可见区的最上方; * 如果item被卷在panel下方,则滚动到panel可见区的最下方; * parmas[target] 承载combobox的DOM * params[value] valueField字段对应的某个值 */ function scrollItem(target, value) { var panel = $(target).combo("panel"); var item = panel.find("div.combobox-item[value=\"" + value + "\"]"); if (item.length) { if (item.position().top <= 0) { var h = panel.scrollTop() + item.position().top; panel.scrollTop(h); } else { if (item.position().top + item.outerHeight() > panel.height()) { var h = panel.scrollTop() + item.position().top + item.outerHeight() - panel.height(); panel.scrollTop(h); } } } }; /** * 选中上一个可见选项,如果该选项被滚动条卷去,则滚动该选项到合适位置。 * parmas[target] 承载combobox的DOM */ function selectPrev(target) { var panel = $(target).combo("panel"); var values = $(target).combo("getValues"); var item = panel.find("div.combobox-item[value=\"" + values.pop() + "\"]"); if (item.length) { var prev = item.prev(":visible"); if (prev.length) { item = prev; } } else { item = panel.find("div.combobox-item:visible:last"); } var value = item.attr("value"); select(target, value); scrollItem(target, value); }; /** * 选中下一个可见选项,如果该选项被滚动条卷去,则滚动该选项到合适位置。 * parmas[target] 承载combobox的DOM */ function selectNext(target) { var panel = $(target).combo("panel"); var values = $(target).combo("getValues"); var item = panel.find("div.combobox-item[value=\"" + values.pop() + "\"]"); if (item.length) { var next = item.next(":visible"); if (next.length) { item = next; } } else { item = panel.find("div.combobox-item:visible:first"); } var value = item.attr("value"); select(target, value); scrollItem(target, value); }; /** * 选中指定valueField值的项 * 选中后调用onSelect事件 * parmas[target] 承载combobox的DOM * params[value] valueField字段对应的某个值 */ function select(target, value) { var opts = $.data(target, "combobox").options; var data = $.data(target, "combobox").data; if (opts.multiple) { var values = $(target).combo("getValues"); for (var i = 0; i < values.length; i++) { if (values[i] == value) { return; } } values.push(value); setValues(target, values); } else { setValues(target, [value]); } for (var i = 0; i < data.length; i++) { if (data[i][opts.valueField] == value) { opts.onSelect.call(target, data[i]); return; } } }; /** * 取消选中指定valueField值的项 * 取消选中后调用onUnselect事件 * parmas[target] 承载combobox的DOM * params[value] valueField字段对应的某个值 */ function unselect(target, value) { var opts = $.data(target, "combobox").options; var data = $.data(target, "combobox").data; var values = $(target).combo("getValues"); for (var i = 0; i < values.length; i++) { if (values[i] == value) { values.splice(i, 1); setValues(target, values); break; } } for (var i = 0; i < data.length; i++) { if (data[i][opts.valueField] == value) { opts.onUnselect.call(target, data[i]); return; } } }; /** * 设置combobox的值 * parmas[target] 承载combobox的DOM * params[values] valueField字段对应的多个值 * params[remainText] 是保留文本,为false将重新设置文本 */ function setValues(target, values, remainText) { var opts = $.data(target, "combobox").options; var data = $.data(target, "combobox").data; var panel = $(target).combo("panel"); panel.find("div.combobox-item-selected") .removeClass("combobox-item-selected"); var vv = [], ss = []; for (var i = 0; i < values.length; i++) { var v = values[i]; var s = v; for (var j = 0; j < data.length; j++) { if (data[j][opts.valueField] == v) { s = data[j][opts.textField]; break; } } vv.push(v); ss.push(s); panel.find("div.combobox-item[value=\"" + v + "\"]") .addClass("combobox-item-selected"); } $(target).combo("setValues", vv); if (!remainText) { $(target).combo("setText", ss.join(opts.separator)); } }; /** * 从select标签的option中获取combobox的data * parmas[target] 承载combobox的DOM */ function getDataTag(target) { var opts = $.data(target, "combobox").options; var data = []; $(">option", target).each(function() { var item = {}; item[opts.valueField] = $(this).attr("value") != undefined ? $(this) .attr("value") : $(this).html(); item[opts.textField] = $(this).html(); item["selected"] = $(this).attr("selected"); data.push(item); }); return data; }; /** * 装载数据 * parmas[target] 承载combobox的DOM * params[data] 要装载的数据,从远程url获取或者开发者传入的数组 * params[remainText] 是保留文本,为false将重新设置文本 */ function loadData(target, data, remainText) { var opts = $.data(target, "combobox").options; var panel = $(target).combo("panel"); //先将数据存储到与target绑定的对象上 $.data(target, "combobox").data = data; //获取values,注意这里是从input.combo-value隐藏域中获取的 var values = $(target).combobox("getValues"); //清空下拉面板中的列表选项 panel.empty(); //根据data循环生成下拉面板列表选项 for (var i = 0; i < data.length; i++) { var v = data[i][opts.valueField]; var s = data[i][opts.textField]; var item = $("<div class=\"combobox-item\"></div>").appendTo(panel); item.attr("value", v); if (opts.formatter) { //如果定义了formatter,则将formatter返回的DOM结构填充到div.combobox-item里 //formatter入参为data元素 item.html(opts.formatter.call(target, data[i])); } else { //直接将文本填充到div.combobox-item里 item.html(s); } //如果某个data元素设置了selected属性(即默认值,注意可以有多个的), //则将默认值与现有的隐藏域列表的值相比较,如果不在该列表中,则添加进去。 //注意这里只是增加了values数组的元素个数并未处理隐藏域列表,隐藏域列表的处理放在setValues方法里。 if (data[i]["selected"]) { (function() { for (var i = 0; i < values.length; i++) { if (v == values[i]) { return; } } values.push(v); })(); } } //设置值,设置的时候会处理隐藏域列表 if (opts.multiple) {//复选 setValues(target, values, remainText); } else {//单选 if (values.length) { setValues(target, [values[values.length - 1]], remainText); } else { setValues(target, [], remainText); } } //此处触发onLoadSuccess事件,入参为data opts.onLoadSuccess.call(target, data); //绑定下拉面板选项的hover和click事件 //这个地方用事件委托是不是要好点呢?可以节省不少资源。 $(".combobox-item", panel).hover(function() { $(this).addClass("combobox-item-hover"); }, function() { $(this).removeClass("combobox-item-hover"); }).click(function() { var item = $(this); if (opts.multiple) { if (item.hasClass("combobox-item-selected")) { unselect(target, item.attr("value")); } else { select(target, item.attr("value")); } } else { select(target, item.attr("value")); $(target).combo("hidePanel"); } }); }; /** * 请求远程数据 * parmas[target] 承载combobox的DOM * params
请求数据的远程url * params[param] 发送给远程url的查询参数 * params[remainText] 是保留文本,为false将重新设置文本 */ function request(target, url, param, remainText) { var opts = $.data(target, "combobox").options; if (url) { opts.url = url; } param = param || {}; //触发onBeforeLoad事件,返回false将取消数据请求 if (opts.onBeforeLoad.call(target, param) == false) { return; } //loader为配适器,用于定义如何获取远程数据 opts.loader.call(target, param, function(data) { //请求成功的话,装载请求到的数据 loadData(target, data, remainText); }, function() { //触发请求出错事件 opts.onLoadError.apply(this, arguments); }); }; /** * 数据过滤(本地)或者请求(远程) * parmas[target] 承载combobox的DOM * parmas[q] 用户输入的文本 */ function doQuery(target, q) { var opts = $.data(target, "combobox").options; //设置values?谁会输入valueField呢?q为text,把text作为value带进去是什么意思呢? //个人觉得这个地方不合理 if (opts.multiple && !q) { setValues(target, [], true); } else { setValues(target, [q], true); } if (opts.mode == "remote") {//如果为remote模式,则请求远程数据 request(target, null, { q : q }, true); } else {//本地模式 var panel = $(target).combo("panel"); //隐藏所有下拉选项 panel.find("div.combobox-item").hide(); var data = $.data(target, "combobox").data; for (var i = 0; i < data.length; i++) { //如果根据text过滤到(过滤规则为:包含用户输入值即匹配)匹配选项,则显示、设置选项。 if (opts.filter.call(target, q, data[i])) { var v = data[i][opts.valueField]; var s = data[i][opts.textField]; var item = panel.find("div.combobox-item[value=\"" + v + "\"]"); //显示item item.show(); if (s == q) {//完全匹配(即完全等于) //设置values setValues(target, [v], true); //添加选中样式 item.addClass("combobox-item-selected"); } } } } }; /** * 实例化combo组件 * parmas[target] 承载combobox的DOM */ function create(target) { var opts = $.data(target, "combobox").options; $(target).addClass("combobox-f"); $(target).combo($.extend({}, opts, { onShowPanel : function() { $(target).combo("panel").find("div.combobox-item").show(); scrollItem(target, $(target).combobox("getValue")); opts.onShowPanel.call(target); } })); }; //构造函数 $.fn.combobox = function(options, params) { if (typeof options == "string") { var method = $.fn.combobox.methods[options]; if (method) { //有mothed则调用之 return method(this, params); } else { //没方法,则继承combo组件的同名方法 return this.combo(options, params); } } options = options || {}; return this.each(function() { var state = $.data(this, "combobox"); if (state) { //更新属性数据 $.extend(state.options, options); //创建combo create(this); } else { //绑定属性数据到target上 state = $.data(this, "combobox", { options : $.extend({}, $.fn.combobox.defaults, $.fn.combobox.parseOptions(this), options) }); //创建combo create(this); //从html中装载数据 loadData(this, getDataTag(this)); } if (state.options.data) { //装载指定的数组 loadData(this, state.options.data); } //请求远程数据 request(this); }); }; //定义对外接口方法 $.fn.combobox.methods = { options : function(jq) { var opts = $.data(jq[0], "combobox").options; opts.originalValue = jq.combo("options").originalValue; return opts; }, getData : function(jq) { return $.data(jq[0], "combobox").data; }, setValues : function(jq, values) { return jq.each(function() { setValues(this, values); }); }, setValue : function(jq, value) { return jq.each(function() { setValues(this, [value]); }); }, clear : function(jq) { return jq.each(function() { $(this).combo("clear"); var panel = $(this).combo("panel"); panel.find("div.combobox-item-selected") .removeClass("combobox-item-selected"); }); }, reset : function(jq) { return jq.each(function() { var opts = $(this).combobox("options"); if (opts.multiple) { $(this).combobox("setValues", opts.originalValue); } else { $(this).combobox("setValue", opts.originalValue); } }); }, loadData : function(jq, data) { return jq.each(function() { loadData(this, data); }); }, reload : function(jq, url) { return jq.each(function() { request(this, url); }); }, select : function(jq, value) { return jq.each(function() { select(this, value); }); }, unselect : function(jq, value) { return jq.each(function() { unselect(this, value); }); } }; //定义属性转换器 $.fn.combobox.parseOptions = function(target) { var t = $(target); return $.extend({}, $.fn.combo.parseOptions(target), $.parser .parseOptions(target, ["valueField", "textField", "mode", "method", "url"])); }; //定义属性和事件的默认值 $.fn.combobox.defaults = $.extend({}, $.fn.combo.defaults, { valueField : "value", textField : "text", mode : "local", method : "post", url : null, data : null, keyHandler : { up : function() { selectPrev(this); }, down : function() { selectNext(this); }, enter : function() { var values = $(this).combobox("getValues"); $(this).combobox("setValues", values); $(this).combobox("hidePanel"); }, query : function(q) { doQuery(this, q); } }, filter : function(q, row) { var opts = $(this).combobox("options"); return row[opts.textField].indexOf(q) == 0; }, formatter : function(row) { var opts = $(this).combobox("options"); return row[opts.textField]; }, loader : function(param, onLoadSuccess, onLoadError) { var opts = $(this).combobox("options"); if (!opts.url) { return false; } $.ajax({ type : opts.method, url : opts.url, data : param, dataType : "json", success : function(data) { onLoadSuccess(data); }, error : function() { onLoadError.apply(this, arguments); } }); }, onBeforeLoad : function(param) { }, onLoadSuccess : function() { }, onLoadError : function() { }, onSelect : function(record) { }, onUnselect : function(record) { } }); })(jQuery);
scrollItem 函数:
/** * 滚动选项到合适位置: * 如果item被卷在panel上方,则滚动到panel可见区的最上方; * 如果item被卷在panel下方,则滚动到panel可见区的最下方; * parmas[target] 承载combobox的DOM * params[value] valueField字段对应的某个值 */ function scrollItem(target, value) { var panel = $(target).combo("panel"); var item = panel.find("div.combobox-item[value=\"" + value + "\"]"); if (item.length) { if (item.position().top <= 0) { var h = panel.scrollTop() + item.position().top; panel.scrollTop(h); } else { if (item.position().top + item.outerHeight() > panel.height()) { var h = panel.scrollTop() + item.position().top + item.outerHeight() - panel.height(); panel.scrollTop(h); } } } }
对于jquery的position和scrollTop等函数不太了解的,请看以下几幅参照图:
需调整的情况一:
调整前:
调整后:
需调整的情况二:
调整前:
调整后:
其它内部函数没有什么太难理解的地方,不过代码中内部函数doQuery中的几句代码的用意我不是十分清楚,希望知晓的童鞋们告知一下。
时间: 2024-09-21 14:45:10