web workers各浏览器下参数传递的差异

2010-4-5 评论(0) 分类:技术文章 Tags:

做军旗游戏时想用web workers消除计算时界面的停滞,实现了后发现在firefox下正常,但在chrome/safari下却不能用,网上关于web workers的资料太少了,也可能是因为我没找到,结果就一直debug了半天,才找出原因所在,原因就是:chrome/safari下,web worker传递的参数全都会被转换成字符串。

chrome/safari下,只能用字符串通信,无法传递其他数据,无论是数组,object,布尔值,数值,全都会被转成字符串,而在firefox下则支持所有数据类型的传递。

看这段示例代码,以下测试在firefox3.5 safari4.0.5 chrome4.1下进行:

HTML:

<!DOCTYPE HTML PUBLIC "-//W3C/DTD HTML4.0 Transitional//EN">
<html>
<head>
	<title>Test threads fibonacci</title>
</head>
<body>
    <script type="text/javascript">
		var worker = new Worker("testWorker.js");
		worker.onmessage = function (event) {
			alert("type:" + typeof event.data);
			alert(event.data[1]);
		};
		worker.postMessage([2,3]);
    </script>
</body>
</html>

testWorker.js:

onmessage = function (event) {
    postMessage(event.data.name);
};

safari/chrome显示:type:string “,”(字符串”2,3″第二个字符)
firefox显示:type:object 3(数组第二个元素)

可以把worker.postMessage([2,3]);的参数改成其他类型试试,safari/chrome显示出来的type都是string。

写出来好简单,发现这个却花了我挺多时间,firefox对google创造出来的东西的支持比chrome还好,真是奇怪~
(更多…)

YUI2.8自定义事件的一个小bug

2010-3-7 评论(1) 分类:技术文章 Tags:

问题

YUI2.8的自定义事件如果添加了两个相同的侦听函数,在移除这些侦听函数时会有错误:

var testEvent = new YAHOO.util.CustomEvent("testEvent");
testEvent.subscribe(eventHandler);
testEvent.subscribe(eventHandler);
testEvent.unsubscribe(eventHandler);
function eventHandler(){
    alert("testEvent fire");
}
testEvent.fire();
//只会显示一个"testEvent fire"

上面看似没问题,注册了两个相同的侦听函数,执行一次unsubscribe移除了一个,于是只剩一个侦听函数,但事实不是这样:

var testEvent = new YAHOO.util.CustomEvent(&quot;testEvent&quot;);
testEvent.subscribe(eventHandler);
testEvent.subscribe(eventHandler);
testEvent.subscribe(eventHandler);
testEvent.subscribe(eventHandler);
testEvent.unsubscribe(eventHandler);
function eventHandler(){
    alert("testEvent fire");
}
testEvent.fire();
//只会显示两个"testEvent fire"

注册四个相同的侦听函数,执行了一次unsubscribe,却只剩下两个侦听函数有效。

(更多…)

jQuery实例内存空间占用

2010-2-8 评论(2) 分类:技术文章 Tags:

刚接触jquery时很好奇,一个jQuery对象,那么多方法可以用,要是每个对象都保存着这些方法,那得占用多大的内存空间啊。很显然,那些方法都是共用的,只保存一份。今天闲着,研究了下。

要知道哪些方法是每个实例各有一份,哪些方法是共享的,还是先来研究下js的基础:js内部是怎样调用一个实例的属性/方法的。看这段代码:

    var Test = function () {
        this.a = "a";
        this.b = "b";
        this.c = function(){
            return "c";
        }
    }
    Test.prototype = {
        b: "b_prototype",
        d: function(){ return "d"; },
        e: "e"
    }

    var t1 = new Test();
    var t2 = new Test();

var t1 = new Test(),这一句,在 Firefox 下,不考虑参数传递,可以用下面的代码来表示 new 的过程:

var o = {__proto__: Test.prototype};
Test.apply(o);
t1 = o;
//摘自http://lifesinger.org/blog/2009/06/yui3-oop/

经过Test.apply(o);这一句,o = {__proto__: Test.prototype, a: “a”, b: “b”, c: function(){ return “c” } }

调用t1(即o)实例的方法/属性时,会先在o查找是否存在,如果存在,即返回,不存在,则沿着原型链__proto__往上查找,实例对象t1的__proto__指向原型Test.prototype,而Test.prototype.__proto__指向父类的原型,一层层往上搜索,这里Test继承原始天尊Object,所以Test.prototype.__proto__ === Object.prototype

alert(t1.a) //a 在对象中找到a = “a”
alert(t1.b) //b 在对象中找到b = “b”,不再继续查找,忽略对象原型里的b = “b_prototype”
alert(t1.e) //e 在对象中找不到d,沿原型链找到Test.prototype有属性e = “e”

从new语句的分解可以看到,在构造函数里用this.xx = xx生成的方法/属性是每个实例对象都会复制一份的,而prototype里的方法/属性则所有对象共享一份。

alert(t1.c === t2.c) //false t1 t2两个对象的方法c是不同的拷贝,分别保存在t1 t2里
alert(t1.d === t2.d) //true 两个方法d都指向Test.prototype.d

需要注意的是,如果你给t1.d赋了新的值或函数,并不等于修改了Test.prototype.d,这点曾经迷惑了我:

t1.d = “new d”;
alert(t1d === t2.d) //false t2仍然指向Test.prototype.d,而t1在自己对象里新增了属性d,调用t1.d时不会再沿原型链向上查找

回过头看看jQuery,很明显,所有jQuery实例用到的方法都保存在prototype里,只保存一份,jQuery实例对象本身只包含了length selector context三个属性,以及搜索到的DOM。例:

    <div id="tid1" class="tclass"></div>
    <div id="tid2" class="tclass"></div>
    //在jquery源码里jQuery.fn ==  jQuery.fn.init.prototype == jQuery.prototype
    $(".tclass") == {__proto__:jQuery.fn.init.prototype,
                      selector: ".tclass",
                      context: document,
                      length: 2,
                      0:/*指向DOM元素<div id="tid1" class="tclass"></div>*/,
                      1:/*指向DOM元素<div id="tid2" class="tclass"></div>*/
                     }

