combox源码解析
/**
* 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等函数不太了解的,请看以下几幅参照图:
需调整的情况一:
调整前:
调整后:
需调整的情况二:
调整前:
调整后:
其它内部函数没有什么太难理解的地方,不过代码中内部函数doQuery中的几句代码的用意我不是十分清楚,希望知晓的童鞋们告知一下。
combox源码解析的更多相关文章
- 【原】Android热更新开源项目Tinker源码解析系列之三:so热更新
本系列将从以下三个方面对Tinker进行源码解析: Android热更新开源项目Tinker源码解析系列之一:Dex热更新 Android热更新开源项目Tinker源码解析系列之二:资源文件热更新 A ...
- 【原】Android热更新开源项目Tinker源码解析系列之一:Dex热更新
[原]Android热更新开源项目Tinker源码解析系列之一:Dex热更新 Tinker是微信的第一个开源项目,主要用于安卓应用bug的热修复和功能的迭代. Tinker github地址:http ...
- 【原】Android热更新开源项目Tinker源码解析系列之二:资源文件热更新
上一篇文章介绍了Dex文件的热更新流程,本文将会分析Tinker中对资源文件的热更新流程. 同Dex,资源文件的热更新同样包括三个部分:资源补丁生成,资源补丁合成及资源补丁加载. 本系列将从以下三个方 ...
- 多线程爬坑之路-Thread和Runable源码解析之基本方法的运用实例
前面的文章:多线程爬坑之路-学习多线程需要来了解哪些东西?(concurrent并发包的数据结构和线程池,Locks锁,Atomic原子类) 多线程爬坑之路-Thread和Runable源码解析 前面 ...
- jQuery2.x源码解析(缓存篇)
jQuery2.x源码解析(构建篇) jQuery2.x源码解析(设计篇) jQuery2.x源码解析(回调篇) jQuery2.x源码解析(缓存篇) 缓存是jQuery中的又一核心设计,jQuery ...
- Spring IoC源码解析——Bean的创建和初始化
Spring介绍 Spring(http://spring.io/)是一个轻量级的Java 开发框架,同时也是轻量级的IoC和AOP的容器框架,主要是针对JavaBean的生命周期进行管理的轻量级容器 ...
- jQuery2.x源码解析(构建篇)
jQuery2.x源码解析(构建篇) jQuery2.x源码解析(设计篇) jQuery2.x源码解析(回调篇) jQuery2.x源码解析(缓存篇) 笔者阅读了园友艾伦 Aaron的系列博客< ...
- jQuery2.x源码解析(设计篇)
jQuery2.x源码解析(构建篇) jQuery2.x源码解析(设计篇) jQuery2.x源码解析(回调篇) jQuery2.x源码解析(缓存篇) 这一篇笔者主要以设计的角度探索jQuery的源代 ...
- jQuery2.x源码解析(回调篇)
jQuery2.x源码解析(构建篇) jQuery2.x源码解析(设计篇) jQuery2.x源码解析(回调篇) jQuery2.x源码解析(缓存篇) 通过艾伦的博客,我们能看出,jQuery的pro ...
随机推荐
- 【转】 Live555
Ⅰ live555简介 Live555 是一个为流媒体提供解决方案的跨平台的C++开源项目,它实现了对标准流媒体传输协议如RTP/RTCP.RTSP.SIP等的支持.Live555实现了对多种音视频编 ...
- 纯css3代码写无缝滚动效果
主要用到css3中的动画 @keyframes, animation. 布局是外层一个div宽固定,然后overflow hidden 绝对定位,里面的ul 固定定位.通过对ul添加动画来实现效果.具 ...
- CentOS6安装python2.7
第一次用centOS,感觉好高大上,安装了差不多一个半小时,学习了挺多命令的 1. 检查centOS中默认的python版本,一般是python2.6. 命令:python –v 2. 安装GCC ...
- mvc伪静态<四> 伪静态后静态页面或者引用的css和图片失效
引用的css和图片失效的解决办法 把样式引用文件的相对路径改成绝对路径就可以了 比如原先的引用路径为:<link href="~/Content/css/style.css" ...
- 25个有用和方便的 WordPress 速查手册
如果你是一个 WordPress 编码器或开发人员,下载一些方便的 WordPress 备忘单寻找你的工作然后你在正确的地方.我们已经列出了25个有用的和方便的 WordPress 速查手册.Word ...
- 一步一步配置NLB(续)之深入测试
在这里http://blog.csdn.net/haoxiaozigang1/article/details/12198679跟大家分享了NLB配置的过程,下面写一些对NLB不同情况的下测试的结果: ...
- MongoDb系列
这个系列主要总结学习MongoDb过程中的一些经验. 简单介绍及环境搭建 常用命令 C#驱动及应用 管理工具MongoVUE使用
- 重点关注之Filter的使用(性能计数和错误处理)
Web API中的filter与MVC中的filter非常类似,最主要的不同是,MVC中的filter放在命名空间System.Web.Mvc下,而Web API中的filter则放在命名空间Syst ...
- ExecuteNonQuery()返回值注意点
在使用ExecuteNonQuery(),调用存储过程,语句执行无错误,但是返回结果一直是-1 原因: 当使用储存过程时, 要把SET NOCOUNT ON 这个语句去掉, 这样数据就有反回值了 当 ...
- Net开发环境配置
Web开发插件: 1.JSEnhancements js和css折叠插件 可以参见dudu的介绍不错的VS2010扩展——JSEnhancements,让js和css也折叠 下载地址:http://v ...