疑问
- 为什么我们在页面html中编写class为easyui-xxx开头的组件,页面渲染后会自动展现对应的组件样式和功能?
- 为什么我们在页面加载完成后通过事件方法添加的带有easyui-xxx的网页组件不生效?
认识Parser解析器
- 它是EasyUI的基础组件,支撑整个EasyUI框架能够正常使用。
- 在网页渲染过程中,自动解析网页中定义的EasyUI组件。
- 在网页结构变化时,能够手动去解析相关的EasyUI组件。
源码分析
- 源码位于easyui包目录下的src目录中。
- 因为easyui依赖于jQuery包,所以所有的方法、对象都追加到jQuery对象的上或原型链上。
1. easyui对象
$.easyui = {
//获取元素在数组中的位置
indexOfArray: function(a, o, id){},
//移除数组元素
removeArrayItem: function(a, o, id){},
//添加或更新数组元素
addArrayItem: function(a, o, r){},
//获取数组中元素
getArrayItem: function(a, o, id){},
//遍历数组元素
forEach: function(data, deep, callback){}
};
1.1 indexOfArray方法
参数信息
- 数组对象。
- 需要获取下标的数组元素(基本数据类型)或需要获取下标的数组元素的属性名(对象)。
- 若需要获取下标的数组元素是对象,则该值为对象对应判断属性的值。
返回值
- 元素在数组中的下标位置。
示例一:基本数据类型数组
var items = [12, 34, 45, "李四"];
var index = $.easyui.indexOfArray(items, "李四");//3
基本数据类型使用时,只需要传入数组对象和需要获取下标的数据值。
示例二:对象数组,获取对象所在数组的下标值
var items = [{
name: "张三",
age: 12
}, {
name: "李四",
age: 16
}, {
name: "王五",
age: 32
}];
var index = $.easyui.indexOfArray(items, "name", "李四"); //1
1.2 getArrayItem方法
参数信息
- 数组对象。
- 需要获取下标的数组元素(基本数据类型)或需要获取下标的数组元素的属性名(对象)。
- 若需要获取下标的数组元素是对象,则该值为对象对应判断属性的值。
返回值
- 获取存在于数组中的元素信息。
示例:根据数组元素对象的指定属性名值获取指定的数组元素对象
var items = [{
name: "张三",
age: 12
}, {
name: "李四",
age: 16
}, {
name: "王五",
age: 32
}];
var obj = $.easyui.getArrayItem(items, "name", "李四");//{name: "李四", age: 16}
1.3 addArrayItem方法
参数信息
- 数组对象。
- 需要获取下标的数组元素(基本数据类型)或需要获取下标的数组元素的属性名(对象)。
- 要插入数组的对象值。
示例一:向数组中插入元素对象,若存在,则更新对象
var items = [{
name: "张三",
age: 12
}, {
name: "李四",
age: 16
}, {
name: "王五",
age: 32
}];
$.easyui.addArrayItem(items, "name", {
name: "赵云",
age: 40
});
var items = [{
name: "张三",
age: 12
}, {
name: "李四",
age: 16
}, {
name: "王五",
age: 32
}];
$.easyui.addArrayItem(items, "name", {
name: "李四",
age: 40
});
1.4 removeArrayItem方法
参数信息
- 数组对象。
- 要移除数组元素值(基本类型数据)或移除对象的某个属性名
- 要移除对象的某个属性名对应的值。
示例一:移除数组中对象元素
var items = [{
name: "张三",
age: 12
}, {
name: "李四",
age: 16
}, {
name: "王五",
age: 32
}];
$.easyui.removeArrayItem(items, "name", "李四");
示例二:移除数组中除字符串外基本数据类型元素
var items = [{
name: "张三",
age: 12
}, {
name: "李四",
age: 16
}, {
name: "王五",
age: 32
}, 12];
$.easyui.removeArrayItem(items, 12);
注:不能移除字符串类型元素(第二个参数若为字符串,只能是数组元素对象的某个属性值必须配合第三个元素使用)。
1.5 forEach方法
参数信息
- 要遍历的数组
- 是否深度遍历,true-深度遍历,false-只遍历第一层。
- 遍历数组时的回调方法,参数为数组元素对象,返回false可阻止继续遍历。
示例一:遍历并筛选出符合条件元素(不深度遍历)
var items = [{
name: "张三",
age: 12
}, {
name: "李四",
age: 16
}, {
name: "王五",
age: 32
}, {
name: "赵云",
age: 25,
children: [{
name: "王麻子",
age: 24
}]
}];
var resultItems = [];
$.easyui.forEach(items, false, function(node) {
if (node.age < 25) {
resultItems.push(node);
}
});
console.log(resultItems);
示例二:遍历并筛选出符合条件元素(深度遍历)
var items = [{
name: "张三",
age: 12
}, {
name: "李四",
age: 16
}, {
name: "王五",
age: 32
}, {
name: "赵云",
age: 25,
children: [{
name: "王麻子",
age: 24
}]
}];
var resultItems = [];
$.easyui.forEach(items, true, function(node) {
if (node.age < 25) {
resultItems.push(node);
}
});
console.log(resultItems);
注意:该方法的第二个参数。
2. parser对象
// 是否自动解析
auto: true,
// 空函数
emptyFn: function(){},
// 解析完成后回调方法,一般用于重写
onComplete: function(context){},
// 组件名数组,用来识别组件
plugins:[],
// 解析标签
parse: function(context){},
// 解析高度或宽度值
parseValue: function(property, value, parent, delta){},
// 解析组件的属性
parseOptions: function(target, properties){},
// 解析并设置网页盒子模型或固定定位
parseVars: function(){}
2.1 emptyFn方法
待使用的空方法。
2.2 onComplete方法
解析指定选择器的网页元素内easyui组件后的回调方法。
参数信息
- context
- 传入的选择器字符串;例如:‘#xxx’、'.xxx’等
示例一:解析整个网页
var htm = "<div id=\"mypanel\" class=\"easyui-panel\" title=\"Basic Panel\" style=\"width: 400px; height: 200px;\"></div>";
$("body").append(htm);
$.parser.onComplete = function(context) {
console.log("解析完成!");
console.log(context);
}
$.parser.parse();
- 若parse方法不传参数表示解析整个网页。
- parse方法可传入选择器字符串,表示解析其下子内容。
2.3 plugins组件数组
- easyui里面所有组件名都在此定义,在页面解析组件时会判断是否是easyui组件来控制是否解析组件。
- 若我们自定义组件,必须在此插件数组中追加组件名,不然在页面中无法自动解析组件。
组件集合概述
序号 | 组件名 | 描述 |
---|---|---|
1 | draggable | 拖动 |
2 | droppable | 放置 |
3 | resizable | 调整大小 |
4 | pagination | 分页 |
5 | tooltip | 提示框 |
6 | linkbutton | 按钮 |
7 | menu | 菜单 |
8 | sidemenu | 侧栏菜单 |
9 | menubutton | 菜单按钮 |
10 | splitbutton | 分割按钮 |
11 | switchbutton | 开关按钮 |
12 | progressbar | 进度条 |
13 | radiobutton | 单选按钮 |
14 | checkbox | 复选框 |
15 | tree | 树 |
16 | textbox | 文本框 |
17 | passwordbox | 密码框 |
18 | maskedbox | 掩码框 |
19 | filebox | 文本框 |
20 | combo | 组合 |
21 | combobox | 组合框 |
22 | combotree | 组合树 |
23 | combogrid | 组合网格 |
24 | combotreegrid | 树形表格下拉框 |
25 | tagbox | 标签框 |
26 | numberbox | 数字框 |
27 | validatebox | 验证框 |
28 | searchbox | 搜索框 |
29 | spinner | 调整器 |
30 | numberspinner | 数字调整器 |
31 | timespinner | 时间调整器 |
32 | datetimespinner | 日期时间调整器 |
33 | calendar | 日历控件 |
34 | datebox | 日期控件 |
35 | datetimebox | 日期时间控件 |
36 | timepicker | 时间选择器 |
37 | slider | 滑块 |
38 | layout | 布局组件 |
39 | panel | 面板组件 |
40 | datagrid | 表格 |
41 | propertygrid | 属性表格 |
42 | treegrid | 树形表格 |
43 | datalist | 数据列表 |
44 | tabs | 选项卡 |
45 | accordion | 手风琴 |
46 | window | 弹框 |
47 | dialog | 对话框 |
48 | form | 表单 |
2.4 parseVars方法
用于控制easyui框架在流程里面的解析流程。
parseVars: function() {
// 校验网页的盒子模型,盒子的width是否包含内边距padding
var d = $('<div style="position:absolute;top:-1000px;width:100px;height:100px;padding:5px"></div>').appendTo('body');
$._boxModel = d.outerWidth() != 100;
d.remove();
d = $('<div style="position:fixed"></div>').appendTo('body');
$._positionFixed = (d.css('position') == 'fixed');
d.remove();
}
- $._boxModel为false,表示盒子的width包含了border宽度和padding的宽度。
- $._positionFixed为true,表示是否支持固定定位,true表示支持,false表示不支持。
2.5 parseOptions方法
用于解析easyui组件的data-options相关属性。
parseOptions: function(target, properties) {
var t = $(target);
var options = {};
//1. 判断标签中是否包含data-options属性设置,若有则直接解析设置的属性放置到options对象中。
var s = $.trim(t.attr('data-options'));
if (s) {
if (s.substring(0, 1) != '{') {
s = '{' + s + '}';
}
options = (new Function('return ' + s))();
}
//2. 解析标签中style属性中有关像素大小设置的属性,并放置到options对象中
$.map(['width', 'height', 'left', 'top', 'minWidth', 'maxWidth', 'minHeight', 'maxHeight'], function(p) {
var pv = $.trim(target.style[p] || '');
if (pv) {
if (pv.indexOf('%') == -1) {
pv = parseInt(pv);
if (isNaN(pv)) {
pv = undefined;
}
}
options[p] = pv;
}
});
//3. 解析标签中其他属性,并将这些属性设置以对应基础类型放置到options对象中
if (properties) {
var opts = {};
for (var i = 0; i < properties.length; i++) {
var pp = properties[i];
if (typeof pp == 'string') {
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);
}
//4. 返回解析后的组件属性对象
return options;
}
示例一:解析标签的相关属性,获取easyui组件需要的属性值
var htm = "<div id=\"mypanel\" class=\"easyui-panel\" title=\"基础面板\" style=\"width:700px;height:200px; padding:10px;\" data-options=\"content:'面板内容'\"></div>";
$("body").append(htm);
var options = $.parser.parseOptions(document.querySelector('#mypanel'));
console.log(options);
注:参数的target需要传入js原生选择器获取的值。
2.6 parseValue方法
// property -- 属性名,例如:width
// value -- 属性值,例如:100%
// parent -- 该元素的父元素
// delta -- 偏移值
parseValue: function(property, value, parent, delta) {
// 1. 若偏移值不设置,则为0
delta = delta || 0;
var v = $.trim(String(value || ''));
// 2. 获取值的后缀符号,如果是%,表示相对值,计算像素值
var endchar = v.substr(v.length - 1, 1);
if (endchar == '%') {
v = parseFloat(v.substr(0, v.length - 1));
// 3. 判断是设置width宽度还是height高度,通过父元素来获取
if (property.toLowerCase().indexOf('width') >= 0) {
delta += parent[0].offsetWidth - parent[0].clientWidth;
v = Math.floor((parent.width() - delta) * v / 100.0);
} else {
delta += parent[0].offsetHeight - parent[0].clientHeight;
v = Math.floor((parent.height() - delta) * v / 100.0);
}
} else {
v = parseInt(v) || undefined;
}
// 4. 返回计算后的值
return v;
}
认识clientWidth、offsetWidth的区别?
- clientWidth:对象内容的可视区的宽度,不包滚动条等边线,会随对象显示大小的变化而改变
- offsetWidth:对象整体的实际宽度,包滚动条等边线,会随对象显示大小的变化而改变。
示例一:panel组件中使用示例
2.7 parse方法
解析指定元素或者整个网页body的easyui组件信息。
parse: function(context) {
// 存放easyui组件名数组,元素结构为:{name: 组件名, jq: 对应的组件jq对象}
var aa = [];
// 1.遍历获取指定元素中的所有涉及到的easyui组件
for (var i = 0; i < $.parser.plugins.length; i++) {
var name = $.parser.plugins[i];
var r = $('.easyui-' + name, context);
if (r.length) {
if (r[name]) {
r.each(function() {
$(this)[name]($.data(this, 'options') || {});
});
} else {
aa.push({ name: name, jq: r });
}
}
}
// 2. 通过easyloader组件载入对应的组件
if (aa.length && window.easyloader) {
var names = [];
for (var i = 0; i < aa.length; i++) {
names.push(aa[i].name);
}
easyloader.load(names, function() {
for (var i = 0; i < aa.length; i++) {
var name = aa[i].name;
var jq = aa[i].jq;
jq.each(function() {
$(this)[name]($.data(this, 'options') || {});
});
}
// 3. 回调解析成功事件
$.parser.onComplete.call($.parser, context);
});
} else {
$.parser.onComplete.call($.parser, context);
}
}
示例一:解析指定网页部分
<div id="mydiv"></div>
<script type="text/javascript">
$(function() {
var htm = "<div id=\"mypanel\" class=\"easyui-panel\" title=\"基础面板\" style=\"width:700px;height:200px; padding:10px;\" data-options=\"content:'面板内容'\"></div>";
$("#mydiv").append(htm);
$.parser.parse($("#mydiv"));
});
</script>
注:只能解析指定元素其下子或孙子节点的网页元素。
示例二:解析整个网页
<div id="mydiv"></div>
<script type="text/javascript">
$(function() {
var htm = "<div id=\"mypanel\" class=\"easyui-panel\" title=\"基础面板\" style=\"width:700px;height:200px; padding:10px;\" data-options=\"content:'面板内容'\"></div>";
$("#mydiv").append(htm);
$.parser.parse();
});
</script>
注:解析整个网页body里面的内容。
解析时机
在网页标签渲染完成后,easyloader简单加载器构建之前触发解析网页操作。