2010年2月3日
话说以前我转过一片文章,讲的是 JavaScript中||和&&的妙用,
网址是
http://www.cnblogs.com/mars-bird/archive/2009/10/23/1588406.html
第一段代码就是强调一下这个用法,我在我的项目中使用了一个switch,后来我发现这样的代码好丑,于是我就写成||&&形式的,
后来测试性能的时候,发现性能竟然上了一个数量级,可见这种写法在某些情况下可以增加性能,但是我并不确定是何种情况才能提高性能,因为我测试在通常情况下switch和||&&的性能是差不多的.
原来的代码:
switch(this.now_char=this.str.charAt(this.index)){
case "/":
if(this.handleNote()) continue;else this.str2+=this.now_char;
break;
case "\"":
case "\'":
if(this.handleStr()) continue;else this.str2+=this.now_char;
break;
case "\n":
if(this.handleLine()) continue;else this.str2+=this.now_char;
break;
case "{":
case "}":
if(this.handleDepth()) continue;else this.str2+=this.now_char;
break;
case ":":if(this.handleJson()) continue;else this.str2+=this.now_char;break;
default:
if(this.handleKeyword()) continue;else this.str2+=this.now_char;
break;
}
改写后的代码,功能当然是一样的
(this.now_char=="/"&&(this.handleNote()||(this.str2+=this.now_char)))||
((this.now_char=="\""||this.now_char=="\'")&&(this.handleStr()||(this.str2+=this.now_char)))||
(this.now_char=="\n"&&(this.handleLine()||(this.str2+=this.now_char)))|| ((this.now_char=="{"||this.now_char=="}")&&(this.handleDepth()||(this.str2+=this.now_char)))||
(this.handleKeyword()||(this.str2+=this.now_char))
我嚼的第二种写法更简洁点,||&&还有很多用处,可以看那篇文章的介绍
第二段代码是利用了一个特性:
(ele=document.createElement("div")) ;//这个表达式会返回一个dom元素,赋值的同时会把值返回给外边的括号
于是出来下面这段代码 :
var mixin=function(target,options){
for(var i in options){
target[i]=options[i]
}
}
var ele=null;
mixin(ele=document.createElement("div"),{
id:"aa",
className:"bb",
innerHTML:"sss"
})
document.body.appendChild(ele)
debug(ele.id)//aa
debug(ele.className)//bb
debug(ele.innerHTML)//sss
这段代码是因为我实在厌烦了建立一个dom元素的时候的一大堆语句:
var ele=document.createElement("div")
ele.id="aa";
ele.className="aa"
ele.innerHTML="sss"
等等等等,好烦啊.
于是出来了上面的代码.
用上面的原理还可以这样写代码
(ele=document.createElement("div")).className="aa";
感觉是不是节省了一点空间呢,上面这句话节省了一个变量名,呵呵.
2010年1月29日
话说阿里巴巴今年的校园招聘有一道题目考了一个知识点,那就是setInterval的参数函数里的this指向.
看到这个题,我蒙了,因为那时候我不清除这个问题,想了半天没想出来,后来到网上一查,在国外的某网站查到说setInterval和setTimeout之后的函数的作用域是全局的,也就是里面的this指向的是全局对象.
这个问题可麻烦了,我经常要在循环函数里用this来引用当前对象,也许你想到可以用闭包,不过实际情况并非如此简单,对象实例多了之后,闭包也乱套了.
我的愿望就是让循环函数里的this仍然指向当前上下文的对象,无需传参数,无需闭包(其实这也是闭包,只是形式上看着比较自然而已);
例如:(一部分代码,作用是定时发送请求)
1 var sendRequest=function(){}
2 sendRequest.prototype={
3 .............................
4 .............................
5 beginSend:function(){
6 //使循环函数里的this指向本对象,而不是全局对象
7 this.loop_send=setInterval((function(param){
8 return function(){param.sendARequest();}
9 })(this),this.options.interval);
10 },
11 sendARequest:function(){
12 this.num++;
13 this.checkLimit();
14 var callback = {
15 success: this.handleSuccess,
16 failure: this.handleFail,
17 argument: {
18 handle: this,
19 timeout:500
20 }
21 }
22 var post_data="...."
23 //如果待发送的数据不为空,则将取出一条数据发到后台
24 if(this.data_wait_for_send.length!=0){
25 for(var i=0,j=this.data_wait_for_send.length;i<j;i++){
26 post_data+="&content[]="+this.data_wait_for_send[i];
27 }
28 this.data_wait_for_send=[]
29 }
30 // debug(post_data)
31 var que = Connect.asyncRequest('POST', this.options.getUrl, callback,post_data);
32 },
33 ......................
34 ......................
35 }
36
如此,在sendARequest()函数里,我们可以正常使用this来引用当前对象,使用当前对象的变量和方法,这样岂不是很方便?
2010年1月11日
大家看着觉得好玩就点一下右下角那个"推荐"哈,咱也上个头条 我这个是js构建的,它不是个柱状图,而是一个百分比示意条,它可以定义三种颜色,当百分比小于33%的时候显示红色,表示警告,适中的时候显示黄色,表示适中,大于67%的时候显示绿色表示通畅
而且可以动态改变百分比数值,数值改变时,百分比条是用动画方式表现的,
如下图所示:

