2008年8月30日星期六

如何动态加载 JavaScript 与 CSS

Omar AL Zabir这位MVP总是喜欢搞些稀奇古怪同时又很实用的小东西,并且还十分值得参考。最近他就做了一个叫做ensure的小工具用于动态加载JavaScript、CSS与HTML,而且IE、Firefox、Opera、Safari都支持了,那么我们就来看看ensure是如何做到动态加载JavaScript与CSS的。

在介绍ensure内部的实现之前,让我们先来看看其功能:

ensure({
    html: "popup.html",
    javascript: "popup.js",
    css: "popup.css"
  }, function() {
    Popup.show("hello world");
  }
);

在这段代码中,ensure首先会确保popup.html、popup.js、popup.css这3个文件的加载,如果都没加载过ensure就会动态加载它们;如果已经加载过了,ensure不会再次加载。在确保这3个文件都加载后,ensure会调用后面的匿名函数,也就是执行Popup.show("hello world");。

接下来,就让我们看看ensure是如何动态加载JavaScript与CSS的。

加载JavaScript

在ensure当中,加载JavaScript分两种情况来执行,也就是Safari与非Safari这两种情况。

在IE、Firefox、Opera中加载JavaScript

在这三款浏览器中加载JavaScript,其实只需要创建一个script元素,把src指向要加载的URL,最后把script元素追加到head元素上,那就搞掂了。此项工作是在HttpLibrary.createScriptTag()中完成的。不过我们不仅仅要加载JavaScript,同时还需要知道它什么时候完成加载,这可以通过script元素的onload事件或onreadystatechange事件来实现。

在Safari中加载JavaScript

因为Safari 2不支持onload或者onreadystatechange,所以只能手动通过XHR把URL读去过来,然后再手动eval这段代码,这就带来了一个限制──只能加载本域的JavaScript文件。在ensure当中,eval的工作是通过HttpLibrary.globalEval()来完成的。为了让JavaScript代码在全局(global)上下文中eval,ensure还是使用了创建script元素的方法,并将要eval的JavaScript置于其内,最后把script元素追加到head元素内。

细心的人肯定要问,为什么HttpLibrary.globalEval()要如此设计,而非直接window.eval或者eval.call。这是因为,window.evaleval.call都无法在IE6中实现和script标签加载JavaScript代码一模一样的效果,这两种做法的eval在IE6下仍然不是在全局上下文中执行的。搜索一下你就会发现一些相关的讨论,例如jQuery就曾经使用window.execScript()来完成此项任务。不过最终大家都发现添加script元素才是最好的跨浏览器解决方案,所以现在的jQuery和ensure都是如此实现的了。

加载CSS

相对于加载JavaScript而言,加载CSS就简单多了,而且方法也是类似的:在head元素内直接加入link元素就可以了。这也正是loadCSS()所完成的工作。

实际上,ensure没有确保CSS完成加载后再执行下去。这估计是因为浏览器都能够在CSS加载完成后自动应用到页面上,因此Omar AL Zabir就认为CSS的加载顺序是无关紧要的,不过假如CSS加载速度实在太慢,其实还是会影响显示效果的。

在IE6中加载CSS

这次需要特别照顾的是IE6,而非Safari。IE6在往head元素添加link元素时,必须在window的上下文中完成,因此添加link的函数通过call调用切换了上下文。

总结

实际上动态加载JavaScript与CSS都并不难,在大多数情况下只需要向head元素追加对应的子元素就可以了,只有Safari2和IE6这两款古老的浏览器是需要特殊照顾的。

开始使用 MarsEdit 写 blog

缺少一个客户端,我就总是懒得打开Blogger来写blog。当然,Blogger还算好的,我可以写着写着就按一下save按钮,然后继续写。博客园还不能就地保存,保存一下必须离开编辑页,然后还要重新打开编辑页。我实在无法忍受从PC切换到Mac之后没有客户端的情况……其实是有过一段时间用客户端的,那就是MarsEdit

在MarsEdit的试用期过后,我又懒得写blog了。在忍受不写blog与购买MarsEdit之间,最终我选择了购买MarsEdit,这个价值$29.95还周围都找不到coupon的东西。作为一个shareware,它真的做得不错,但$29.95又实在太贵了,Speed Download那么强大的下载软件也就$25嘛。

在买MarsEdit时,发现可以用PayPal,想起之前注册PayPal时验证信用卡后有$1.95存在PayPal上面了,决定就用PayPal支付,把那$1.95余额花掉。接着在PayPal的页面看到,原来通过PayPal来购买外汇超级是超级贵的。根据今天Yahoo Finance公布的汇率,1CNY能够买入0.1459USD,但是PayPal说1CNY只能买入0.141154USD,实在相差太远了。幸好,PayPal允许你选择不用它来做购汇,用回VISA的购汇功能,只不过支付时无法确定最终购汇汇率。如果VISA的购汇汇率不至于那么离谱,还是VISA比较省钱。不过到底VISA购汇价格如何,我还真的没有留意过,以后要仔细研究一下帐单。

2008年8月21日星期四

MobileMe 真应该免费 beta 一年

服务上线两个星期,就因为服务质量问题表示道歉,每人送30天试用期。然后因为港币用户的验证费用扣错了(应该扣1USD,实际扣了100HK$),又再送30天试用期。现在又因为近期服务不稳定,再给大家60天试用期。加上注册时的60天试用期,这已经是180天了。

对比起这样不断道歉和延长试用期的做法,Apple还不如学习一下Google,让服务免费beta,让更多用户来发现bug和汇报bug,等服务成熟了再开始收费。当然,这样做的坏处是Apple必须保留免费用户,否则全面转为收费的话,很多beta阶段的免费用户都会流失掉。