[转]浅谈jQuery EasyUI的属性设置
2016-02-06 19:57
731 查看
原文地址:http://www.easyui.info/archives/1664.html
对jQuery EasyUI有一定了解的话,应该知道基本上每一个组件都有一个"options"方法用于返回该组件实例的属性。这些属性决定了组件实例将如何展现和运作。
不过EasyUI的属性取值渠道比较多,多得有些让人眼花缭乱,渠道多有好处也有坏处,好处就是很灵活,坏处就是容易乱,比如说多种渠道设置同一个属性时,到底以哪种渠道设置的属性为准呢?
本篇文章就来较为详细的做一下总结,注意,我们只关注属性有哪些渠道获取,各种渠道的优先级,属性的类型等,我们并不关注属性是如何影响组件实例的。
【渠道2】组件实例宿主DOM对象的data-options属性,data-options是以json串的方式设置属性的,很灵活;
【渠道3】组件实例宿主DOM对象的属性,也就是html标签的各个属性(style和data-options例外)
【渠道4】组件实例宿主DOM对象的style属性;
【渠道5】如果组件的实例化是通过javascript的话,组件构造函数的的入参也可以设置组件实例属性
/**
* panel组件的构造函数
* @param options
* @param param
* @returns {*}
*/
$.fn.panel = function(options, param){
if (typeof options == 'string'){
return $.fn.panel.methods[options](this, param);
}
options = options || {};
return this.each(function(){
var state = $.data(this, 'panel');
var opts;
if (state){//已经初始化过
//属性值用新传入的覆盖
opts = $.extend(state.options, options);
state.isLoaded = false;
} else {//还未初始化
//获取panel组件实例的属性列表
opts = $.extend({}, $.fn.panel.defaults, $.fn.panel.parseOptions(this), options);
$(this).attr('title', '');
state = $.data(this, 'panel', {
options: opts,
panel: wrapPanel(this),
isLoaded: false
});
}
addHeader(this);
setProperties(this);
if (opts.doSize == true){
state.panel.css('display','block');
setSize(this);
}
if (opts.closed == true || opts.minimized == true){
state.panel.hide();
} else {
openPanel(this);
}
});
};
关键性代码就是22行,使用了jQuery的extend函数,这行代码的信息量很大,我们可以轻松地看出,组件默认值的优先级最低;如果是javascript构造的组件实例,构造函数的入参优先级最高;夹在中间的是panel组件的属性转换器。
到这里,我们至少已经得出了一个简单结论:【渠道1】的优先级最低,【渠道5】的优先级最高。
/**
* panel组件的属性转换器
* @param target 组件实例的宿主DOM
* @returns {*}
*/
$.fn.panel.parseOptions = function (target) {
var t = $(target);
return $.extend(
//新空对象
{},
//公用的属性转换器
$.parser.parseOptions(target, ['id', 'width', 'height', 'left', 'top',
'title', 'iconCls', 'cls', 'headerCls', 'bodyCls', 'tools', 'href',
{cache: 'boolean', fit: 'boolean', border: 'boolean', noheader: 'boolean'},
{collapsible: 'boolean', minimizable: 'boolean', maximizable: 'boolean'},
{closable: 'boolean', collapsed: 'boolean', minimized: 'boolean', maximized: 'boolean', closed: 'boolean'}
]),
//loadingMessage属性有什么特别之处,为什么要单独作为一个参数?
{
loadingMessage: (t.attr('loadingMessage') != undefined ? t.attr('loadingMessage') : undefined)
}
);
};
又是用了jQuery的extend函数,直接看上去,得不到太多信息,extend函数有三个实参。第一个是为了拷贝到新对象上,第二个是公用的属性转换器,第三个则有些莫名其妙,单单的一个loadingMessage属性,为何要特殊处理?
/**
* parse options, including standard 'data-options' attribute.
*
* @param target [DOM对象] 组件的宿主对象
* @param properties [数组] 需要优先从宿主对象属性(不包含data-options属性)中取的opts列表
*
* calling examples:
* $.parser.parseOptions(target);
* $.parser.parseOptions(target, ['id','title','width',{fit:'boolean',border:'boolean'},{min:'number'}]);
*/
function parseOptions(target, properties){
var t = $(target);
var options = {};
//第一步:首先从data-options属性中取opts
var s = $.trim(t.attr('data-options'));
if (s){
//兼容写大括号和不写大括号的写法
if (s.substring(0, 1) != '{'){
s = '{' + s + '}';
}
//利用Function函数将字符串转为化对象
//到这里,我们第一次取值就完成了。
options = (new Function('return ' + s))();
}
//第二步:如果properties不为空的话,则将properties中定义的属性增加或者覆盖到“第一步”中取到的属性列表中
if (properties){
var opts = {};
for(var i=0; i<properties.length; i++){
var pp = properties[i];
//如果是字符串型
if (typeof pp == 'string'){
//width height left top四个属性从宿主对象的style属性中取值
if (pp == 'width' || pp == 'height' || pp == 'left' || pp == 'top'){
opts[pp] = parseInt(target.style[pp]) || undefined;
} else {//其它情况直接从宿主对象的对应属性中取值
opts[pp] = t.attr(pp);
}
} else {//布尔型或者数字型
for(var name in pp){
var type = pp[name];
if (type == 'boolean'){//布尔型从宿主对象对应属性中取值,同时将属性值转为布尔型
opts[name] = t.attr(name) ? (t.attr(name) == 'true') : undefined;
} else if (type == 'number'){//数字型也从宿主对象对应属性中取值,同时将属性值转为浮数
opts[name] = t.attr(name)=='0' ? 0 : parseFloat(t.attr(name)) || undefined;
}
}
}
}
//第二步取得的结果覆盖到第一步取得的结果中
$.extend(options, opts);
}
return options;
}
原来主要的逻辑在公用的属性转换器里面,由代码我们可以清楚的看出【渠道3】和【渠道4】的优先级是高于【渠道2】的。
公用属性转换器的入参properties的用途究竟是什么呢?有代码可以看出,它是一个数组,数组元素只支持string和object两种,再 回头看,结合panel组件的属性转换器代码,不难可以看出,properties属性规范了组件实例各属性的数据类型,不过properties能规范 的类型只有boolen,number,string三种。
特殊的,对于width,height,left,top这四个属性,优先级最高的是【渠道4】,即最优先从style属性中取,且类型都为number型。
【渠道5】 > 【渠道4】 > 【渠道3】 > 【渠道2】 > 【渠道1】
到此处,我们关心的主要问题就算分析完了。不过还是遗留了一个问题,那就是panel组件的属性转换器中,为何loadingMessage属性不 放在公共的属性转换器里处理?单独作为参数传入的,number,bool,string这三种类型之外的属性,才有这必要。1.3.5的panel源码 为何要把loadingMessage属性特殊对待,本人是没有想明白。
注意:本文的分析基于data-options属性出世之后(印象中是1.2.6版本之后),文中代码是1.3.5版本的源码。
对jQuery EasyUI有一定了解的话,应该知道基本上每一个组件都有一个"options"方法用于返回该组件实例的属性。这些属性决定了组件实例将如何展现和运作。
不过EasyUI的属性取值渠道比较多,多得有些让人眼花缭乱,渠道多有好处也有坏处,好处就是很灵活,坏处就是容易乱,比如说多种渠道设置同一个属性时,到底以哪种渠道设置的属性为准呢?
本篇文章就来较为详细的做一下总结,注意,我们只关注属性有哪些渠道获取,各种渠道的优先级,属性的类型等,我们并不关注属性是如何影响组件实例的。
属性获取渠道
【渠道1】组件定义时设置的默认值,组件没有实例化就是相当于"类"了,各种属性设置了默认值;【渠道2】组件实例宿主DOM对象的data-options属性,data-options是以json串的方式设置属性的,很灵活;
【渠道3】组件实例宿主DOM对象的属性,也就是html标签的各个属性(style和data-options例外)
【渠道4】组件实例宿主DOM对象的style属性;
【渠道5】如果组件的实例化是通过javascript的话,组件构造函数的的入参也可以设置组件实例属性
组件的构造函数
这么多种方式设置组件实例的属性,到底他们的优先级是什么样的?我们拿panel组件的源码来分析,看pane构造函数代码,因为一切都是从构造函数开始的:/**
* panel组件的构造函数
* @param options
* @param param
* @returns {*}
*/
$.fn.panel = function(options, param){
if (typeof options == 'string'){
return $.fn.panel.methods[options](this, param);
}
options = options || {};
return this.each(function(){
var state = $.data(this, 'panel');
var opts;
if (state){//已经初始化过
//属性值用新传入的覆盖
opts = $.extend(state.options, options);
state.isLoaded = false;
} else {//还未初始化
//获取panel组件实例的属性列表
opts = $.extend({}, $.fn.panel.defaults, $.fn.panel.parseOptions(this), options);
$(this).attr('title', '');
state = $.data(this, 'panel', {
options: opts,
panel: wrapPanel(this),
isLoaded: false
});
}
addHeader(this);
setProperties(this);
if (opts.doSize == true){
state.panel.css('display','block');
setSize(this);
}
if (opts.closed == true || opts.minimized == true){
state.panel.hide();
} else {
openPanel(this);
}
});
};
关键性代码就是22行,使用了jQuery的extend函数,这行代码的信息量很大,我们可以轻松地看出,组件默认值的优先级最低;如果是javascript构造的组件实例,构造函数的入参优先级最高;夹在中间的是panel组件的属性转换器。
到这里,我们至少已经得出了一个简单结论:【渠道1】的优先级最低,【渠道5】的优先级最高。
组件的属性转换器
对于渠道2,3,4,我们则要进一步分析panel组件的属性转换器了,看它的代码:/**
* panel组件的属性转换器
* @param target 组件实例的宿主DOM
* @returns {*}
*/
$.fn.panel.parseOptions = function (target) {
var t = $(target);
return $.extend(
//新空对象
{},
//公用的属性转换器
$.parser.parseOptions(target, ['id', 'width', 'height', 'left', 'top',
'title', 'iconCls', 'cls', 'headerCls', 'bodyCls', 'tools', 'href',
{cache: 'boolean', fit: 'boolean', border: 'boolean', noheader: 'boolean'},
{collapsible: 'boolean', minimizable: 'boolean', maximizable: 'boolean'},
{closable: 'boolean', collapsed: 'boolean', minimized: 'boolean', maximized: 'boolean', closed: 'boolean'}
]),
//loadingMessage属性有什么特别之处,为什么要单独作为一个参数?
{
loadingMessage: (t.attr('loadingMessage') != undefined ? t.attr('loadingMessage') : undefined)
}
);
};
又是用了jQuery的extend函数,直接看上去,得不到太多信息,extend函数有三个实参。第一个是为了拷贝到新对象上,第二个是公用的属性转换器,第三个则有些莫名其妙,单单的一个loadingMessage属性,为何要特殊处理?
公用的属性转换器
进一步看公用属性转换器的源码:/**
* parse options, including standard 'data-options' attribute.
*
* @param target [DOM对象] 组件的宿主对象
* @param properties [数组] 需要优先从宿主对象属性(不包含data-options属性)中取的opts列表
*
* calling examples:
* $.parser.parseOptions(target);
* $.parser.parseOptions(target, ['id','title','width',{fit:'boolean',border:'boolean'},{min:'number'}]);
*/
function parseOptions(target, properties){
var t = $(target);
var options = {};
//第一步:首先从data-options属性中取opts
var s = $.trim(t.attr('data-options'));
if (s){
//兼容写大括号和不写大括号的写法
if (s.substring(0, 1) != '{'){
s = '{' + s + '}';
}
//利用Function函数将字符串转为化对象
//到这里,我们第一次取值就完成了。
options = (new Function('return ' + s))();
}
//第二步:如果properties不为空的话,则将properties中定义的属性增加或者覆盖到“第一步”中取到的属性列表中
if (properties){
var opts = {};
for(var i=0; i<properties.length; i++){
var pp = properties[i];
//如果是字符串型
if (typeof pp == 'string'){
//width height left top四个属性从宿主对象的style属性中取值
if (pp == 'width' || pp == 'height' || pp == 'left' || pp == 'top'){
opts[pp] = parseInt(target.style[pp]) || undefined;
} else {//其它情况直接从宿主对象的对应属性中取值
opts[pp] = t.attr(pp);
}
} else {//布尔型或者数字型
for(var name in pp){
var type = pp[name];
if (type == 'boolean'){//布尔型从宿主对象对应属性中取值,同时将属性值转为布尔型
opts[name] = t.attr(name) ? (t.attr(name) == 'true') : undefined;
} else if (type == 'number'){//数字型也从宿主对象对应属性中取值,同时将属性值转为浮数
opts[name] = t.attr(name)=='0' ? 0 : parseFloat(t.attr(name)) || undefined;
}
}
}
}
//第二步取得的结果覆盖到第一步取得的结果中
$.extend(options, opts);
}
return options;
}
原来主要的逻辑在公用的属性转换器里面,由代码我们可以清楚的看出【渠道3】和【渠道4】的优先级是高于【渠道2】的。
公用属性转换器的入参properties的用途究竟是什么呢?有代码可以看出,它是一个数组,数组元素只支持string和object两种,再 回头看,结合panel组件的属性转换器代码,不难可以看出,properties属性规范了组件实例各属性的数据类型,不过properties能规范 的类型只有boolen,number,string三种。
特殊的,对于width,height,left,top这四个属性,优先级最高的是【渠道4】,即最优先从style属性中取,且类型都为number型。
最后的结论
由上述总结,可以得出以下结论:【渠道5】 > 【渠道4】 > 【渠道3】 > 【渠道2】 > 【渠道1】
到此处,我们关心的主要问题就算分析完了。不过还是遗留了一个问题,那就是panel组件的属性转换器中,为何loadingMessage属性不 放在公共的属性转换器里处理?单独作为参数传入的,number,bool,string这三种类型之外的属性,才有这必要。1.3.5的panel源码 为何要把loadingMessage属性特殊对待,本人是没有想明白。
注意:本文的分析基于data-options属性出世之后(印象中是1.2.6版本之后),文中代码是1.3.5版本的源码。
相关文章推荐
- jQuery实现简单拖拽
- jQuery源码解析1(Utilities)
- 用户界面框架jQuery EasyUI示例大全之DataGrid(2/4)
- jQuery-认识JQuery,jQuery选择器
- jQuery13(相对元素的练习)
- jQuery12(prev练习,相对元素)
- jQuery scrollFire插件
- jQuery11(过滤器的3个练习)
- jQuery10(过滤器)
- jQuery9(操作类选择器,开关灯)
- jQuery中使用attr(), prop(), val()获取value的异同
- jQuery8(常见方法next.prev等,常见方法练习)
- jQuery7(多条件选择器,层次选择器)
- jQuery4(3种选择器,选择器获取元素)
- jQuery4(Dom与jQuery对象的相互转换)
- jQuery.extend函数详解--
- JQuery3(map,each,trim方法)
- jQuery2(JQuery实现onload)
- jQuery1(jQuery介绍及$)
- 【jQuery基础学习】12 jQuery学习感想