动态效果可以看我的一个演示demo:http://www.beiju123.cn/ajax-weibo.html(对不起,因为刚才有人输入了违法字符,造成数据错误,请大家不要输入特殊字符) 插个广告,我的博客:http://www.beiju123.cn/blog/
js代码如下:
1 var PercentBar=function(){
2
3 }
4 PercentBar.prototype={
5 /**
6 *这是一个百分比显示条,可以显示现在的数值状况,而且有三种颜色,不同的值的时候有不同的颜色,平滑变化
7 */
8 value:0,
9 inner_ele:null,
10 outer_ele:null,
11 text_ele:null,
12 init:function(config){
13 this.options={
14 color_arr:["#FF4A4A","#FFFF4A","#3FDB3F"],//三种颜色,分别表示低中高三种状态
15 text_arr:["网络信号差","网络信号一般","网络信号好"],
16 back_color:"#fff",//背景颜色
17 width:150,
18 height:12,
19 border_color:"#ddd",
20 container_id:"percent",
21 id:"percentbar",//生成的结构的id
22 max_value:150
23 }
24 mixin(this.options,config);
25 this.createHtml();
26 },
27 createHtml:function(){
28
29 var outer=document.createElement("div");
30 outer.id=this.options.id;
31 var inner=document.createElement("div");
32 inner.style.height=this.options.height+"px",
33 inner.style.width="0px",
34 inner.style.backgroundColor=this.options.color_arr[0]
35 outer.appendChild(inner);
36 this.inner_ele=inner;
37 Dom.get(this.options.container_id).appendChild(outer);
38 outer.style.height=this.options.height+"px",
39 outer.style.width=this.options.width+"px",
40 outer.style.backgroundColor=this.options.back_color,
41 outer.style.border= "1px "+this.options.border_color+" solid"
42 this.outer_ele=outer;
43 var text=document.createElement("div");
44 text.style.height=(this.options.height+10)+"px",
45 text.style.width=this.options.width+"px",
46 text.style.backgroundColor=this.options.back_color,
47 text.style.border= "1px "+this.options.border_color+" solid"
48 text.style.textAlign="center";
49 text.style.lineHeight=(this.options.height+10)+"px"
50 this.text_ele=text;
51 Dom.get(this.options.container_id).appendChild(text);
52 },
53 update:function(num){
54 var ani=new YAHOO.util.Anim(this.inner_ele, {width:{to:(num/this.options.max_value)*this.options.width}},1);
55 ani.onTween.subscribe(function(s, o,handle) {
56 handle.value=handle.inner_ele.style.width.replace("px","")*1
57 var color=""
58 var text=""
59 var num=handle.value;
60 var num2=handle.options.width/3;
61 if(num<=num2){
62 color=handle.options.color_arr[0]
63 text=handle.options.text_arr[0]
64 }else if(num>num2&&num<=num2*2){
65 color=handle.options.color_arr[1]
66 text=handle.options.text_arr[1]
67 }else{
68 color=handle.options.color_arr[2]
69 text=handle.options.text_arr[2]
70 }
71 handle.text_ele.innerHTML=text;
72 handle.inner_ele.style.backgroundColor=color;
73 },this)
74 ani.animate();
75 }
76 }
当然也有css
1 .percent{
2 height:12px;
3 width:150px;
4 background-color: #fff;
5 border: 1px #ddd solid;
6 }
7 .inner{
8 height:12px;
9 width:100px;
10 background-color: #00CC00;
11 }
原理就是动态改变里面那个层的宽度,原理很简单,代码用了yui,呵呵
2010年1月10日
前奏
MVC模式在js里已经很久之前就被实现了,但是这种自动化的模式是否适合js目前的性能就值得讨论了,mvc在脚本语言中的发扬光大我嚼的是ruby on rails的功劳,所以后来很多mvc框架都跟rails非常相似,包括php中我了解过的几个框架和几个js的mvc框架,都是强调一个自动化,习惯优于配置,但是这样造成了初始化的时候耗费了大量性能资源,运行时也要随时处理分配三层代码,性能浪费严重.
前几天在懒懒交流会上听玉伯讲了一个重构之美,说到JavaScript代码就应该写的像JavaScript,而不是写的跟java一样,或者像ruby一样,其中有一个原因我想就是JavaScript原生的写法省掉了一些为了转换写法做出的中间处理,而且可以充分利用js的一些特性,所以写js没必要写的多炫,没必要想jQuery一样N句代码可以写成一句话,这中间jQuery要做一些解析,这些解析就是性能啊.况且这种写法会造成重构麻烦,和其他的js难以结合,而且时间长了,甚至忘了js到底应该怎么写了,会jQuery的人不一定会js.
问题来了
http://mission.jianghu.taobao.com/umissionList.htm?tracelog=MISSION004