KISSY Editor 源码学习笔记

2009-12-9 评论(0) 分类:技术文章 Tags:

KISSY Editor是淘宝的开源编辑器,基于YUI2.x开发,但感觉YUI在里面起到的就一个工具的作用,我没学过YUI2.x,对阅读源代码似乎也没多大影响。不过貌似YUI2.x本来就是一个工具库的角色。

觉得边看代码边写下来效果会好得多,以后尽量这样做~对这个源码看得并不仔细,只是试图把主要部分框出来,第一次这样做,写出来挺乱,确实是一个“笔记”来着,零零散散。以下记录和分析可能有挺多错误,望发现者指正。

基础

KISSY Editor由多个模块组成,除核心模块外,其他各个模块相互独立(配置模块除外,如smilies~config~wangwang)。各个插件也相互独立,方便日后扩展。
模块上用到的HTML模板都在开头定义,用全大写字母命名,模板上的参数用大括号括起,使用时用replace替换参数。所有的编辑器外观样式都通过className在css里定义。

创建一个编辑器可用KISSY.Editor(textarea, config)也可用new KISSY.Editor(textarea, config),第一种当成函数调用,会返回new KISSY.Editor(textarea, config)

基础函数:

  • 添加模块:add: function(name, fn, details),把所有模块都保存在属性mods里。
  • 添加插件:addPlugin: function(name, p), 把所有插件都保存在属性plugins里,不允许同名插件覆盖。
  • 加载所有模块:_loadModules 执行所有模块的fn,并把执行过的模块加入属性_attached里

核心模块

core~config: 配置模块
lang~*: 语言模块,保存在E.lang[*],自由扩展多种语言
core~plugin: 定义插件种类
core~dom: 封装一些与编辑器dom相关的方法到E.Dom
core~color: 封装颜色值转换相关方法和正则表达式E.Color
core~command: 不明
core~range: 封装Range方法(获取选中对象、选中文本等)到E.Range

core~instance: 实例化编辑器

  • render编辑框,在此模块开头定义编辑框模板,在传入的textArea前面插入编辑框,隐藏textArea
  • 创建工具栏和菜单栏,并调用各自的init()初始化: this.toolbar = new E.Toolbar(this); this.statusbar = new E.Statusbar(this);
  • 初始化所有插件(工具栏和状态栏的插件在各自的对象里初始化),给每个插件的属性editor传入当前编辑器实例 p.editor = this;
  • 连接到编辑框的三个属性:container contentWin contentDoc

core~toolbar: 工具栏模块

  • init(): 按照config.toolbar的参数用_addItem加载插件
  • _addItem(): 初始化插件,把插件加入工具栏
  • _bindItemUI: 给插件绑定事件
  • 三个updateState相关方法让鼠标点击编辑框时插件根据情况改变状态

core~statusbar: 状态栏模块 与工具栏类似

core~menu: 下拉菜单模块

工具栏有些插件点击后有下拉菜单供选择,例如字体、颜色等,在此模块定义通用方法,需要用到的插件可以调用。

插件模块

smilies~config~default、smilies~config~wangwang: 表情插件的配置文件 每一个为一套表情图片。

plugin~*: 插件模块

  • 每个模块使用E.addPlugin添加相应的插件
  • 每个插件里:
    • 如果定义了init(),则在初始化插件时会调用。
    • 每个插件都有属性type,指定它是哪个类型的插件
    • 放在toolbar上的插件,点击时执行插件的exec() 方法
    • 插件与外部的连接:editor: 编辑器实例,即Editor.Instance

暂时到这里~

JavaScript小型选择器mini源码解析

2009-10-22 评论(6) 分类:技术文章 Tags:

在网上发现一个JavaScript小型选择器—mini,其介绍在这里已经说得挺清楚了,就不再罗嗦了。简单来说,mini选择器只支持以下选择语句:

* `tag`
* `tag > .className`
* `tag > tag`
* `#id > tag.className`
* `.className tag`
* `tag, tag, #id`
* `tag#id.className`
* `.className`
* `span > * > b`

经过调查,以上选择语句已经满足了95%以上的需求。

mini选择器实例代码如下:

var pAnchors = mini('p > a'); // Returns an array.
for (var i = 0, l = pAnchors.length; i < l; ++i) {
    // Do stuff...
}

下载源码查看,发现源码并不难,至少比jquery简单得多,就想试着分析一下它的源码,练练手,之前我是想分析jquery源码的,但发现实在太难了,超出能力范围了,还是先从简单的代码开始吧。

mini选择器大体上,就是先把选择语句最右边的元素先选出来,再根据左边的父元素层层过滤得到符合整个语句的元素。

例如”#a table .red”这个语句的选择过程,就是先选出页面上所有class=”red”的dom元素,再在选出来的元素中判断其父元素是否为table,是则保存,不是则丢弃。这层筛选完后,把结果再进行一次筛选,判断其父元素是否id=”a”,是则保留,不是则丢弃,最后就筛选出了符合”#a table .red”的所有dom元素。

其余细节的解析,我用注释的方式加在代码上了。我发现要把分析代码的过程写出来真是很难,代码是看得懂,但就是表达不出来代码的意思。我现在写出来的那些注释,似乎有点乱,估计别人也挺难看懂,不过当练兵吧,我在写之前并没有完全了解mini的原理,写完后就清晰了,看跟写还是有很大区别的,写出来对自己挺有帮助。

有些地方其实我也不是知道得很清晰,可能会有错误存在。代码里我还有一些细节不理解,有疑问的地方我打上了**号,希望高手看到能告知吧~

