combox源码解析

时间:2021-07-09 11:53:18
/**
* 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: jeasyui@gmail.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等函数不太了解的,请看以下几幅参照图:

需调整的情况一:

调整前:
combox源码解析
调整后:
combox源码解析

需调整的情况二:

调整前:
combox源码解析
调整后:
combox源码解析

其它内部函数没有什么太难理解的地方,不过代码中内部函数doQuery中的几句代码的用意我不是十分清楚,希望知晓的童鞋们告知一下。

combox源码解析的更多相关文章

  1. 【原】Android热更新开源项目Tinker源码解析系列之三:so热更新

    本系列将从以下三个方面对Tinker进行源码解析: Android热更新开源项目Tinker源码解析系列之一:Dex热更新 Android热更新开源项目Tinker源码解析系列之二:资源文件热更新 A ...

  2. 【原】Android热更新开源项目Tinker源码解析系列之一:Dex热更新

    [原]Android热更新开源项目Tinker源码解析系列之一:Dex热更新 Tinker是微信的第一个开源项目,主要用于安卓应用bug的热修复和功能的迭代. Tinker github地址:http ...

  3. 【原】Android热更新开源项目Tinker源码解析系列之二&colon;资源文件热更新

    上一篇文章介绍了Dex文件的热更新流程,本文将会分析Tinker中对资源文件的热更新流程. 同Dex,资源文件的热更新同样包括三个部分:资源补丁生成,资源补丁合成及资源补丁加载. 本系列将从以下三个方 ...

  4. 多线程爬坑之路-Thread和Runable源码解析之基本方法的运用实例

    前面的文章:多线程爬坑之路-学习多线程需要来了解哪些东西?(concurrent并发包的数据结构和线程池,Locks锁,Atomic原子类) 多线程爬坑之路-Thread和Runable源码解析 前面 ...

  5. jQuery2&period;x源码解析&lpar;缓存篇&rpar;

    jQuery2.x源码解析(构建篇) jQuery2.x源码解析(设计篇) jQuery2.x源码解析(回调篇) jQuery2.x源码解析(缓存篇) 缓存是jQuery中的又一核心设计,jQuery ...

  6. Spring IoC源码解析——Bean的创建和初始化

    Spring介绍 Spring(http://spring.io/)是一个轻量级的Java 开发框架,同时也是轻量级的IoC和AOP的容器框架,主要是针对JavaBean的生命周期进行管理的轻量级容器 ...

  7. jQuery2&period;x源码解析&lpar;构建篇&rpar;

    jQuery2.x源码解析(构建篇) jQuery2.x源码解析(设计篇) jQuery2.x源码解析(回调篇) jQuery2.x源码解析(缓存篇) 笔者阅读了园友艾伦 Aaron的系列博客< ...

  8. jQuery2&period;x源码解析&lpar;设计篇&rpar;

    jQuery2.x源码解析(构建篇) jQuery2.x源码解析(设计篇) jQuery2.x源码解析(回调篇) jQuery2.x源码解析(缓存篇) 这一篇笔者主要以设计的角度探索jQuery的源代 ...

  9. jQuery2&period;x源码解析&lpar;回调篇&rpar;

    jQuery2.x源码解析(构建篇) jQuery2.x源码解析(设计篇) jQuery2.x源码解析(回调篇) jQuery2.x源码解析(缓存篇) 通过艾伦的博客,我们能看出,jQuery的pro ...

随机推荐

  1. Zabbix agent on Microsoft Windows

    1.在Windows上创建目录: C:\Windows\zabbix\ 2.下载安装包并解压到新建的目录 3.下载地址:http://www.zabbix.com/downloads/3.0.0/za ...

  2. redis实现与分析

    http://www.kuqin.com/shuoit/20141019/342739.html

  3. 武汉新芯:已建成IP体系,欲以存储器为特色

    武汉新芯集成电路制造公司(XMC)是地方*投资的半导体企业,2006年由湖北省.武汉市.武汉市东湖高新区投资,并由东湖高新区管理的全资国有企业,前几年委托SMIC(中芯国际)经营管理,从2012年底 ...

  4. xheditor 进阶

    xhEditor提供两种方式初始化编辑器: 方法1:利用class属性来初始化和传递各种初始化参数,例:  class="xheditor {skin:'default'}" 方法 ...

  5. 一些CSS&sol;JS小技巧

    CSS部分 1.文本框不可点击 .inputDisabled{ background-color: #eee;cursor: not-allowed;} 2.禁止复制粘贴 onpaste=" ...

  6. 【洛谷T7243】【CJOJ2225】【BYVoid S3】珠光宝气阁(潜入辛迪加)

    Description "我们最新的研究成果<毒药研究方案>被可恶的辛迪加偷走了!"作为拉文霍德的一员,你一定感到很震惊,因为它是我们最尖端的科研人员的一年的研究成果. ...

  7. delphi JPG图片 旋转 切边 缩放

    unit UCutFigure_JPG; //JPG 切图 interface uses Windows, Messages, SysUtils, Variants, Classes, Graphic ...

  8. android官方技术文档翻译——设计时布局属性

    本文译自androd官方技术文档<Designtime Layout Attributes>:http://tools.android.com/tips/layout-designtime ...

  9. k8s&lpar;一&rpar; kubeadm简单集群初始化

    写给想入门kubernetes的同学们 # 系统版本 [root@master ~]# cat /etc/os-release NAME="CentOS Linux" VERSIO ...

  10. MyBatis-parameterType 取出入参值

    SQL 映射文件的几种入参情况 一.单个基本类型参数 public MyUser selectMyUser(Integer id); <!-- #{参数名或任意名}:取出参数值 --> & ...