这个是淘江湖中的一个任务页面,里面有个弹出窗口,现在线上的代码不是我写的,我只是拿来做个例子,当然是反面的例子.这个弹出窗口是可以重用的,也就是每次点击不同的任务都会重新在js里构造这个弹出窗口的内容.
我透露下源代码里构造html的js函数:
1 //生成任务的HTML
2 function taskHtml(data_task,staticServer){
3
4 var contentHtml="";
5 contentHtml+='<div class="tip_task"><ins class="tk_close"></ins><div class="tip_task_content"><ul>';
6 contentHtml+='<li class="tk_pic"><img src="'+data_task.missionLogo+'" alt="" width="100" /></li>';
7 contentHtml+='<li class="tk_title">'+data_task.missionName+'</li>';
8 contentHtml+='<li class="tk_detail">'+data_task.missionDetail+'</li>';
9 contentHtml+='<li class="tk_goal">任务目标:'+data_task.missionStep+'</li>';
10 if(data_task.missionInfo){
11 contentHtml+='<li class="tk_info"><samp><span class="icon_alert_0">'+data_task.missionInfo+'</span></samp></li>';
12 }
13 contentHtml+='</ul><samp class="tk_action"> <span class="itg">任务奖励淘金币:<span class="integral_font">'+data_task.missionScore+'</span></span><samp class="act skin-blue"><span class="btn n-btn"><a href="#url">继续完成任务</a></span></samp> <a class="next">呃...我想先看看下一个>></a> </samp></div>';
14 contentHtml+='</div>';
15 return contentHtml;
16 }
这么简单的一个弹出层被写成这样,可能是时间太紧作者来不及思考,直接构造了这个函数,可是我前几天接到任务说要改掉这个弹出框,我把html重新改了,然后进到这个函数,我的天啊,这叫我怎么改啊,乱七八糟一大团,而且如果以后还有一个人想改这里的话,又是一个乱七八糟,说不定还会怨我写的不好.
其实前几天恰好我想了一个好办法来解决这个问题,这个办法很简单,很明了,很直观,很容易重构.
如果是我,我会这样做
我前几天要做一个网页,他上面的内容都是js根据后台数据自动生成的,也就是你看这个页面的源代码的时候,除了js和一个外套你什么都看不到,而这个网页里面的元素很多都是重复的,对于这样的需求我首先想到的是"模板"这个词,对于大量重复的视图,自然而然想到要用模板来解决这种事情了.那好我们就开始动工吧.
需求:我要在一个页面里面构造100个内容不同但是框架相同的栏目,他的html如下:
<div id="tpl_item" style="display: none">
<div class="item-bd">
<div class="bd-inner">
<div class="username">
这里是姓名:</div>
<div class="question">
这里是内容</div>
<div class="time">
这里是时间</div>
<div class="action">
<a class="yes" href="javascript:void(0);">YES!</a>这里是票数
<a class="no" href="javascript:void(0);">NO!</a>这里是否定的票数</div>
</div>
</div>
</div>
哦,看起来好简单,如果你想到了用字符串来串接html和数据的话,欧耶,你写出来的代码就会是上面那样的,一团糟,不容易修改,而且字符串的串接性能堪忧,虽然我即将使用模板来实现这个东西,但是我嚼的模板的解析速度比字符串的串接速度还要快,我只用了两次正则而已.
这么做:我把模板和数据都预先定义好,然后定义一个通用函数,这个函数可以根据数据和模板构建html,而且它是通用的,任何html都可以用他来构建,只需要数据和模板是配套的.
这里的数据要是标准的才行,我把它定义成这样:
/***************************主页项目的js------数据部分**********************************/
/**
*下面的数据结构部分,所有的数据对象都必须遵从此结构,便于后面的自动化处理
*/
/**
* 主页项目的数据
*/
M.ItemData=function(){}
M.ItemData.prototype={
data:{
username:"sffffffffffffffffffffffffffffffffffffffffffffs",
content:"ss",
time:"ss",
yes_num:0,
no_num:0
},
/**
*转换字符串到对象
*/
transO:function(str){
}
}
transO函数是为了和ajax交互的函数,将data构造成html后缀形式,和本文无关,data里保存了这个模板中要用到的所有数据,注意书写顺序不能改变.
有了数据,下一步就是设定一个模板了,可以将模板定义成一个字符串放在js中,也可以作为一个display:none的元素放在html里,之后用innerHTML读出来
我是放在html里的,模板如下:
<div id="tpl_item" style="display: none">
<div class="b-t"></div>
<div class="b-b"></div>
<div class="item-bd">
<div class="bd-inner">
<div class="username">
$!username:
</div>
<div class="question">
$!content
</div>
<div class="time">
$!time
</div>
<div class="action">
<a class="yes" href="javascript:void(0);">YES!</a><span><num>$!yes_num</num>票</span>
<a class="no" href="javascript:void(0);">NO!</a><span><num>$!no_num</num>票</span>
</div>
</div>
</div>
</div>
其中的"$!data"就是模板参数了,之后通过一个函数我们将把数据和模板混合在一起生成一个完整的html,然后inner到要inner的地方就ok了.
重点函数:把模板和数据混合起来
M.ViewBuilder=function(){
/**
*这是一个视图构建类,传进一个模板字符串和数据结构,将其构造成html
*/
return{
/**
*构造函数
*param s 数据结构,必须符合一定的规则
*param tpl_id 存放模板的html元素
*/
build:function(s,tpl_id){
var s=s;
if(s==null||tpl_id==null){
alert("ViewBuilder->build()的参数不完整")
}else if(s.data==null){
alert("ViewBuilder->build()的参数不是指定的数据结构吧")
}
//获取到模板字符串
var tpl=document.getElementById(tpl_id),tpl_str=tpl.innerHTML,reg_str="",count=1,replace_txt="";
//根据数据构造正则字符串
for(var data1 in s.data){
reg_str+="([\\s\\S]*?)\\$\\!"+data1+"";
count++
}
reg_str+="([\\s\\S]*)"
//构造完毕,生成正则字符串
var reg=new RegExp(reg_str);
//在模板字符串里执行这个正则
reg.exec(tpl_str)
count=1
tpl_str=""
//根据匹配的数据重新构造html
for(var data in s.data){
tpl_str+=RegExp["$"+count]+s.data[data]
count++
}
tpl_str+=RegExp["$"+count]
//返回html结构的字符串,ok
return tpl_str
}
}
}()
解释都在注释中,只是执行了一次正则,而不是循环执行的,那样会影响效率
这样我们就可以让模板变得清晰了,而且如果要修改结构只需要修改模板即可,模板跟平常的html没有差异,修改起来当然简单明了,而且这个构造函数是可重用的,在其他数据和模板中可以用,例如:我可以再定义一个投票的数据结构: /**
* 投票的数据
*/
M.VoteData=function(){}
M.VoteData.prototype={
data:{
id:0,//当前投票项目的id(数据库中的id)
yesorno:1//投的是yes就是1,no就是0
},
/**
* 根据数据构造请求字符串
*/
transS:function(){
return "id="+this.id+"&yesorno="+this.yesorno;
}
}只要数据结构符合标准和顺序,那就没有问题,好了,本文到此为止
2010年1月8日
为什么写这篇文章