这里可以看到,单独选择一个id占了所有选择语句的一半以上,个人感觉mini没有对id进行单独优化,算是不足吧,并且就算只选择一个id,mini(“#id”)返回的也是一个数组,很不方便,实用性不强。

(更多…)

JPuzzle – 拼图游戏

2009-6-2 评论(1) 分类:作品 Tags:

puzzle

http://cnbang.net/lab/Jpuzzle/

最近继续学javascript,某一时刻想试着用jquery和jquery UI里的drop and drop实现之前用FLASH做的拼图游戏,于是动手做了,主要的功能很快就做好了,但做着做着,就想把它做完整了,在细节、代码结构、注释、界面、效率这些方面花了很多时间。

代码结构自我感觉算是不错了,但计时器似乎不应该那样写,在卡的时候计时也会跟着卡,不能正确地计时。

注释第一次用英文写,肯定有语法错误,可能还有歧义,算是尝试吧。

运行效率上,我一直没法找到提高运行效率的方法,现在一直按Start开始新拼图的时候,在firefox和Chrome下没问题,在IE下会一直增加内存,暂时还不知道怎么解决这个问题。

排行榜也花了不少时间,只保留10个最高分数,虽然可以正常运行,但要作弊是十分简单的,在本地做个有score和name的表单提交过去就能随意写入任何分数了,这个,应该是要在php加session或cookie解决吧,先不改了。

做个成品出来真是很耗时,尤其是做界面。这次我没有虎头蛇尾,由始至终都很认真做每一部分,我好像有个坏习惯,自己做个什么东西,在没做完的时候,会老是惦记着它,近乎疯狂地想赶快完成,相当疯狂~

最后还是说下玩法,进入页面后直接点击start就可以开始了,可以复制网上任意一张图片的地址在photo url框,也可以在随后的选择栏里选择预置的几张图片,piece Num为切割的块数。分数的计算是,块数越多分越高,时间越多分越少。

源码

使用javascript验证、发送饭否信息

2009-5-2 评论(1) 分类:技术文章 Tags:

想做点东西练练javascript,于是打算为饭否AIR客户端爱饭加上额外的功能,例如上传图片等,先尝试了使用javascript验证和发送饭否消息,仅这个就花了不少时间,很多时间花在了去除低级错误上,例如jquery ajax的type:”post”被我写成了method:”post”,花了很多时间才找出来。

先放上主要代码,使用jquery框架以及jquer.base64插件:

var name = "yourname";
var pass = "yourpassword";
$.ajax({
    url: "http://api.fanfou.com/statuses/update.xml",
    type:"POST",
    data: "status=信息内容",
    beforeSend: function(xhr){
    xhr.setRequestHeader('Authorization', 'Basic ' + $.base64.encode(name + ':' + pass));
  },
  success: function(data, textStatus){
    alert(data);
  }
});

有两个主要问题:

1.验证用户

饭否API验证方式为 HTTP Basic 验证,需要对“用户名:密码”进行base64编码,添加进ajax请求的http头信息。
XMLHttpRequest.setRequestHeader(‘Authorization’, ‘Basic ‘ + $.base64.encode(name + ‘:’ + pass));
使用饭否需要验证的API(例如读取私信、发送消息)时,都要在请求前加上这一条HTTP头信息。

爱饭使用YUI框架,可以通过YAHOO.util.Connect.initHeader设置每次ajax请求的默认HTTP头信息,
initHeader(‘Authorization’, ‘Basic ‘ + Base64.encode(name + ‘:’ + pass), true);
最后一个参数”true”代表设为默认http头信息。
设置之后每次使用YAHOO.util.Connect.asyncRequest进行ajax请求都不需要再添加验证。

2.跨域问题

使用ajax请求资源总有跨域问题,即请求只能发生在相同的域内。
以上那段代码请求的域为api.fanfou.com,如果放在网上,除非放在饭否服务器,有相同的域,否则无法运行。
对本地网页,在跨域上,不同浏览器有不同规则,IE允许跨域访问,firefox与chorme都不行。以上代码放在本地,用IE打开,就可以运行,用firefox打开,会出现Access to restricted URI denied” code: “1012。

使用IE可以通过设置可信任站点实现跨域获取内容,其他跨域方法网上很多,还未细究,但都是要在客户端设定或者两站互相配合。

所以,使用javascript发送饭否消息只适合运用在AIR程序上,AIR有自己特定的安全沙箱,允许跨域请求。

另,饭否与Twitter的API几乎一样,可直接套用。

P.S 早上起来,发现已经有人对爱饭扩展,做了查看上传的图片的功能,他还在做上传图片的功能,我也没必要再做这部分功能了。开源的好处之一,就是有很多人帮忙完善这个程序啊~

[翻译]改善你的jquery—25条jquery实用技巧<二>

2009-4-12 评论(3) 分类:技术文章 Tags:

上一篇: 改善你的jquery—25条jquery实用技巧<一>

11.适当地使用动画效果

我第一次使用jQuery的时候就喜欢上了像slideDown()、fadeIn()等这些预定义的动画函数,使用它们可以很简单地做出很酷的动画效果。jQuery的animate()函数的强大和易用让人很容易进一步实现更好的效果,事实上,如果你看了jquery的源代码你会发现fadeIn()、slideDown()等这些函数的内部都使用了animate()函数,它们只是让animated()更易使用。

slideDown: function(speed,callback){
return this.animate({height: &quot;show&quot;}, speed, callback);
},

fadeIn: function(speed, callback){
return this.animate({opacity: &quot;show&quot;}, speed, callback);
}

animate()函数只是简单地接收所有的CSS属性,平滑地把属性从一个值转换到另一个值,形成动画。你可以使用它平滑地改变任何你想改变的属性,如width, height, opacity, background-color, top, left, margin, color, font-size等等。

下面这段简单的代码实现了当鼠标放在菜单上时菜单上所有列表元素的高度都平滑地改变到100px。

$('#myList li').mouseover(function() {
$(this).animate({&quot;height&quot;: 100}, &quot;slow&quot;);
});

animation函数不像jquery的其他函数,它是自动队列的,即如果你想在一个效果执行完后接着执行另一个效果,只需要连续使用两次animate函数,无需任何回调。

$('#myBox').mouseover(function() {
$(this).animate({ &quot;width&quot;: 200 }, &quot;slow&quot;);
$(this).animate({&quot;height&quot;: 200}, &quot;slow&quot;);
});

如果你想让两个动画同时执行,把参数放在一个object里,调用一次即可:

$('#myBox').mouseover(function() {
$(this).animate({ &quot;width&quot;: 200, &quot;height&quot;: 200 }, &quot;slow&quot;);
});

你可以传入值是数字的属性形成动画,如果要传入非数字的属性,那就要使用插件,例如平滑过渡颜色效果的插件:Color Animations

12.学习事件委托

使用jQuery可以很容易地为DOM元素绑定事件,但如果绑定太多事件会让程序效率降低,事件委托可以让你可以让你绑定更少的事件但达到同样的效果,解释事件委托最好的办法是通过例子:

$('#myTable TD').click(function(){
$(this).css('background', 'red');
});

这是一个简单的函数,为表格的所有单元格绑定事件,当鼠标点击单元格即变红色,假设表格有50行10列,那么总共绑定了500个事件。如果我们能值为table绑定一个事件,让事件处理函数找出所点击的单元格,再让它变成红色,那是再好不过的了。事实上,这就是事件委托,也是我们接下来要做的:

$('#myTable').click(function(e) {
var clicked = $(e.target);
clicked.css('background', 'red');
});

参数”e”包含了所有关于event信息,包括了实际接收鼠标点击的元素,我们所需要做的就是检查鼠标点击的是哪个单元格,非常简洁。

事件委派还有一个好处。当你为一个块(表格、列表或div块等)里所有的元素绑定事件,当增加元素到这个块里,新增加的元素并不会自动绑定事件,如果使用事件委托,事件绑定后即使新增加元素也可以正常工作。

13.用class保存状态

这是html块保存状态最基本的方法,jQuery擅长通过class操纵元素,因此如果你想储存状态信息,为什么不创建一个额外的的class去保存它呢?

这是一个例子,我们想创建一个可展开的菜单,点击按钮时如果面板是关闭的,则调用slideDown(),如果是打开着则调用slideUp(),先看看HTML:

&lt;div class=&quot;menuItem expanded&quot;&gt;
   &lt;div class=&quot;button&quot;&gt;
        click me
    &lt;/div&gt;
    &lt;div class=&quot;panel&quot;&gt;
        &lt;ul&gt;
            &lt;li&gt;Menu item 1&lt;/li&gt;
            &lt;li&gt;Menu item 2&lt;/li&gt;
            &lt;li&gt;Menu item 3&lt;/li&gt;
        &lt;/ul&gt;
    &lt;/div&gt;
&lt;/div&gt;

很简单,我们添加一个额外的class属性,目的只是为了说明元素现在的状态。添加后,我们要做的只是添加鼠标点击时执行slideUp()和slideDown()的事件处理函数。

$('.button').click(function() {

var menuItem = $(this).parent();
var panel = menuItem.find('.panel');

if (menuItem.hasClass(&quot;expanded&quot;)) {
menuItem.removeClass('expanded').addClass('collapsed');
panel.slideUp();
}
else if (menuItem.hasClass(&quot;collapsed&quot;)) {
menuItem.removeClass('collapsed').addClass('expanded');
panel.slideDown();
}
});

这是个很简单的例子,只作演示用,通过添加额外的class,你可以储存各种各样的值在HTML元素里。

然而,大多数情况下,使用下一条技巧里的方法更好。

14.使用jQuery的内部函数data()储存状态

jQuery有个内部函数data()可以用来在任何DOM元素上储存键/值形式的数据,因为一些原因,这个函数并没有很好的文档说明。储存数据就像下面写的这样简单:

$('#myDiv').data('currentState', 'off');

我们可以改良上一个例子的程序,我们仍然使用同样的HTML(但去掉class”expanded”),使用data()储存状态:

$('.button').click(function() {

var menuItem = $(this).parent();
var panel = menuItem.find('.panel');

if (menuItem.data('collapsed')) {
menuItem.data('collapsed', false);
panel.slideDown();
}
else {
menuItem.data('collapsed', true);
panel.slideUp();
}
});

我想你会同意,这样写比之前更简洁。要了解更多关于data()与removeData(),看这里:jQuery internals

15.创建自定义选择器

jQuery有很多内置的选择器可以通过id、class、标签、属性等查找元素,但如果你想通过其他jQuery里没有的方式查找元素,该怎么做?

或许可以在选择之前为元素添加一个class属性再进行选择,但其实扩展jQuery写新的选择器并不难。下面我们通过例子来说明:

$.extend($.expr[':'], {
over100pixels: function(a) {
return $(a).height() &amp;gt; 100;
}
});

$('.box:over100pixels').click(function() {
alert('The element you clicked is over 100 pixels high');
});

第一部分代码创建了一个新的选择器,用来查找高度大于100像素的元素。第二部分代码使用这个新选择器为所有符合条件的元素添加click事件。

关于自定义选择器在这里就不多说了,你可以上google搜索“custom jquery selector”,肯定能找到很多很好的例子。

16.合理化你的HTML,并在页面载入的时候修改它

这个标题看起来似乎没什么意义,但实际上这个技巧不仅可以让你的代码变得整洁,还可以减少页面的大小,对SEO也有帮助。看看下面的HTML:

&lt;div class=&quot;fieldOuter&quot;&gt;
    &lt;div class=&quot;inner&quot;&gt;
        &lt;div class=&quot;field&quot;&gt;This is field number 1&lt;/div&gt;
    &lt;/div&gt;
    &lt;div class=&quot;errorBar&quot;&gt;
        &lt;div class=&quot;icon&quot;&gt;&lt;img src=&quot;icon.png&quot; alt=&quot;icon&quot; /&gt;&lt;/div&gt;
        &lt;div class=&quot;message&quot;&gt;&lt;span&gt;This is an error message&lt;/span&gt;&lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;fieldOuter&quot;&gt;
    &lt;div class=&quot;inner&quot;&gt;
        &lt;div class=&quot;field&quot;&gt;This is field number 2&lt;/div&gt;
    &lt;/div&gt;
    &lt;div class=&quot;errorBar&quot;&gt;
        &lt;div class=&quot;icon&quot;&gt;&lt;img src=&quot;icon.png&quot; alt=&quot;icon&quot; /&gt;&lt;/div&gt;
        &lt;div class=&quot;message&quot;&gt;&lt;span&gt;This is an error message&lt;/span&gt;&lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;

上面是一个表可能的结构,为了作为例子稍微做了下修改,我想你一定会觉得上面这段代码十分难看,如果表里有很长的内容,页面将会很长,重复元素也很多。如果只是放下面这段代码到你的页面,会更好:

&lt;div class=&quot;field&quot;&gt;This is field 1&lt;/div&gt;
&lt;div class=&quot;field&quot;&gt;This is field 2&lt;/div&gt;
&lt;div class=&quot;field&quot;&gt;This is field 3&lt;/div&gt;
&lt;div class=&quot;field&quot;&gt;This is field 4&lt;/div&gt;
&lt;div class=&quot;field&quot;&gt;This is field 5&lt;/div&gt;

我们需要做的只是使用jQuery做一点点处理,把之前那些需要重复的的HTML代码加进来,像这样:

$(document).ready(function() {
    $('.field').before('&lt;div class=&quot;fieldOuter&quot;&gt;&lt;div class=&quot;inner&quot;&gt;');
    $('.field').after('&lt;/div&gt;&lt;div class=&quot;errorBar&quot;&gt;&lt;div class=&quot;icon&quot;&gt;
        &lt;img src=&quot;icon.png&quot; alt=&quot;icon&quot; /&gt;&lt;/div&gt;&lt;div class=&quot;message&quot;&gt;
        &lt;span&gt;This is an error message&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;');
});

不建议经常使用这个技巧,使用了它,在页面载入时会有闪烁的现象,但在有很多重复的HTML元素的情况下,这个技巧能有效地减小你页面的大小,对SEO也有好处,因为除去了很多重复无关的元素。

译者注:如果要考虑向后兼容,即考虑没有javascript的浏览器,任何情况下都不应该这样使用。

17.通过延迟加载内容提高速度和SEO友好度

还有另一个方法,可以加快页面载入,使爬虫蜘蛛抓取的页面更整洁,就是在页面其他部分载入完成后,再用AJAX载入内容。用户能看到正常的一面,爬虫蜘蛛也能只抓取到你想让它抓取的内容。

我们已经在自己的网站上使用这个技巧了,上面那些紫色的按钮能打开3个表单,一个导航和一个google map,这些都会让页面大小加倍,所以,我们把上面那些全部放在一个静态的HTML文件里,当页面读取完毕时再使用load()读取这个文件获得这部分元素,像这样:

$('#forms').load('content/headerForms.html', function() {
// 内容读取完毕时执行这里的代码,只执行一次
// 把所有事件处理程序都放这里
});

我不会到处使用这个技巧,你应该考虑它的优缺点,它会增加对服务器的请求,你的页面刚打开时载入的部分不能马上使用,但如果能正确地使用,它会是一个很好的优化技巧。

18.使用jQuery里的工具函数

jQuery里有很多实用的函数,填补了javascript的空白。

函数API见这里

特别地,浏览器对javascript里一些数组函数的支持并不好(IE7甚至不支持indexOf()),jQuery有迭代、筛选、复制、合并和去除重复元素这一系列针对数组的函数。

其他在javascript很难实现的功能,像获取下拉列表的值,在传统javascript里,必须使用getElementById获取<select>元素,再获取它所有的子元素,迭代检查它们有没有selected,十分繁琐。而使用jQuery很容易就能获取:

$('#selectList').val();

很值得花一点时间仔细看看jQuery文档,探索那些相对少人知道的函数。

19.同时使用多个框架时,用noconflict重命名jQuery对象

大部分javascript框架使用符号$作为记号,当页面上使用了超过一个框架时,这会产生冲突。幸运的是有一个很简单的解决方法,nofonflict()函数让你能自定义jQuery的名字:

var $j = jQuery.noConflict();
$j('#myDiv').hide();

20.怎样提示图片已经加载完毕

这个问题似乎没有怎么被提及到,但这在做相册的时候是很常见的问题,其实这个问题很简单。

你需要做的只是在<img>标签上使用.load()函数,给它一个回调函数。下面的例子改变了<img>标签的”src”属性,载入一张新的图片,并为它加了一个简单的load()函数。

$('#myImage').attr('src', 'image.jpg').load(function() {
alert('Image Loaded');
});

你会发现图片载入完毕时提示框会弹出。

21.总是使用最新版本

jQuery一直在改进,它的创建者John Resig一直在寻找方法改进jQuery性能。

jQuery现在的版本是1.2.6(翻译时已经是1.3.2),但john透露它在开发一个新选择器引擎:Sizzle 。它能提升选择器的效率,在firefox里能达到4倍。所以,应该使用最新版本以获得更好的性能。

译者注:sizzle引擎已经开发完成,jquery1.3已经使用此引擎,sizzle主页:http://sizzlejs.com/

22.怎样检查元素存在

在你操纵一个元素前你不需要检查那个元素是否存在,因为如果你选择的元素不在DOM里jQuery不会做任何事。但如果你需要检查是否有元素被选择了,或者检查有多少个元素被选择,你可以使用length属性。

if ($('#myDiv).length) {
// 你的代码
}

很简单,但并不易见。

23.用js向HTML标签添加一个class属性

我是从Karl Swedberg的两本书 (1) (2) 里学到这招。

他在我最近的一片文章的评论里提到这个技巧,它的原理是这样的:

首先,jQuery加载完成后用它为<HTML>标签增加一个名为”JS”的class属性:

$('HTML').addClass('JS');

因为这只会在javascript可用的情况下发生,所以你可以利用它添加只有javascript可用时才有的css属性,像这样:

.JS #myDiv{display:none;}

这意味着,当javascript可用时我们可以隐藏一些内容,再在需要时用jQuery让它显现,而javascript不可用时(以及搜索引擎抓取页面时),内容并没有隐藏,用户能正常看到那些内容。我会在以后经常使用这个技巧。

可以来这里读取Karl Swedberg关于这个例子的全文:

24.返回”false”阻止默认行为

这可能是显而易见的,但有时候却不是,如果你习惯这样做:

&lt;a href=&quot;#&quot; class=&quot;popup&quot;&gt;Click me!&lt;/a&gt;

然后给它绑定一个事件,像这样:

$('popup').click(function(){
// Launch popup code
});

它会很好的工作,直到你在一个长页面使用它,才会发现问题,你会发现”#”使鼠标点击它时自动返回页面顶部。

你需要做的是阻止它的默认行为,在事件处理函数里添加”return false”可以阻止任何事件的任何默认行为:

$('popup').click(function(){
// Launch popup code
return false;
});

25.缩写ready事件

这是个很小的技巧,可以通过缩写函数$(document).ready为你腾出几个字节的空间。

可以把

$(document).ready(function (){
// your code
});

缩写成这样:

$(function (){
// your code
});

后话

终于翻译完了,好长的文章,真佩服他这么有耐性写这么长的文章。似乎我耐心还是不够,一口气是翻译不了那么长的,分了好几个次。第二部分代码还没排版,没有缩进,以后再修改。

[翻译]改善你的jquery—25条jquery实用技巧<一>

2009-4-8 评论(2) 分类:技术文章 Tags:

原文链接:Improve your jQuery – 25 excellent tips

jquery是令人惊叹的框架,我已经使用它一年多了,我发觉使用它的时间越长,就会越喜欢它,也越理解它内部的工作原理。

我不是一个jquery专家,我也不奢求成为专家,所以如果这篇文章里出现一些错误我非常乐意接受大家的批评和改正。

我常常称自己为jquery中级使用者,我一年以来学到的、总结出来的技巧和技术,应该可以让一些人从中获益。

1.从google code载入jquery

Google挺久以前就开始提供各种javascript库供使用者载入,从google载入javascript框架相对于直接从自己的服务器载入有许多优势,首先它节省了自己服务器的带宽,从google载入也十分快,最重要的是如果用户之前已经访问过其他从google载入javascript库的网站,再访问你的网站时直接从缓存里取出即可,无需再次请求,会大大加快载入速度。

这很有意义,有多少网站储存着同一个版本的jquery呢,它们都没有被缓存。从google载入jquery是很容易做到的:

&lt;script src=”http://www.google.com/jsapi”&gt;&lt;/script&gt;
&lt;script type=”text/javascript”&gt;

    // 载入jQuery
    google.load(”jquery”, “1.2.6″);

    google.setOnLoadCallback(function() {
    //代码放这里
    });

&lt;/script&gt;

或者,你也可以直接包含jquery对应的网址,像这样:

&lt;script src=”http://ajax.googleapis.com/ajax/libs/jquery/1.2.6/jquery.min.js” type=”text/javascript”&gt;&lt;/script&gt;

完整的说明在这里

2.使用参考表(cheat sheet)

这不仅仅是个关于jquery的建议,对于很多语言,都有相应的很好的参考表,它很方便地把所有函数都列在可打印的A4格式的文档上,对于jquery1.2,下面这两个网址给出了两个很好的参考表:

http://www.gscottolson.com/weblog/2008/01/11/jquery-cheat-sheet/
http://colorcharge.com/jquery/

3.组合你所有的代码并压缩它们

嗯,这本来是一个普遍的javascript的建议,但任何使用jquery的大项目通常都会使用很多插件,所以这条建议也适用于jquery。

现在的浏览器不能同时载入几个脚本,这意味着如果你一次载入很多个脚本,会减慢载入网页的速度,所以,如果你的脚本需要载入到所有页面上,就可以考虑把你所有的代码放在一起,然后压缩它们。

有一些插件脚本已经被压缩过,你要考虑的是那些没有被压缩过的脚本。压缩脚本需要花费的时间很少,个人来说,推荐Dean Edwards制作的Packer这个工具。

4.使用firebug的控制台记录工具

如果你还没有安装firebug,那应该立即去下载。它除了提供其他一些常用的功能例如检查http传输和查找CSS错误外,还有优秀的控制台记录命令让你能轻易地检测脚本里的错误。

这里有这个功能的详细介绍

我个人喜欢的功能是“console.info”,它能输出信息或变量,代替了弹出窗口的alert,还有“console.time”让你轻易地在一段代码间设置一个计时器,得出执行这段代码所用的时间。这些console命令都很容易使用:

console.time('create list');

for (i = 0; i &amp;lt; 1000; i++) {
var myList = $('.myList');
myList.append('This is list item ' + i);
}

console.timeEnd('create list');

这个例子我故意写了一些效率很低的代码,在接下来几条技巧/建议里,我会说明如何使用这个计时器展示代码的改进。

5.通过暂存减少选择器的使用

jquery选择器让人赞叹,它以难以置信的简单方式选择页面上任何元素,但在其内部,选择器需要做大量的工作,所以如果大量使用选择器,你会发现程序开始变得很慢。

如果你多次地选择同一个元素(例如在一个循环里),你可以只选择它一次并赋给一个变量,再在你的核心代码里使用它。下面这个例子我们通过循环添加元素到一个无序列表里。

for (i = 0; i &amp;lt; 1000; i++) {
var myList = $('.myList');更能描述问题
myList.append('This is list item ' + i);
//译者注:我觉得直接用$('.myList').append('This is list item ' + i),去掉myList更能说明问题
}

这在我的电脑里用firefox3花费了1066毫秒(想象下如果是IE6将会是多长时间!),这在javascript来说是很慢的。现在我们看看下面的代码,只使用选择器一次:

var myList = $('.myList');

for (i = 0; i &amp;lt; 1000; i++) {
myList.append('This is list item ' + i);
}

只花费了224毫秒,快了4倍多,只是移动了一行代码。

6.减少DOM处理

通过减少插入DOM的次数,我们可以让刚才那个例子运行得更快。DOM插入操作像.append().prepend().after()和.wrap()都相对地更耗费资源,如果运行很多DOM插入处理会让网页变得很慢。

我们需要做的是使用字符串连接建立列表,然后使用函数把列表一次性添加到无需列表上,使用.html()会更快,看看下面的例子:

var myList = $('#myList');

for (i=0; i&amp;lt;1000; i++){
myList.append('This is list item ' + i);
}

在我的电脑里运行需要216毫秒,差不多1/5秒,但如果我们先建立一个字符串,再使用.html()插入,像这样:

var myList = $('.myList');
var myListItems = '';

for (i = 0; i &amp;lt; 1000; i++) {
myListItems += '&amp;lt;li&amp;gt;This is list item ' + i + '&amp;lt;/li&amp;gt;';
}

myList.html(myListItems);

用了185毫秒,虽然没有快多少,但还是节省了31毫秒。

7.插入DOM时包含所有内容在单个元素里

嗯,别问我为什么这样做(我一定会有更有经验的程序员可以解释)

上一个例子中我们通过.html()函数插入1000个元素到一个无序列表里。如果我们在插入前将它包含在一个UL标签里,然后将整个UL插入到另外一个标签(一个DIV)里,这样就只插入了一个标签,而不是1000个,这会让程序块很多,像这样:

var myList = $('.myList');
var myListItems = '&amp;lt;ul&amp;gt;';

for (i = 0; i &amp;lt; 1000; i++) {
myListItems += '&amp;lt;li&amp;gt;This is list item ' + i + '&amp;lt;/li&amp;gt;';
}

myListItems += '&amp;lt;/ul&amp;gt;';

myList.html(myListItems);

只花费了19毫秒的时间,巨大的改进,比第一个例子快了50倍。

8.尽可能使用ID而不是class

jquery优秀的选择器使通过Class选择元素变得跟通过ID选择元素一样简单,让人比以前更放心大胆地使用class。但事实上最好还是使用ID而不是class,因为使用ID获取元素时,jquery使用了浏览器默认的方法(getElementByID)获取元素,这无需经过遍历元素即可找到所需要的元素,比通过class查找元素更快。有多快?让我们来看看。

我使用了前面的例子,并对它进行小小的改动,让每一个li元素都有class属性,然后通过循环选择每一个元素。

// 创建list
var myList = $('.myList');
var myListItems = '&amp;lt;ul&amp;gt;';

for (i = 0; i &amp;lt; 1000; i++) {
myListItems += '&amp;lt;li class=&quot;listItem' + i + '&quot;&amp;gt;This is a list item&amp;lt;/li&amp;gt;';
}

myListItems += '&amp;lt;/ul&amp;gt;';
myList.html(myListItems);

// 每一个元素都选择一次

for (i = 0; i &amp;lt; 1000; i++) {
var selectedItem = $('.listItem' + i);
}

跟我想的差不多,我的浏览器卡住了,它使用了5066毫秒(超过5秒)。现在修改一下代码,给每一个元素一个ID代替class,然后做同样的事:

// 创建list
var myList = $('.myList');
var myListItems = '&amp;lt;ul&amp;gt;';

for (i = 0; i &amp;lt; 1000; i++) {
myListItems += '&amp;lt;li id=&quot;listItem' + i + '&quot;&amp;gt;This is a list item&amp;lt;/li&amp;gt;';
}

myListItems += '&amp;lt;/ul&amp;gt;';
myList.html(myListItems);

// 每一个元素都选择一次
for (i = 0; i &amp;lt; 1000; i++) {
var selectedItem = $('#listItem' + i);
}

这段代码只需要61毫秒的时间,差不多快了100倍!

9.给你的选择器定一个范围

默认地,当你像#(“.myDiv”)这样使用一个选择器,页面上所有的DOM元素将会被遍历,去查找符合条件的元素,这将会十分耗资源。

jquery的选择函数提供了第二个参数:

jQuery( expression, context )

通过提供一个上下文参数给选择器,可以让选择的范围缩小在所提供的元素内,不需要遍历整个页面。

为了演示,让我们引用上一个例子的代码,创建1000个元素的无序列表,每个li标签都有class属性,循环选择每一个元素,像上一个例子所看到的,将会花费超过5秒的时间运行这段代码。

var selectedItem = $('#listItem' + i);

然后我为选择器增加了一个上下文参数,让它只在.myList元素里遍历,像这样:

var selectedItem = $('#listItem' + i, $('.myList'));

它仍然使用了3818毫秒的时间,因为选择class还是一个效率低的做法。但这已经比之前快了25%,而且只为选择器添加了一个上下文参数而已。

译者注:我想如果把上面的代码放在一个大页面里执行,效果会很明显。

10.使用jquery链

jquery最酷的特性之一就是它能把函数调用连接在一起,例如,如果你想转换某个元素的class,可以这样写:

$('myDiv').removeClass('off').addClass('on');

如果你像我的话,会在学习jquery的前5分钟就知道这个特性,但jquery链不仅仅是这样。
首先,它可以跨行运行(因为jquery=JavaScript),这意味着你可以像这样简洁地写代码:

$('#mypanel')
.find('TABLE .firstCol')
.removeClass('.firstCol')
.css('background' : 'red')
.append('&amp;lt;span&amp;gt;This cell is now red&amp;lt;/span&amp;gt;');

养成使用jquery链的习惯可以减少选择器的使用。

假设你想在某一个元素上执行几个函数,但其中有一个函数通过某种途径改变了元素本身,像这样:

$('#myTable').find('.firstColumn').css('background','red');

上面的代码首先选择了一个table,然后选择table里class为”firstColumn”的单元格填充红色。

假设我们现在想把所有class为”lastColumn”的单元格都填上蓝色。因为我们之前已经用find()函数把所有class不是”firstColumn”的单元格排除了,所以我们要再次使用选择器获取table元素,所以我们就不能使用jquery链了,是不是?其实不是,jquery还有一个end()函数可以让链恢复到未被改变的时的状态,这样就可以继续链接下去,像这样:

$('#myTable')
.find('.firstColumn')
.css('background','red')
.end()
.find('.lastColumn')
.css('background','blue');

创建一个有链接功能的jquery函数比你想象中的还要简单,需要做的只是在函数里修改完元素后返回它:

$.fn.makeRed = function() {
return $(this).css('background', 'red');
}

$('#myTable').find('.firstColumn').makeRed().append('hello');

简单不?

继续阅读:改善你的jquery—25条jquery实用技巧<二>

[AIR]微博客数据保存 twitkeeper

2008-10-2 评论(8) 分类:作品 Tags:

twitkeeper

需要安装最新ADOBE AIR v1.01 :http://airdownload.adobe.com/air/win/download/latest/AdobeAIRInstaller.exe

twitkeeper下载:twitkeeper.zip

在某个时候看到一句话:“话说,腾迅QQ也总有一天会消失,那么一些记录也就只是或长或短的暂时;那么,饭否,也是吧?”

我是 觉得腾讯QQ是只会演变成别的东西而不会消失,饭否就不知道它能提供服务多久了,饭否没有盈利,创建它的人也放任着饭否不管全力搞海内了。不过饭否有那么 多的用户,在互联网用户是基础~~所以该是不会突然就消失的。数据是放在网上安全还是放在本地由自己保管安全?就几年前的话毫无疑问自己保管才踏实,现在 就不一定了,有时放在实力雄厚的网站上更安全。饭否现在来说,实力并不咋样。饭否已经不增加新功能了,搜索功能都没有(不会是我没发现吧?),各种对数据 的统计也没有。

所以,就想写个程序,一可以保存饭否上的数据,二可以对保存的数据进行分析统计。其实所有的微博客构造都差不多,于是想做可扩展的,不单是读取饭否的,也可以读取叽歪、滔滔、twitter等其他微博客内容的。

获取数据是一页页读取HTML再提取有用的数据的,由于安全沙箱的关系,网页中不能获取不同域的网站的页面数据,所以需要做桌面应用程序。做桌面软件, 我就只会做ADOBE AIR程序而已,事实上,我只会用网络语言。上次的账本程序用actionscrīpt3写,这次就用javascrīpt写了,刚好很想练练js,刚好 发现一个很好的软件aptana,写程序、调试都很方便,又刚好看到一本好书Pro Javascrīpt Design Patterns,于是在暑假接近8月的时候就开始写了~~

开头部分十分认真,想结构想半天,想很多以后扩展什么的,定一个类都定半 天,写的代码看起来挺舒服。后来暑假某一段时间开始就不写程序啦,到了开学都没怎么再写,开学后事情又多,没啥时间,又想快点完成这个程序,好专心做其 他,结果,我做东西的一贯作风:虎头蛇尾又出现了。。现在的程序又变成了只求效果,不管结构、扩展什么的东西了~~这个是匆匆完工的,外观做得马虎,统计 的东西应该用图表表现出来的,现在直接输出数字了,挺难看,名字叫twitkeeper,twit是微博客元始天尊twitter的缩写,但里面只能读取 之前写的饭否和叽歪,懒得去写twitter的读取了。。程序没经过多少测试,BUG有多少就不知道了。。

之前设想,先做出基本的,以 后再慢慢扩展以下功能:用户登陆,可发送信息,可同时发送一条信息到多个微博客(饭否、叽歪、twitter等),支持读取封闭帐户(就是只有好友才能查 看的帐户),支持读取保存私信,视图可任意扩展,本地收藏夹(每条消息后面有选项“保存到收藏夹”),等等,有了数据,就可以进行各样的分析统计。上面那 些想起来都是可以实现,不过相当麻烦,估计我是不会再做。。

在做的过程发现,饭否比叽歪好很多,饭否读取速度飞快,叽歪读取速度那个慢啊。。饭否页面结构好,获取数据的时候省事不少,叽歪的页面。。内容不是单独在一个标签里,奇怪。页面又很不简洁。

运行的时候,才发现在页面插入DOM很耗时,显示四五十页的文件都要五六秒。。这个应该可以改善的,改成不一次性读取,有空再改。搜索遍历等的查找就很 快。用XML格式储存数据得注意”<” “>” “&”这三个字符,会破坏XML的结构,当时分析XML结构出错时就在想,早知用json了,没那么严格,XML一不小心结构没了就完蛋。。

ADOBE AIR虽然说支持用JS写,但觉得还是用FLEX做,用actionscrīpt才是正道~~用JS麻烦不少~~

我自己是觉得应用里的搜索功能挺好用,就是读取数据多的文件时很慢,上面说了,在页面插入DOM很耗时,待改进。

10月3日更新:

昨晚放上twitkeeper时就说到,读取数据多的文件时十分慢,今天刚好看一篇文章:Javascrīpt动态生成表格的性能调优,用数组储存元素再一起放进页面,速度提高了近10倍~~~~现在读取的速度很快~~~不晓得为啥用数组储存会比其他方式快那么多,估计得深入了解才能知道~~