jQuery EasyUI parser 的使用场景
parser,故名意思,就是解析器的意思,别看他只有那么几行代码,jQuery Easyui 能够根据class就能正常渲染页面全靠它了。一般情况下,我们并用不到解析器,本文主要讨论一下,什么情况下会用到它,如何使用。
自动调用parser:
我们之所以在页面中,只要书写相应的class,Easyui就能成功渲染页面,这是因为解析器在默认情况下,会在文档装载完成的时候($(document).ready)被调用一次,而且是渲染整个页面。
手动调用parser:
有些童鞋反映,当页面装载完后,如果用javascript生成的DOM中包含了Easyui支持控件的class,比如说,用javascript生成了以下代码:
1 | < div class = "easyui-accordion" id = "tt" > |
2 | < div title = "title1" >1</ div > |
3 | < div title = "title2" >2</ div > |
虽然页面上有这样的DOM了,但是没有被渲染为Easyui的accordion插件,原因很简单,Easyui并不会一直监听页面,所以不会主动渲染,这时候就需要手工调用Easyui的parser进行解析了。不过手工调用需要注意以下几点:
(1) 解析目标为指定DOM的所有子孙元素,不包含这个DOM自身:
比如上面代码生成的HTML,id="tt"是我们想要的手风琴DIV,像下面代码去手工解析的话是得不到你想要的结果的:
1 | $.parser.parse($( '#tt' )); |
道理很简单,parser只渲染tt的子孙元素,并不包括tt自身,而它的子孙元素并不包含任何Easyui支持的控件class,所以这个地方就得不到你想要的手风琴效果了,应该这样写:
1 | $.parser.parse($( '#tt' ).parent()); |
渲染tt的父节点的所有子孙元素就可以了,个人觉得通过jQuery的parent()方法是最安全不过的了,不管你的javascript输出了什么DOM,直接渲染其父节点就可以保证页面能被正确解析。
(2) 某些组件无法多次解析同一个DOM元素:
如果页面上本身就有tt元素:
1 | < div class = "easyui-accordion" id = "tt" > |
页面装载完,你通过脚本向tt元素append两个子DIV,然后解析:
1 | $( '#tt' ).append( '<div title="title1">1</div><div title="title2">2</div>' ) |
2 | $.parser.parse($( '#tt' ).parent()); |
不要以为你会得到一个满意的accordion,你什么也得不到,因为页面初始化的时候parser就主动渲染过tt元素,这时候tt已经被parser重构,你再到脚本中append,得到的DOM结构,其实并不是你预想的结果了,所以要避免这种用法。
parser组件作为easyui的基础类组件,其实担当者相当重要的角色,我们之所以能够通过定义一些样式就能完成对页面的自动渲染,完全是依靠parser组件;同时parser组件内部还定义了公用的属性转化器等几个实用工具,话不多说了,直接上带有注释的代码:
1 /**
2 * jQuery EasyUI 1.3.1
3 * Licensed under the GPL terms
4 * To use it on other terms please contact us
5 * Copyright(c) 2009-2012 stworthy [ stworthy@gmail.com ]
6 * (function($){})(jQuery) 定义一个匿名函数,并将jQuery对象作为实参传给这个匿名函数运行之。
7 *
8 */
9 (function($){
10 //在jQuery对象定义全局属性parser,parser属性也是对象,其自身又包含:auto,onComplete,plugins,parse,parseOptions这几个属性。
11 $.parser = {
12 //设置是否自动渲染页面(或者为DOM块)的开关
13 auto: true,
14 //渲染完成后会触发onComplete事件(如果该事件绑定了处理函数,则每次调用parser渲染DOM后都会触发一次该事件绑定的处理函数)
15 onComplete: function(_1){
16 },
17 //plugins属性枚举出了所有组件名称标识。
18 plugins: ["draggable", "droppable", "resizable", "pagination", "linkbutton", "menu", "menubutton", "splitbutton", "progressbar", "tree", "combobox", "combotree", "combogrid", "numberbox", "validatebox", "searchbox", "numberspinner", "timespinner", "calendar", "datebox", "datetimebox", "slider", "layout", "panel", "datagrid", "propertygrid", "treegrid", "tabs", "accordion", "window", "dialog"],
19 //parse就是实现渲染DOM的核心方法了,入参为DOM对象,其实也就是某个DOM块,不传入参的话,则是对整个页面进行渲染。
20 parse: function(_2){
21 var aa = [];
22 for (var i = 0; i < $.parser.plugins.length; i++) {
23 var _3 = $.parser.plugins[i];
24 //查找指定DOM下的特定的easyui组件,即以_2为上下文查找_3组件对应的DOM对象,_2为空的话,则在整个document为上下文。
25 var r = $(".easyui-" + _3, _2);
26 if (r.length) {
27 if (r[_3]) {
28 //调用plugins某个里_3组件对象的构造函数,上下文当然也就是r对象自身了,也就是构造函数里面使用的this
29 //这个地方不要犯晕,r[_3]其实是function类型,其实也就是各个组件对应的构造函数。
30 //例如panel组件的代码里总有这样的定义:$.fn.panel = function(jq, options){...},这个匿名函数我就称之为组件的构造函数(这个称呼只是个约定)。
31 //而jQuery扩展fn的话等于扩展了全局方法到jQuery对象上,也就是所有的jQuery对象都具备了panel这个方法,
32 //如果没有使用easyloader的话,r对应其实已经具备了easyui所有组件的构造函数。
33 //这个地方如果不理解的话,请去翻阅jQuery的对象模型以及继承关系方面的资料。
34 r[_3]();
35 }
36 else {//这个else分支也只有在使用easyloader的时候才会调用到了
37 aa.push({
38 name: _3,
39 jq: r
40 });
41 }
42 }
43 }
44 if (aa.length && window.easyloader) {
45 var _4 = [];
46 for (var i = 0; i < aa.length; i++) {
47 _4.push(aa[i].name);
48 }
49 easyloader.load(_4, function(){
50 for (var i = 0; i < aa.length; i++) {
51 var _5 = aa[i].name;
52 var jq = aa[i].jq;
53 //调用组件的构造函数
54 jq[_5]();
55 }
56 $.parser.onComplete.call($.parser, _2);
57 });
58 }
59 else {
60 //这地方调用绑定的onComplete事件,入参为_2这个上下文。
61 //为什么这个写法?还不是想把_2作为参数传给onComplete事件绑定的函数处理程序嘛。
62 $.parser.onComplete.call($.parser, _2);
63 }
64 },
65 //公用的属性转换器,兼容data-options方式和老方式,功能可谓灵活而强大。
66 parseOptions: function(_6, _7){
67 var t = $(_6);
68 var _8 = {};
69 var s = $.trim(t.attr("data-options"));
70 if (s) {
71 var _9 = s.substring(0, 1);
72 var _a = s.substring(s.length - 1, 1);
73 if (_9 != "{") {
74 s = "{" + s;
75 }
76 if (_a != "}") {
77 s = s + "}";
78 }
79 _8 = (new Function("return " + s))();
80 }
81 if (_7) {
82 var _b = {};
83 for (var i = 0; i < _7.length; i++) {
84 var pp = _7[i];
85 if (typeof pp == "string") {
86 if (pp == "width" || pp == "height" || pp == "left" || pp == "top") {
87 _b[pp] = parseInt(_6.style[pp]) || undefined;
88 }
89 else {
90 _b[pp] = t.attr(pp);
91 }
92 }
93 else {
94 for (var _c in pp) {
95 var _d = pp[_c];
96 if (_d == "boolean") {
97 _b[_c] = t.attr(_c) ? (t.attr(_c) == "true") : undefined;
98 }
99 else {
100 if (_d == "number") {
101 _b[_c] = t.attr(_c) == "0" ? 0 : parseFloat(t.attr(_c)) || undefined;
102 }
103 }
104 }
105 }
106 }
107 $.extend(_8, _b);
108 }
109 return _8;
110 }
111 };
112 //文档准备好后,根据$.parser.auto的设置来决定是否自动渲染
113 $(function(){
114 if (!window.easyloader && $.parser.auto) {
115 $.parser.parse();
116 }
117 });
118 //扩展_outerWidth方法到jQuery对象上,用于兼容IE这种不是正规盒子模型的另类浏览器
119 $.fn._outerWidth = function(_e){
120 if (_e == undefined) {
121 if (this[0] == window) {
122 return this.width() || document.body.clientWidth;
123 }
124 return this.outerWidth() || 0;
125 }
126 return this.each(function(){
127 if (!$.support.boxModel && $.browser.msie) {
128 $(this).width(_e);
129 }
130 else {
131 $(this).width(_e - ($(this).outerWidth() - $(this).width()));
132 }
133 });
134 };
135 //扩展_outerHeight方法到jQuery对象上,用于兼容IE这种不是正规盒子模型的另类浏览器
136 $.fn._outerHeight = function(_f){
137 if (_f == undefined) {
138 if (this[0] == window) {
139 return this.height() || document.body.clientHeight;
140 }
141 return this.outerHeight() || 0;
142 }
143 return this.each(function(){
144 if (!$.support.boxModel && $.browser.msie) {
145 $(this).height(_f);
146 }
147 else {
148 $(this).height(_f - ($(this).outerHeight() - $(this).height()));
149 }
150 });
151 };
152 //获取/设置属性的方法(attributes和properties)。获取在匹配的元素集中的第一个元素的属性值。prop()方法应该被用来处理boolean attributes/properties以及在html(比如:window.location)中不存在的properties。其他所有的attributes(html 中你看到的那些)可以而且应该继续使用.attr()方法来进行操作。
153 $.fn._propAttr = $.fn.prop || $.fn.attr;
154 })(jQuery);