Parser
parser模块是easyloader第一个加载的模块
在前面的笔记中,我们分析了easyloader源代码,知道easyui加载模块组件可以有2种方式:
- 通过设置class属性的方式
- 使用JavaScript的方式进行渲染
这本质上是通过parser解析器来实现的,一般是自动触发完成整个页面的解析。
parser主要作用
parser自动渲染:
最主要的运用场景:
只要我们书写相应的class,easyui就能成功的渲染页面。
这是因为解析器在默认情况下,会在dom加载完成的时候($(docunment).ready)
被调用,而且是渲染整个页面。parser解析器手动渲染:
需要手动调用的情况是:
当页面已经加载完成,但是此时我们使用js生成的DOM中包含了easyui支持的class,并且我们也有将其渲染成easyui组件的需求。在这种情况下就需要手动调用parser
<div class="easyui-accordion" id="tt">
<div title="title1">1</div>
<div title="title2">2</div>
</div>
当上面代码的生成是在easyui渲染界面完成之后,easyui不会一直监听页面,所以该段代码不会被渲染成“手风琴DIV”的样式。
此时就需要我们手动去渲染了。
注意:
解析目标位指定DOM的所有子孙元素,不包括DOM本身。
$parser.parser($('tt').parent())
并非
$.parser.parse($('#tt'));
尽量不要多次解析同一个DOM元素(ID):页面初始化就已经主动渲染过dom节点了,你再次手动解析该dom节点时该dom已经被parser重构,得到的DOM就并非是你料想的结果,该方式应该尽量避免。
- 用法总结
$.parser.parse(); // 不带参数,默认解析该页面中所有被定义为easyui组件的节点就是解析整个页面
$.parser.parse('#cc'); // 带一个jquery选择器,使用这种方式我们可以单独解析局部的easyui组件节点
注意:
上面的id不能是为当前组件的id,必须为当前组件父容器的id。也就是说这个jquery选择器必须是解析组件的父级以上的节点,潜台词就是这个查找出来的节点相当于一个容器,它只会解析容器里面的内容。
<div id="cc">
<div id="Window" class="easyui-window"></div>
</div>
$.parser.parse('#cc');
这么使用id为Window的节点才会正常。
$.parser.parse();
不传参是解析页面中所有定义为easyui组件的节点。
- 特性
名称 | 类型 | 描述 | 默认值 |
---|---|---|---|
$.parser.auto | boolean | 定义是否自动解析easyui组件 | true |
- 事件
名称 | 参数 | 描述 |
---|---|---|
$.parser.onComplete | context | 当解析器完成解析动作的时候触发 |
- 方法
名称 | 参数 | 描述 |
---|---|---|
$.parser.parse | context | 解析easyui组件 |
parser源代码阅读分析笔记:
源代码很多,只截取了部分主要的放了上来
/**
* parser模块主要作用是解析页面中easyui的控件
*/
$.parser = {
//是否自动解析
auto : true,
//渲染完成后会触发onComplete事件,若该事件绑定了处理函数,则每次调用parser渲染DOM后都会触发一次该事件绑定的处理函数
onComplete : function(_b) {
},
//plugins属性枚举出了所有可以被parse解析的控件组件名称
plugins : [ "draggable", "droppable", "resizable", "pagination",
"tooltip", "linkbutton", "menu", "menubutton", "splitbutton",
"switchbutton", "progressbar", "tree", "textbox",
"passwordbox", "filebox", "combo", "combobox", "combotree",
"combogrid", "combotreegrid", "numberbox", "validatebox",
"searchbox", "spinner", "numberspinner", "timespinner",
"datetimespinner", "calendar", "datebox", "datetimebox",
"slider", "layout", "panel", "datagrid", "propertygrid",
"treegrid", "datalist", "tabs", "accordion", "window",
"dialog", "form" ],
/**
* function(context) 解析函数
* 实现渲染DOM的核心方法,传入参数为DOM对象,其实也就是某个DOM块,不传入参的话,则是对整个页面进行渲染。
*/
parse : function(_c) {
var aa = [];
for (var i = 0; i < $.parser.plugins.length; i++) {
// _d=name , 控件名字
var _d = $.parser.plugins[i];
// 查找指定DOM下的特定的easyui组件,也就是查找class为easyui-控件名字的jq对象,即以_c为上下文查找_d组件对应的DOM对象,比如:easyui-layout._c为空的话,则在整个document为上下文
var r = $(".easyui-" + _d, _c);
if (r.length) {
if (r[_d]) { //如果有这个对象
/**
* 调用plugins某个里_d组件对象的类似于构造函数的那个匿名函数(就把它叫做构造函数了。。仅仅是因为叫起来方便)
* 上下文就是r对象自身了,也就是构造函数里面使用的this
* 这个地方容易晕,r[_d]其实是function类型,其实也就是各个组件对应的构造函数
* 例如panel组件的代码里有这样的定义:
* $.fn.panel = function(jq, options){...}
* jQuery扩展fn的话等于扩展了全局方法到jQuery对象上,也就是所有的jQuery对象都具备了panel这个方法
* 如果没有使用easyloader的话,r对应其实已经具备了easyui所有组件的构造函数
*/
r.each(function() {
$(this)[_d]($.data(this, "options") || {});
});
}
else { //这个else分支也只有在使用easyloader的时候才会调用到
aa.push({
name : _d,
jq : r
});
}
}
}
if (aa.length && window.easyloader) {
var _e = [];
for (var i = 0; i < aa.length; i++) {
_e.push(aa[i].name);
}
easyloader.load(_e, function() {
for (var i = 0; i < aa.length; i++) {
var _f = aa[i].name;
var jq = aa[i].jq;
//调用组件的构造函数
jq.each(function() {
$(this)[_f]($.data(this, "options") || {});
});
}
$.parser.onComplete.call($.parser, _c);
});
}
else {
/**
* 调用绑定的onComplete事件
* 传入参数为上下文_c
* 目的就是把_c作为参数传给onComplete事件绑定的函数来处理程序
*/
$.parser.onComplete.call($.parser, _c);
}
},
//公用的属性转换器
parseValue : function(_10, _11, _12, _13) {
_13 = _13 || 0;
var v = $.trim(String(_11 || ""));
var _14 = v.substr(v.length - 1, 1);
if (_14 == "%") {
v = parseInt(v.substr(0, v.length - 1));
if (_10.toLowerCase().indexOf("width") >= 0) {
v = Math.floor((_12.width() - _13) * v / 100);
} else {
v = Math.floor((_12.height() - _13) * v / 100);
}
} else {
v = parseInt(v) || undefined;
}
return v;
},
parseOptions : function(_15, _16) {
var t = $(_15);
var _17 = {};
var s = $.trim(t.attr("data-options"));
if (s) {
if (s.substring(0, 1) != "{") {
s = "{" + s + "}";
}
_17 = (new Function("return " + s))();
}
$.map([ "width", "height", "left", "top", "minWidth", "maxWidth",
"minHeight", "maxHeight" ], function(p) {
var pv = $.trim(_15.style[p] || "");
if (pv) {
if (pv.indexOf("%") == -1) {
pv = parseInt(pv);
if (isNaN(pv)) {
pv = undefined;
}
}
_17[p] = pv;
}
});
if (_16) {
var _18 = {};
for (var i = 0; i < _16.length; i++) {
var pp = _16[i];
if (typeof pp == "string") {
_18[pp] = t.attr(pp);
} else {
for ( var _19 in pp) {
var _1a = pp[_19];
if (_1a == "boolean") {
_18[_19] = t.attr(_19) ? (t.attr(_19) == "true")
: undefined;
} else {
if (_1a == "number") {
_18[_19] = t.attr(_19) == "0" ? 0
: parseFloat(t.attr(_19))
|| undefined;
}
}
}
}
}
$.extend(_17, _18);
}
return _17;
}
};
//文档准备好后,根据$.parser.auto的设置来决定是否自动渲染
$(function() {
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();
if (!window.easyloader && $.parser.auto) {
$.parser.parse();
}
});
//扩展_outerWidth方法到jQuery对象上,用于兼容IE这种不是正规盒子模型的浏览器
$.fn._outerWidth = function(_1b) {
if (_1b == undefined) {
if (this[0] == window) {
return this.width() || document.body.clientWidth;
}
return this.outerWidth() || 0;
}
return this._size("width", _1b);
};
//扩展_outerHeight方法到jQuery对象上,用于兼容IE这种不是正规盒子模型的浏览器
$.fn._outerHeight = function(_1c) {
if (_1c == undefined) {
if (this[0] == window) {
return this.height() || document.body.clientHeight;
}
return this.outerHeight() || 0;
}
return this._size("height", _1c);
};
$.fn._scrollLeft = function(_1d) {
if (_1d == undefined) {
return this.scrollLeft();
} else {
return this.each(function() {
$(this).scrollLeft(_1d);
});
}
};
$.fn._propAttr = $.fn.prop || $.fn.attr;
$.fn._size = function(_1e, _1f) {
if (typeof _1e == "string") {
if (_1e == "clear") {
return this.each(function() {
$(this).css({
width : "",
minWidth : "",
maxWidth : "",
height : "",
minHeight : "",
maxHeight : ""
});
});
} else {
if (_1e == "fit") {
return this.each(function() {
_20(this, this.tagName == "BODY" ? $("body") : $(this)
.parent(), true);
});
} else {
if (_1e == "unfit") {
return this.each(function() {
_20(this, $(this).parent(), false);
});
} else {
if (_1f == undefined) {
return _21(this[0], _1e);
} else {
return this.each(function() {
_21(this, _1e, _1f);
});
}
}
}
}
} else {
return this.each(function() {
_1f = _1f || $(this).parent();
$.extend(_1e, _20(this, _1f, _1e.fit) || {});
var r1 = _22(this, "width", _1f, _1e);
var r2 = _22(this, "height", _1f, _1e);
if (r1 || r2) {
$(this).addClass("easyui-fluid");
} else {
$(this).removeClass("easyui-fluid");
}
});
}