已走过的2011
2011结束了,距离开青岛一年多
这段时间忠旭模具上线运行1年
这段时间已经把设计扔掉了
这段时间写起JavaScript更加熟悉了
这段时间了解很多linux的东西
这段时间少了很多想法
这段时间仿佛老了很多
2011年偷偷的就溜走了
而我好像还是站在原地
生活耍的我团团转
而我好像失去了自己的方向
转载请注明原文出处《已走过的2011》 如无特别声明,所有文章均遵守创作共用 署名-非商业-禁止演绎 3.0协议。
瀑布布局的JavaScript实现方式
瀑布式布局是一种多列等宽不等高的一种页面展示方式,用于图片来源比较复杂,图片尺寸比较复杂时可以使用的一种展示方式,这种展示方式比较优美,让人有种错落有致的感觉.这种展示方式在淘宝的我要买,新浪微博的广场以及蘑菇街等等网站都有应用.这里是我刚刚做的一个小站91美图
实现布局有三个思路:
- 最传统的思路,多弄几个容器,分几列,然后往每个列里面插入元素.其实用table分几列实现更加方便:P;
- 使用html5中css3的多列布局来实现.参见w3c标准中的css3多列布局模块;
- 使用绝对布局,通过javascript生成元素的布局位置.
前两种方法在网上都有比较详细的介绍,我这里就不再多说了,这里主要说一下我做的第三种实现的优缺点以及我的实现思路.
第三种方案是所有的要布局的元素都是绝对定位的,然后通过javascript来判断每个元素位置,动态设置位置实现布局.
缺点
需要使用javascript来遍历元素,然后要根据前一个元素来判断后一个元素的位置,这样可能对一些老版本的浏览器造成负担,特别是IE6这种老古董,而且在javascript失效的时候,整个页面的布局都会乱掉.另外如果整个页面宽度是变化的,则可能每次窗口尺寸改变时都要重新计算所有元素的位置,在页面中元素较多的时候可能会有闪烁的现象.另外如果页面中出现图片,则需要实现定义好图片的尺寸,否则会出现无法正确计算元素位置的情况.
优点
布局更加灵活,元素绝对定位,可以使用javascript灵活操作.页面宽度改变时可以重新布局整个页面.可以使页面的中的元素真正流动起来,让新添加的元素插入到高度最低的列,使页面的低端更加整齐,对插入的元素高地要求较低.可以较为方便的实现延迟加载.
具体实现
最开始我实现的时候是通过使用javascript生成虚拟的列,根据元素的顺序为每个元素分配一个列和行,然后计算每个元素的位置,举个例子,现在假设有四个列:
使其在页面中布局.事实上这个解决办法跟第一种和第二种是一个道理的.最后页面每列的高度差别可能会很大.
//getElements()方法用于获取页面中的元素
var items = getElements();
var columnCount = 4;
var columnWidth = 230;
var padding = 8;
//遍历所有元素
for(var i = 0, len = items.length; i < len; i++){
//获取当前的元素
var currentItem = items[i];
//获取当前对象的列
var currentColumn = (i + 1) % 4;
//获取当前对象的行
var currentLevel = parseInt(i / 4);
//有了当前的行跟列可以根据上一层的对象计算出当前对象的高度
var left = currentColumn * columnWidth;
var top = items[i - 4] ? 0 : items[i - 4].style.top + items[i - 4].clientHeight + padding;
//设置当前的位置
currentItem.style.top = top + 'px';
currentItem.style.left = left + 'px';
}
代码和逻辑都比较简单,根据当前的行跟列计算出位置就行了.但是这个逻辑还是会出现元素高地差距过大的情况.看一下新浪weibo的广场图片效果:
可以看到越到最后可能列高度之间的差距会越大.这不是我们想要实现的效果.
所以我这里换了一个思路,虚拟的列还是要有的,但是行的概念我们抛弃掉,我采用的是一个类似流动的概念,新插入的元素是根据每个列的高度,那个高度最低就流向哪个列,最后确保每个列的高度都趋近一致,实现我们想要的效果.当然我们可以采取获取所有元素的高度,然后统一计算一下,获取一个最佳的排列方法,但是这会给浏览器带来较大的计算量,而且如果不断的加载更多的图片我们会得不偿失,所以我们采用的是一个流动的模型,只让当前对象寻找最低的高度然后插入.
这里我实现了一个Column对象,一个ImgItem对象.Column对象用于维护每一列的信息,包括列的最到高度列宽度等列信息.ImgItem对象保存了一个html节点对象,还有一些设置元素位置,获取当前元素位置的方法.
下面是Column对象的代码:
var Column = function(order){
this.order = order;
this.maxHeight = 0;
this.columnWidth = 230;
this.left = this.columnWidth * order;
this.lastItem = null;
this.positioned = false;
this.setReferItem = function(item){
this.lastItem = item;
}
this.getHeight = function(){
if(this.lastItem){
this.maxHeight = this.lastItem.getBottom();
}
return this.maxHeight;
}
this.getLeft = function(){
return this.left;
}
};
ImgItem对象的代码:
var ImgItem = function(referNode, column){
this.referNode = referNode;
this.bottom = -1;
this.positioned = false;
};
ImgItem.prototype = {
/*
*set the refer node's top
* @param value: Number
*/
setTop: function(value){
this.referNode.style.top = value + 'px';
},
/*
*set the refer node's left
* @param value: Number
*/
setLeft: function(value){
this.referNode.style.left = value + 'px';
},
/*
*get the refer node bottom position
*/
getBottom: function(){
if(this.positioned){
if(this.bottom < 0){
this.bottom = parseInt(this.referNode.style.top) + this.referNode.clientHeight;
}
return this.bottom;
}else{
throw("current node has not been positioned!");
}
},
setPosition: function(column){
this.positioned = true;
this.setLeft(column.getLeft());
this.setTop(column.getHeight() + 10);
column.setReferItem(this);
}
};
基础打好了,下面要做的就是给元素进行布局了:
//首先根据配置信息中的列数初始化列
for(var i = 0, len = this.config.columnCount; i < len; i++){
this.columns.push(new Column(i));
}
//获取页面上已存在的对象
var liItems = document.getElementById('img_list').getElementsByTagName('li');
//将所有的对象进行布局
for(i = 0, len = liItems.length; i < len; i++){
this.addNewItem(liItems[i]);
}
好吧这里还用到了一个addNewItem方法:
getMinHeightColumn: function(){
var minHeight = -1, tempColumn = null;
//遍历所有的列,获取当前高度最低的列,并返回
for(var i = 0,len = this.columns.length; i < len; i++){ if(minHeight > this.columns[i].getHeight() || minHeight == -1){
minHeight = this.columns[i].getHeight();
tempColumn = this.columns[i];
}
}
return tempColumn;
},
getMaxHeight: function(){
var maxHeight = -1;
//遍历列对象,获取高度最高的列并返回高度
for(var i = 0, len = this.columns.length; i < len; i++){
if(maxHeight < this.columns[i].getHeight()){
maxHeight = this.columns[i].getHeight();
}
}
return maxHeight;
},
addNewItem: function(liItem){
//设置元素的位置
var imgItem = new ImgItem(liItem);
imgItem.setPosition(this.getMinHeightColumn());
this.cachedItems.push();
//设置容器的高度
document.getElementById('img_list').style.height = this.getMaxHeight() + 'px';
}
基本的布局逻辑已经都齐了,还有的就是lazyload的一些逻辑了,这些逻辑都比较通用.加载后布局对象的逻辑是相同的.这里就不再赘述了.
抛砖引玉,如有谬误还请不吝指教.
转载请注明原文出处《瀑布布局的JavaScript实现方式》 如无特别声明,所有文章均遵守创作共用 署名-非商业-禁止演绎 3.0协议。
JavaScript与设计模式
这篇文章主要是记录自己的一些想法,同时也能够让刚刚接触编程的人对如何构建健壮易用的代码有所了解,而有丰富编程经验的高手能够给予意见和建议.这里打算把这个议题写成一个系列文章,这篇就算做一个引子,废话可能多一点,见谅:P
模式实际上是生活中很常见的概念,它并不神奇.举个例子,我们迈过一个台阶,这里很自然的你就使用了一种模式,要先抬高一条腿,然后将脚落到前面的台阶上,然后再抬高另外一条腿,放到另外一个台阶上,这个过程很自然的完成了.当然你也可以选择两条腿同时蹦上去,或者加上手,爬上去,但这不是首选的方式.我们迈过台阶的时候实际就是一个迈台阶的模式,你不用考虑很久直接就会迈过去,事实上如果你连迈台阶都要考虑很久的话…可能你的日子就没法正常过了.
程序设计中的模式也是对程序设计中可能碰到的问题的解决方法进行抽象和总结得出的一些范式.你可以在解决典型问题的时候使用相关的模式,相当于站在巨人的肩上,只有基础打好了,后面才会有更好的发展.合理的使用设计模式能够使你构建出健壮,易扩展,易维护的程序.
设计模式方面的资料非常多,但是绝大多数都是针对强类型的语言实现写的,如Java等,而JavaScript则是弱类型的语言,它没有类,接口,抽象类,继承,重载,多态等概念.所以在实现的时候有更多的变通余地,更加灵活,但是随之而来的就是不利于控制.所以编写优秀的JavaScript代码对编程人员的要求反而更高,因为很多东西编译器无法替你检查,你需要自己去控制.
先说类,JavaScript中没有类的概念,但是Function对象可以实现类的大部分功能,它可以作为一个完整的对象来使用,其实我理解的类就是一个有自己属性的,有自己行为的一个抽象,我们没必要拘泥于类的这个概念.这方面的资料很多,这里就不多说了.
再说接口,这个概念JavaScript确实没有,也没类似的代替内容,接口在我理解中其实是签订的一个契约,继承接口的类必须尊重契约做事,至于这个契约是实质的契约还是口头的约定,其形式更大于内容.当然有接口,编译器会在编译的时候发现不遵守契约的坏分子,让其无所遁形,给开发者带来很大的便利.如果所有的类都是君子的话,这个检查就没什么必要了是吧,我相信每个写JavaScript的程序员都是君子:)这里的接口可以是我们的一段注释,我们约定这里必须存在哪些属性,哪些方法,实现的时候就要做到这些,当然我们也可以设计一个额外的功能用于检测目标对象是否真正的实现了相关的方法.
一下是一个简单的实例,我们来定义一个电器类的接口:
code-1:
/*
Interface IElectrialEquipment {
Number Voltage; //额定电压
Number Current; //额定电流
function void TurnOn(); //打开
function void TurnOff(); //关闭
}
*/
我们把这段注释拿来当我们的契约,这里我们不提接口.我们要实现一个实现这个契约的电视机,或者电冰箱.
那我的实现代码应该是这个样子的:
var TV = function(voltage, current){ //Implement IElectrialEquipment
this.Voltage = voltage;
this.Current = current;
this.TurnOn = function(){
//打开电视机
console.log('电视启动了');
};
this.TurnOff = function(){
//关闭电视机
console.log('电视关闭了');
};
this.TurnDown = function(){
//调低音量
}
this.TurnUp = function(){
//调高音量
}
}
var Fridge = function(voltage, current){
this.Voltage = voltage;
this.Current = current;
this.TurnOn = function(){
//打开电冰箱
console.log('冰箱启动了');
};
this.TurnOff = function(){
//关闭电冰箱
console.log('冰箱关闭了');
};
this.OpenDoor = function(){
//打开冰箱门
};
this.CloseDoor = function(){
//关闭冰箱门
}
}
//现在我们再来做一个万能遥控器
var PowerfulRemoteController = function(IElectrialEquipment){
this.TurnOn = function(){
IElectrialEquipment.TurnOn();
}
this.TurnOn = function(){
IElectrialEquipment.TurnOff();
}
this.ShowInfo = function(){
console.log('额定电压是: ' + IElectrialEquipment.Voltage + ' 额定电流是: ' + IElectrialEquipment.Current);
}
}
//现在我要打开冰箱,不然东西全坏了
new PowerfulRemoteController(new Fridge(225, 5)).TurnOn();
//现在我要打开电视看电视,看点新闻联播吧
new PowerfulRemoteController(new TV(225, 2)).TurnOn();
这里如果我们要继续控制我们的微波炉或者电脑,我们只需要按照我们定义好的契约实现相关的对象就可以使用我们的万能遥控器控制了,而且我们的万能遥控器完全不需要修改任何内容,这也就是我们常说的对修改关闭,对扩展开放原则.大家可以在Fx中的命令行里面试一下.但是弊端也有,就是如果某位程序员在实现一个电器的时候没有实现TurnOff或者TurnOn方法,编译器是不会报错的.当然我们的万能遥控器也就失效了.所以这种方法对团队里面的开发人员要求比较高,大家都要是君子,做事情都要按契约来.
事实上我们也可以定义一个检测对象,用检测对象强制检测一下实现的对象中是否含有我们需要的属性或方法,如果没有则报错.
其他的重载多态JavaScript中也没有相应的实现,只能通过一些手段来实现类似的功能,比如重载,JavaScript中后定义的方法会完全覆盖先前定义的方法,这个可以拿来凑合一下当作重载。JavaScript对传入实参的类型和数目没有强制的要求,所以可以通过判断传入的实参的数目和类型来做不同的操作实现类似多态的功能。
转载请注明原文出处《JavaScript与设计模式》 如无特别声明,所有文章均遵守创作共用 署名-非商业-禁止演绎 3.0协议。
表单验证工具V0.2
是的,我又把那个表单验证工具重构了一遍。事实证明上一个版本的表单验证工具API设计的还算是比较成功,起码这个版本完全重构后,基本的API没有改动。只是对实现部分的代码进行了彻底的重构。由于使用了jQuery的ajax相关功能,所以需要本控件需要jQuery支持。
上一个版本中是把所有的规则存储到一个缓存中,然后通过统一的方法进行节点的验证,这样带来的不便就是需要在全局对所有的节点进行控制,现在是将检查的节点封装一个对象,让数据和行为联系更加紧密,每个人只干自己的事情,逻辑上更加清晰。全局validate对象只负责初始化和最终表单整体的验证,以及提供一些工具方法。另外将提示信息也封装成为一个对象,便于提示信息样式的更改。
validate对象现在只包含两个对外开放的方法,一个是init,一个是check。init方法用于初始化,传入相关的对象id,规则,事件等信息,check方法用于检查整个表单是否通过验证,当表单中含有ajax验证时,会在验证通过后调用回调函数。
check方法有个可选的回调函数参数,当验证规则中存在ajax验证时,控件并不能即时给出验证结果,具体的验证结果会在ajax回调完成后得出。所以这里只要存在尚未验证过的ajax验证,check方法总是会返回false。如果通过调用后想执行一些操作,就需要将需要操作的步骤写到check方法的回调函数中。这样在validate在进行完ajax检查并且通过验证后会调用方法。
VNode对象代码的主体结构如下:
var VNode = function(rule){
self.check = function(isLocal){
//local check
//ajax check
};
self.checkAjax = function(fn){};
self.reset = function(){};
self.checkManul = function(isLocal){};
self.checkEvent = function(e){};
self.resetEvent = function(){};
if(rule.trigger){
rule.trigger.call(self);
}else{
$(self.node).bind(validate.config.trigger, self.checkEvent);
}
$(self.node).bind(validate.config.reset, self.resetEvent);
};
这里VNode包含了所有的数据,规则,以及验证的逻辑,验证的时候要做的就是新建一个VNode对象,剩下的工作交个VNode来做就好了。VNode对象主要是根据提供的规则信息获取要验证的元素,然后对要验证的元素按照一定的次序进行验证,包括自定义的验证方法,长度,自定义的正则表达式,系统提供的正则验证,ajax验证。所有的验证都是验证规则存在是才进行的。
VNode对象也提供了两个钩子,一个是自定义验证函数,一个是自定义触发验证函数。自定义验证函数用于处理复杂逻辑的验证,自定义触发验证函数默认取代blur方法,用于日历控件等等选择方式的验证。
还有一个重要的对象就是HintMessage对象,这个对象其实就是根据给出的引用节点,自动计算出提示信息该显示的位置。当然它有相应的参数控制显示的偏移量,默认是与给出的节点位置重合的。当然新实例化一个HintMessage对象并不能显示出提示信息,要想显示提示信息还需要调用HintMessage.show(message, messageType)方法来显示相应的信息以及相应的信息类型。信息类型包含以下几种:
//错误 HintMessage.ERROR = 0; //警告 HintMessage.WARNING = 1; //消息 HintMessage.MESSAGE = 2; //成功 HintMessage.SUCCESS = 3;
每种消息类型对应不同的样式名称,你可以通过自定义样式来显示不同样式的提示信息。当然HintMessage还有一点要注意就是给window对象绑定onresize事件,当窗口改变大小的时候需要重新计算提示信息显示的位置。绝对定位带来方便的同时也有些副作用。
相关文章:
转载请注明原文出处《表单验证工具V0.2》 如无特别声明,所有文章均遵守创作共用 署名-非商业-禁止演绎 3.0协议。
Raphaël-JavaScript Library-javascript矢量绘图
你是不是经常究竟在用到javascript绘图时纠结于IE的不支持canvas,纠结于canvas绘图API的繁琐纠结?现在你可以从这种纠结中摆脱出来了。
对,Raphaël是一款轻量的用于web的矢量绘图javascript库,用它你可以很简单的创建各类矢量图形,而且它本身还支持丰富的接口以及各种事件处理。更关键的是它绘出的图像都是矢量的。当然你也可以创建一个包含位图的图像。
Raphaël是使用SVG和VML进行绘图的,所以不要担心兼容性,它支持FireFox3.0+,Chrome5.0+,Opera9.0+以及Internet Explorer6.0+,对你没看错,它完美支持IE6+。而且Sencha Labs也使用了这个库。
怎么样心动了吧?心动不如行动,赶快拿起手中的电话订购吧,呃,赶快用你的鼠标点击下载使用吧!
简单的使用方法:
// Creates canvas 320 × 200 at 10, 50
var paper = Raphael(10, 50, 320, 200);
// Creates circle at x = 50, y = 40, with radius 10
var< circle = paper.circle(50, 40, 10);
// Sets the fill attribute of the circle to red (#f00)
circle.attr("fill", "#f00");
// Sets the stroke attribute of the circle to white
circle.attr("stroke", "#fff");
转载请注明原文出处《Raphaël-JavaScript Library-javascript矢量绘图》 如无特别声明,所有文章均遵守创作共用 署名-非商业-禁止演绎 3.0协议。
commonjs-module1.1.1-draft-中文版
Contract 约定
Require
- requrie是一个函数
- require函数接受一个模块标识符
- require返回外部模块的输出API
- 如果有一个依赖循环,那么外部模块在它被另外一个依赖请求时,可能并没有完成执行;在这种情况下,在调用require请求当前模块执行之前,require返回的对象必须至少包含外部模块准备好的输出接口。
- 如果请求的模块无法获取,require必须抛出一个错误
- require函数可以有一个main属性
- 这个属性可行时,应该是只读的且不能被删除
- main属性必须是undefined或者与一个已加载的模块上下文中的module相等
- require函数可以有一个paths属性,它是一个优化过的路径字符串数组,从高到低,到最顶层的模块路径。
- paths属性不能存在在沙箱(sandbox,一种安全的模块系统)中
- paths属性必须在所有模块中的引用必须一致
- 使用替换对象取代paths属性可能没有任何作用
- 如果paths属性存在,paths属性内容的修改必须被相应模块的搜索行为所体现
- 如果paths属性存在,它可能不是一个搜索路径的详细清单,加载器可能在提及的路径之前或之后在内部查找其他位置
- 如果paths属性存在,它是加载器解决,标准化及规范化提供的路径的优先选择。
Module Context 模块上下文
- 在模块中有一个符合上述定义的自由变量require
- 在模块中有一个自由变量exports,模块可以向其添加自己的API作为自己的执行方法。
- 模块必须使用exports对象作为对外的唯一途径
- 在模块中必须要有个自由变量,module对象
- module对象必须有一个id属性作为顶层的模块id。这个id属性必须能够通过使用require(module.id)返回module.id来源所对应的导出对象。
- module对象可以有一个uri字符串,它应该是一个合格的指向模块被创建的资源的URI(统一资源标示符).uir属性不应该出现在沙箱中。
Module Identifiers 模块标示符
- 模块的标示符(id)是一个以斜线分割的协议字符串
- 一个协议必须是驼峰式的标示符,“.”或者“..”
- 模块标示符可以没有文件名后缀例如“.js”
- 模块标示符可以是相对路径,也可以是绝对路径。如果一个模块标示符是以“.”或者“..”开始的,那么它是相对的。
- 绝对标示符用于解决概念模块命名空间的根。
- 相对标示符被解决相对于requrie被写和调用的模块标示符
Unspecified 未指明
标准中留下了一下未作说明的几个要点:
- 模块是否存储在数据库中,文件系统中,或者工厂方法,或是互换链接库
- 是否支持的路径是为解决模块加载器模块标示符
转载请注明原文出处《commonjs-module1.1.1-draft-中文版》 如无特别声明,所有文章均遵守创作共用 署名-非商业-禁止演绎 3.0协议。