这几天被一个省市菜单纠结了,测试给我提了N多bug,都是关于省市联动菜单的,大家就省市菜单到底应该具有何种行为持有各自的异议,特别是我,跟大家观点都不同,当然也有不少常识性的行为被我忽略了,下面是我这几天的bug列表,其中大部分都是关于这个联动的,当然js上联动实现起来也不难,这里讨论的不是技术细节,不讨论如何实现,只讨论联动到底应该怎样才能让用户感觉自己在使用一个自然而然的产品.我这个联动组件是在淘宝里使用的比较普遍的一个联动,不知道为什么问题还这么多,特别是我刚拿过来的时候,可用性极差.样式如下: 
先不说这个组件有什么不好的,一个联动组件至少应该有这些基本的行为才能符合人的习惯: (1),让用户一眼就可以看出这是一个做什么的组件. (2),让用户在很短的时间内就可以熟练操作这个组件. (3),可以保证用户的任何选择都不会引起数据的逻辑错误. 这是基本的三条,上面这个组件首先不符合第一条,不要因为我们一看就知道这是一个联动组件就行了,要把自己想象成一个从来没见过这种组件的电脑白痴,他第一次看到这个组件会认出来么,如果我是个电脑白痴,我会不会试着去点第二个和第三个下拉列表呢?然后才知道第一个可以点?看到的第一眼,如果我不做任何操作,我会知道这是干什么的么? 显然上面这个控件做不到这一点,他只是在旁边做了一个文字指示,但是用户仍然会困惑,特别是有三个下拉框,应该先点击哪个呢?会出现什么结果呢? 下面是点击第一个下拉菜单后的样式 
选中省后,市出现了,但是第一个元素是空的,仍然没有指示下一步该做什么,其实世界上有很多人对此会手足无措,即使大部分人知道应该去点第二个,下拉后才会看到市的选项. 我们来做个改变吧这样的组件不知道为什么被其他项目采用了,反正我们这个项目里,大家对这个组件很有意见,大家都踊跃提出意见,造成我的bug数目急剧上升,改了又改,大家还是不满意. 总结下吧,应该让联动菜单有哪些行为呢? (1)需要在三个联动菜单中显示三个默认值,来引导用户,让他看一眼就知道这里是让你选省市区的,开始我们定的是显示三个”请选择”,后来觉得还是不够明了,分别改成了”省份”,”城市”,”区(县)”,这样用户看到这个组件的时候至少知道这里原来是用来选省市区的呀.我们把空白替换成了省份,城市和区(县) 
(2)选择省后,城市下拉里填充元素,但是仍然显示一个城市给用户,而不是设置默认的市,这是为什么呢?有人说默认的省市区可以让用户更省力,可是上面说过了第三条我们要保证数据的准确性,不能设置任何默认值,以便获取尽量真实的用户地址,如果设置了默认的地址,用户直接点确定,当然这里是搜索,不需要太准备的数据,但是如果是在设置的地方,一定不能设置默认值 
(3)细心的人应该注意到了,这个时侯区县那里变成了不可用的,这里是我和同事争论的焦点,同事说应该显示默认的区(县),可是我坚持不显示(当然最后我还是听从pd的,把后面的加上了)为什么呢?我需要引导用户挨个选,如果同时显示了城市和区县,有的电脑白痴会试着去点后面那个,发现不可用才回来点城市这个,因为它想县区那里或许直接显示了他想要的县,省的选城市了,事实上县区那里什么都没有,我们应该引导用户,让他从左往右选,选择城市后县变成空的,大多数人就能知道这个是不可用的了,
(4)如果有的用户选择了省份之后又闲着没事会去选择默认文案”省份”呢?我嚼的应该将后面两个下拉都恢复默认的文案”城市”和”区县”,然后清空他的其他元素,而有的人说应该保留前面选择的城市,这样就可以只提交市,而不用选择省份信息了,这里我很纠结,我嚼的怎样都有道理,实在难以取舍,最后我还是坚持了自己的做法,那就是恢复默认文案,清楚选择的城市信息 (5)我选择了省份,然后选择了城市,这个时候如果我发现自己选错了城市,闲着没事我点了默认的文案”城市”,这个时候也应该做一下处理,在脚本中.具体根据情况而定 其实下拉表单该设计成什么样要看环境而定,如果是获取用户信息的地方,要求就要严格点,以便最大限度引导用户填写真实姓名.最近被bug搞晕了,以后再慢慢改进此文
什么能叫做css hack呢? ie6的_.ie6,ie7的*,ie8的\0这些都是很出名的hack,他们之所以称得上是hack是有理由的: (1)他们大多是浏览器的bug造成的,或者微软故意选取了一些蹩脚的方式来做hack,而且保证这些hack不会被别人使用 (2)浏览器不会修复这些hack,如果说一个方法,暂时可以用作hack,而有一天微软修复了ie6,让它不能辨认_,那么这个”_”就不能叫做hack了 (3)我支持的别人不支持的不能轻易用作hack 刚才要用到chrome和Safari的hack,我在网上搜了一下,大多数都是说将伪类”nth-of-type(1)”作为hack,我晕啊,这是css的标准属性啊,只是有的浏览器没实现而已,而且趋势是最终这个属性会被其他浏览器实现,这种东西也能做hack? 前一阵,这个hack可以用来区分chrome和Firefox,而现在Firefox也实现了这个属性,如果你以前做过100个网站,里面用了100处这个hack,欧耶,那你有事情做了,重新重构吧,累死人不偿命啊. 其他浏览器有可能实现,或者是浏览器超前实现的不能用作hack,hack必须是不会改变的,不过对于chrome来说我还真不知道该用什么hack比较安全,现在奉上chrome的hack.2010年1月份目前可用. @media screen and (-webkit-min-device-pixel-ratio:0) { body { background-color: blue; } } 此hack在被某些压缩工具压缩后会失效 我的个人博客:前端工作室
2009年12月18日
摘要: 按照惯例,demo在此独立浏览,这样显得更直观。以前做谷歌的小工具时,api里提供了一个很有用的函数,那就是在程序运行时可以使层动态随内容大小而变化,而且是平滑变换,在一些jquery的lightbox里也普遍有这种效果,看起来很酷的样子。下面我们就自己来实现一个这样的组件,没有参考其他资料,纯属自己瞎写。我觉得我这个方法很简单了,只需要在外边多套一个层就可以,而且可以容纳大量的文字(为什么这样说... 阅读全文
2009年12月13日
摘要: Lunascape刚刚发布了一款名叫Orion的浏览器产品,这也是Lunascape浏览器系列的第六个版本,主打卖点一样:三核心浏览器,包含IE、Firefox和Webkit核心,并且开始支持扩展。Lunascape表示,这款浏览器非常适合Web设计和开发人员,因为它可以随时在引擎之间进行切换,浏览器兼容目前最新的Windows 7系统,跑分速度也要明显更快。下载:Lunascape Orion ... 阅读全文
2009年11月30日
摘要: 这次的任务需要用到脚本了,我们要实现一个任务栏的东西,页面上所有的container层都可以最小化到这个任务栏的层中,如果本来的层是绝对定位的(position:absolute),则最小化之后再次恢复到原来的状态时,会回到原来的位置,而如果本来的层是相对定位的(positoin:relative),则层不会记住原来的位置,因为它是流动的,每次恢复状态的时候,层都将加入到文档流中,随着文档流变化,... 阅读全文
2009年11月29日
摘要: 前几天收到了一个google wave的邀请,说实话我对这个东西没啥兴趣,没时间去研究他到底是干什么的,但是作为一个前端,还是应该关心一下这些东西的,于是看了一下这玩意,看到第一眼嚼的好经验,我立即好奇起来,这种阴影好神奇啊,是咋做出来的呢,我去网上搜了一通,没有发现制作阴影的文章(除了那种粗糙的阴影),于是我用ff把它涉及到的图片都扒了下来,第一时间自己用这些图片组建了这种样式,可能我的方法和g... 阅读全文
|