我想怎么页面怎么动,他就怎么动。
时间:2011-03-10 来源:56707801
最近一直在做公司网站的js,一直都想做一些自己的东西,想要有一个自己的框架,也可以说是一个公司的框架(因为公司目前只有我一个人专职写js =。=!),一直都是在挖其他时下流行框架的代码。一开始总是在挖Extjs的代码,觉得它类的继承和扩展很符合我的口味,最近Tangram挖的比较多,很多工具类都很简洁。
在实际的工作中,我也慢慢的有了一些自己的想法。因为公司的页面模版很杂乱,在我刚来公司的时候,基本上是每个页面引用若干脚本,由美工根据业务来配置的,所以我第一个任务就是把这些js全部收入到自己的代码库里面,然后合并成一个算是分类js的东西,每个类别由美工来找我,我这边控制合并的业务功能,总算是不用被美工牵着鼻子走了。
但是在现在的情况还是很复杂,不可能针对每种模版做一个套js,总避免不了一些冗余,而且模版的修改上线是一个很繁琐的流程,这个时候,我就开始有了一个设想,完全由js来控制页面,而不是仅仅被使用。js不只是一个工具,他可以有自己的生命。
如何让js来控制页面,这里面最主要就有2个问题要解决。
第一,js的数据来源,毕竟页面上用到的业务数据,肯定是要由有台来提供的,以前是js提供方法,然后模版上调用的时候直接将数据填充进去,这样很被动,耦合太深。
第二,如何控制各种js执行的时间,以前由模版来调用的时候,都是直接在需要调用的位置直接调用js函数,但是,其一,他阻塞(虽然我不确定setInterval是不是多线程不会阻塞,但是起码不会更糟),其二就是不够灵活,如果业务发生一点变换,可能就需要修改模版来改变函数的调用时机。
第一个问题比较好解决,我做了一个类,用来装配全局的数据,然后协调好后台开发给我在页面上提供这些数据。
至于第二个问题,我想到开发一个代理层,这个代理层能够浏览器兼容的,处理各种元素的加载事件,这样我就可以保证在执行某些函数时,不会发生元素还未加载的问题(以前可能都用domload和onload,但是我感觉这些都很慢),而且还可以随时随地的执行这些函数,达到最快的用户响应。以前同步加载js的时候,也许很少碰到元素未加载的情况,但是当开始无阻塞的加载js的时候,就可能会面临这种问题。
现在我要开始做一些改革了,比如延时加载某些功能,可能用户不太重视的功能,我们可以放到后面去加载,某些对用户体验影响比较大的功能,我们可以保证他展现出来立马就可以交互。
好吧,我承认,最主要的目的就是不用修改模版就可以达到我想要做的任何事情;)
下面附上代理层的部分代码:
1 /**
2 * 扫描列表
3 * @private
4 * @property
5 */
6 __scanList : [],
7
8 /**
9 * 扫描函数 标识
10 * @private
11 * @property
12 */
13 __scanHandler : null,
14
15 /**
16 * 扫描间隔
17 * @private
18 * @property
19 */
20 __scanInterval : 500,
21
22 /**
23 * 给iframe和img对象添加onload事件(浏览器兼容)
24 * @param {Htmlelement} element the iframe or img object
25 * @param {Object} fn callback
26 */
27 __attachOnload : function(element, fn){
28 if (fe.isIE) {
29 element.attachEvent("onload", fn);
30 }
31 else {
32 element.onload = fn;
33 }
34 },
35
36 /**
37 * 将该元素的响应代理给浏览器处理
38 * @method
39 * @param {Object} o lazyload list object
40 * @param {HTMLElement} element the element
41 */
42 __agency : function(o, element){
43 var me = this;
44 o.hosting = true;
45 this.__attachOnload(element, function(){
46 me.__call(o, element);
47 });
48 },
49
50 /**
51 * 调用该元素的回调函数
52 * @method
53 * @param {Object} o
54 * @param {Object} element
55 */
56 __call : function(o, element){
57 o.loaded = true;
58 for (var i = 0, leni = o.listeners.length; i < leni; i++) {
59 if (typeof o.listeners[i] == 'function')
60 o.listeners[i].call(element);
61 }
62 },
63
64 /**
65 *
66 * 根据是否能够获取到指定元素为判断依据
67 *
68 * 如果已经可以获取到该元素,则判断该元素的类型
69 * 如果该元素是iframe,则根据iframe.document.readyState来判断iframe是否已经加载完成,
70 * 如果iframe已经加载完成,则直接调用回调函数,
71 * 否则封装一个代理函数,交由浏览器处理onload
72 *
73 * warning:如果该iframe跨域,无法判断是否加载完成,则默认为未加载完成
74 * 如果这个时候iframe已经加载完成,可能会丢失该响应
75 *
76 * 如果该元素是img
77 * 则 根据img.readyState和img.complete来判断img是否已经加载完成,
78 * 如果img已经加载完成,则直接调用回调函数,
79 * 否则封装一个代理函数,交由浏览器处理onload
80 *
81 * 否则认为该元素已经加载完成
82 * @param {String} id Element's ID
83 * @return {Boolean} the element is loaded
84 */
85 __isLoaded : function(el){
86 if(el){
87 if(el.tagName && el.tagName == 'iframe'){
88 try {
89 if (el.contentWindow.document.readyState == 'complete') {
90 return true;
91 }
92 }
93 catch (e) {
94 /**
95 * 没有找到合适的办法来判断是跨域还是未加载document对象,只能按照跨域处理
96 */
97 //if(typeof console != 'undefinded'){
98 // alert(e.name) ;
99 //}
100 };
101 /**
102 * 将事件响应交给浏览器代理
103 */
104 me.__agency(o, el);
105 return false;
106 }
107 else if(el.tagName && el.tagName == 'img'){
108 if (el.readyState == 'complete' || el.complete) {
109 return true;
110 }
111
112 /**
113 * 将事件响应交给浏览器代理
114 */
115 me.__agency(o, el);
116 return false;
117 }
118 else return true;
119 }
120 else return false;
121 },
122
123 /**
124 * 给指定id的元素添加扫描事件,
125 * 传入的回调函数将在该元素加载时被调用
126 * 如果添加事件的时候元素已经加载完成,则会被立即调用
127 *
128 * warning:
129 * @method
130 * @param {Object} id
131 * @param {Object} fn
132 */
133 onload: function(id, fn){
134 /**
135 * 如果在扫描列表中找到该元素,则进行判断:
136 * 当该元素已经加载完成时,直接执行回调函数
137 * 当该元素还未加载完成时,将回调函数加入队列
138 */
139 for (var i = 0, leni = this.__scanList.length; i < leni; i++) {
140 if (this.__scanList[i].id == id) {
141 if (this.__scanList[i].loaded == true) {
142 fn.call(this.get(id));
143 return;
144 }
145 else {
146 this.__scanList[i].listeners.push(fn);
147 return;
148 }
149 }
150 }
151
152 /**
153 * 如果没有在扫描元素列表中找到该元素,
154 * 则讲该元素加入扫描列表
155 */
156 this.__scanList.push({
157 id: id,
158 loaded: false,
159 listeners: [fn],
160 hosting: false
161 });
162 },
163
164 /**
165 * 开启引擎
166 * @method
167 */
168 start : function(){
169 var me = this;
170 if (!this.__scanHandler) {
171 /**
172 * init the interval
173 * scan the lazyload elements every 500 milliseconds
174 */
175 this.__scanHandler = setInterval(function(){
176 /**
177 * 遍历扫描列表,判断其中的元素是否加载完成
178 */
179 for (var i = 0, leni = me.__scanList.length; i < leni; i++) {
180 var o = me.__scanList[i];
181 /**
182 * 如果已经加载或者已经交给浏览器代理的,不进行判断
183 */
184 if (o.loaded)
185 continue;
186 if (o.hosting)
187 continue;
188
189 /**
190 * 如果已经加载则直接调用回调函数
191 */
192 var el = me.get(o.id);
193 if (me.__isLoaded(el)) {
194 me.__call(o, el);
195 }
196 }
197 }, this.__scanInterval);
198 }
199 }