【 tulaoshi.com - Web开发 】
                             
                            经常有人询问一些技巧,因此干脆写这么一篇文章给各位jQuery爱好者,算是抛砖引玉吧。 a)样式  很多人会认为样式是个很复杂的东西,需要沉着冷静的心态加上非凡的审美观才能设计出赏心悦目的UI,抛开图片设计不说,其实css也就是那么些属性:position,margin,padding,width,height,left,top,float,border,background... 
(本文来源于图老师网站,更多请访问http://www.tulaoshi.com/webkaifa/) 代码如下:
html xmlns="http://www.w3.org/1999/xhtml" 
head 
titleTest Page/title 
/head 
body 
jQuery是一个框架!压缩后有30多k吧。 
/body 
/html 
细心的人: 
代码如下:
html xmlns="http://www.w3.org/1999/xhtml" 
head 
titleTest Page/title 
style type="text/css" 
body 
{ 
font-family:'宋体'; 
font-size:12px; 
} 
/style 
/head 
body 
jQuery是一个框架!压缩后有30多k吧。 
/body 
/html 
专心的人: 
[Ctrl+A 全选 注:如需引入外部Js需刷新才能执行]
我们对比一下三者的UI效果: 
一目了然,或许很多的站点失去关注正是因为这不起眼的font-family,font-size。当然这还只是个简单的例子,掌握css应该从简单做起,从基本入手,在实践中运用并不断深入。 
b)脚本 
我们同样需要对javascript有着深刻的理解,对dom, xhr, Regex, call-apply, prototype等都应该有一定的了解。 
有人会说要这些有啥用啊,对dom的操作其实通过getElementById, getElementsByTagName以及其他的API都可以轻松的完成,这话是没错,当思路确定后,思想才是重点,一段代码是精华还是糟粕很容易就可以区分出来,究其原因还是取决你自己,举个简单的例子,大量的html组装,路人甲: 
代码如下:
var a = new Array(10); 
var menu = ''; 
for (var i = 0; i  a.length; i++) { 
menu += 'li class="style_' + a[i] + '" ' + a[i] + '/li'; 
} 
路人乙: 
代码如下:
String.prototype.format = function() { 
var args = arguments; 
return this.replace(/{(d{1})}/g, function() { 
return args[arguments[1]]; 
}); 
}; 
var a = new Array(1,2,3,4,5,6,7,8,9,0); 
var m = 'li class="style_{0}" {0}/li'; 
for (var i = 0; i  a.length; i++) { 
menu += m.format(a[i]); 
} 
在实现方式明确的情况下,优雅高效的代码显然更具吸引力。 
jQuery开发或使用,更多的灵感是来自实践,而不是copy||paste(奉行拿来主义的同学可以离开了)。 
那么在这里我会用一个简单的例子来阐述jQuery插件开发的流程,能否举一反三就看各位看官了。 
开发一个插件之前我们需要对自己的目的有一个清醒的认识,有很明确的方向感,那么此次我作为示例插件的目的,就是呈现一个用于UI的Slider - 滑动条,常年从事于或暂时专注于win32开发的同学应该比较了解。 
草图 
真正动手编码之前我们还需要有一个草图来描述自己插件的“长相”(事件驱动或API封装的可以忽略)。 
很多的同学在做UI开发前往往会忙于搜集各种小图片(非精通ps或iconworkshop人士),其实漂亮的图标的确可以美化我们的UI,不过我一般的处理方式是编写易于扩展的css,前期的UI呈现尽量少使用图片,多用线条完成。 
ok,言归正卷,那么我的slider设计草图是: 
解释下下文将用到的几个词: 
slider: 此部分是作为拖拽手柄来使用,用户可以通过拖拽此部分来更新completed bar的位置。 
completed: 此部分作为bar的内嵌元素,作为特殊效果来显示slider与起始点的距离,亦即与slider的value值关联。 
bar: slider的载体,completed的满值。 
思路:  slider作为手柄提供拖拽功能,作用区域为bar,拖拽过程中completed条必须实时更新(长度),影响区域为slider至bar左端的距离。 
  开发jQuery UI/Effect 插件在很多时候都需要与UI交互,因此在呈现上需要提供Html tree来绘制我们的插件,最终通过js dom来输出,那么在绘制简单的dom结构的时候我会直接用js来完成,不过如果嵌套比较复杂的话,我们还是应该先用html来完成,然后转变成js输出。 
html tree: 
代码如下:
div class="defaultbar" 
div class="jquery-completed" /div 
div class="jquery-jslider" /div 
/div 
deafultbar - bar 
jquery-completed - completed 
jquery-jslider - slider 
前期UI呈现上我们不使用图片,尽量用线条、颜色来完成: 
Css 
代码如下:
/**//*----default skin----*/ 
.defaultbar 
{}{ 
margin-top: 10px; 
height: 5px; 
background-color: #FFFFE0; 
border: 1px solid #A9C9E2; 
position: relative; 
} 
.defaultbar .jquery-completed 
{}{ 
height: 3px; 
background-color: #7d9edb; 
top: 1px; 
left:1px; 
position: absolute; 
} 
.defaultbar .jquery-jslider 
{}{ 
height: 15px; 
background-color: #E6E6FA; 
border: 1px solid #A5B6C8; 
top: -6px; 
display: block; 
cursor: pointer; 
position: absolute; 
} 
将bar的position属性设置成relative,以方便子节点的浮动(子节点使用position:absolute来获得内联浮动效果)。 
那么我们可以看下这个css和html tree产生的UI效果: 
ok,具备了所需的元素 - slider, completed, bar. 
一些规范: 
当我们画出了UI之后就可以正式编写jQuery插件代码了,不过在着之前我们还需要对jQuery插件开发的一些规范性有一些了解。 
1. 使用闭包:  代码如下:
(function($) { 
// Code goes here 
})(jQuery); 
这是来自jQuery官方的插件开发规范要求,使用这种编写方式有什么好处呢? 
a) 避免全局依赖。 
b) 避免第三方破坏。 
c) 兼容jQuery操作符'$'和'jQuery ' 
我们知道这段代码在被解析时会形同如下代码: 
代码如下:
var jq = function($) { 
// Code goes here 
}; 
jq(jQuery); 
这样效果就一目了然了。 
2. 扩展  jQuery提供了2个供用户扩展的‘基类' - $.extend和$.fn.extend. 
$.extend 用于扩展自身方法,如$.ajax, $.getJSON等,$.fn.extend则是用于扩展jQuery类,包括方法和对jQuery对象的操作。为了保持jQuery的完整性,我比较趋向于使用$.fn.extend进行插件开发而尽量少使用$.extend. 
3. 选择器  jQuery提供了功能强大,并兼容多种css版本的选择器,不过发现很多同学在使用选择器时并未注重效率的问题。 
a) 尽量使用Id选择器,jQuery的选择器使用的API都是基于getElementById或getElementsByTagName,因此可以知道效率最高的是Id选择器,因为jQuery会直接调用getElementById去获取dom,而通过样式选择器获取jQuery对象时往往会使用 getElementsByTagName去获取然后筛选。 
b) 样式选择器应该尽量明确指定tagName, 如果开发人员使用样式选择器来获取dom,且这些dom属于同一类型,例如获取所有className为jquery的div,那么我们应该使用的写法是$('div.jquery')而不是$('.jquery'),这样写的好处非常明显,在获取dom时jQuery会获取div然后进行筛选,而不是获取所有dom再筛选。 
c) 避免迭代,很多同学在使用jQuery获取指定上下文中的dom时喜欢使用迭代方式,如$('.jquery .child'),获取className为jquery的dom下的所有className为child的节点,其实这样编写代码付出的代价是非常大的,jQuery会不断的进行深层遍历来获取需要的元素,即使确实需要,我们也应该使用诸如$(selector,context), $('selector1selector2'), $(selector1).children(selector2), $(selctor1).find(selector2)之类的方式。 
开始编码  话题有点扯远,ok,在对UI有了清晰的认识后我们就可以使用js来输出html了。 
我们使用jSlider来命名这个slider插件(为了避免插件冲突,插件命名时也应十分考究,这里我就俗一回)。 
代码如下:
$.extend($.fn, { 
///summary 
/// apply a slider UI 
////summary 
jSlider: function(setting) { 
} 
}); 
在插件开发中比较标准的方式是将元数据独立出来并开放API,比如这里的setting参数传入值,有时候为了减少代码编写量,我习惯于直接在插件内赋值: 
代码如下:
var ps = $.extend({ 
renderTo: $(document.body), 
enable: true, 
initPosition: 'max', 
size: { barWidth: 200, sliderWidth: 5 }, 
barCssName: 'defaultbar', 
completedCssName: 'jquery-completed', 
sliderCssName: 'jquery-jslider', 
sliderHover: 'jquery-jslider-hover', 
onChanging: function() { }, 
onChanged: function() { } 
}, setting); 
规范的做法: 
代码如下:
$.fn.jSlider.default = { 
renderTo: $(document.body), 
enable: true, 
initPosition: 'max', 
size: { barWidth: 200, sliderWidth: 5 }, 
barCssName: 'defaultbar', 
completedCssName: 'jquery-completed', 
sliderCssName: 'jquery-jslider', 
sliderHover: 'jquery-jslider-hover', 
onChanging: function() { }, 
onChanged: function() { } 
}; 
$.extend({},$.fn.jSlider.default,setting); 
ok, 下面描述下我所定义的这些API的作用: 
renderTo: jSlider的载体、容器,可以是一个jQuery对象,也可以是选择器。 
enable: jSlider插件是否可用,true时end-user可拖拽,否则禁止。 
initPosition: jSlider的初始值,‘max'或者‘min',亦即 slider的value值,1或者0。 
size: jSlider的参数,包括2个值barWidth - bar的长度, sliderWidth - slider的长度。 
barCssName: bar的样式名称,便于end-user自行扩展样式。 
completedCssName: completed的样式名称。 
sliderCssName: slider的样式名称。 
sliderHover: slider聚焦时的样式名称。 
onChanging: slider被拖拽时触发的事件。 
onChanged: slider拖拽结束时触发的事件。 
此时我们需要将renderTo强制转换成jQuery对象(兼容使用selector的情况): 
ps.renderTo = (typeof ps.renderTo == 'string' ? 
$(ps.renderTo) : ps.renderTo); 
然后将html tree输出到render: 
/* ---------- 
html tree: 
代码如下:
div ----sliderbar 
div /div ---- completed bar 
div /div ---- slider 
/div 
-----------*/ 
var sliderbar = $('divdiv /divdiv /div/div') 
.attr('class', ps.barCssName) 
.css('width', ps.size.barWidth) 
.appendTo(ps.renderTo); 
var completedbar = sliderbar.find('div:eq(0)') 
.attr('class', ps.completedCssName); 
var slider = sliderbar.find('div:eq(1)') 
.attr('class', ps.sliderCssName) 
.css('width', ps.size.sliderWidth); 
这样我们就在UI上直接呈现了Html并且用定制的css进行渲染,分别用sliderbar, completedbar, slider对我们需要的三个对象进行缓存。 
ok, 在呈现了UI后我们就需要提供方法来实现slider的拖拽,在这之前我们还需要实现一个方法,就是completedbar的实时更新,即在拖动slider的时候让completedbar始终填充左侧区域: 
代码如下:
var bw = sliderbar.width(), sw = slider.width(); 
//make sure that the slider was displayed in the bar(make a limited) 
ps.limited = { min: 0, max: bw - sw }; 
if (typeof window.$sliderProcess == 'undefined') { 
window.$sliderProcess = new Function('obj1', 'obj2', 'left', 
'obj1.css('left',left);obj2.css('width',left);'); 
} 
$sliderProcess(slider, completedbar, eval('ps.limited.' + ps.initPosition)); 
bw,sw用来存储sliderbar和slider的长度,此处没有直接使用ps.size里的值是为了防止样式里的border-width对width造成破坏。 
定义一个私用成员limited来存储slider[left]的最大值和最小值,并在后面直接使用eval('ps.limited.' + ps.initPosition)来获取,从而避免switch操作。 
同时还需定义一个全局Function用来定位completedbar的填充长度以及slider左侧距离,我给其命名为$sliderProcess。 
那么我们接下来剩下的工作就是slider的拖拽功能了,那么在这里我会用到之前发布的一款jQuery拖拽插件,并做适量的订制: 
代码如下:
//drag and drop 
var slide = { 
drag: function(e) { 
var d = e.data; 
var l = Math.min(Math.max(e.pageX - d.pageX + d.left, ps.limited.min), ps.limited.max); 
$sliderProcess(slider, completedbar, l); 
//push two parameters: 1st:percentage, 2nd: event 
ps.onChanging(l / ps.limited.max, e); 
}, 
drop: function(e) { 
slider.removeClass(ps.sliderHover); 
//push two parameters: 1st:percentage, 2nd: event 
ps.onChanged(parseInt(slider.css('left')) / sw - ps.limited.max, e); 
$().unbind('mousemove', slide.drag).unbind('mouseup', slide.drop); 
} 
}; 
if (ps.enable) { 
//bind events 
slider.bind('mousedown', function(e) { 
var d = { 
left: parseInt(slider.css('left')), 
pageX: e.pageX 
}; 
$(this).addClass(ps.sliderHover); 
$().bind('mousemove', d, slide.drag).bind('mouseup', d, slide.drop); 
}); 
} 
这样当jSlider enable属性为true时,在end-user按下鼠标时绑定mousemove事件,在鼠标弹起时移除,我们只需要同步更新slider的left 属性和completedbar的width即可,同时在drag中绑定onChanging方法,在drop中绑定onChanged方法,向这两个方法推送的参数相同,1百分比,即value值,介于0~1,2event。 
那么至此我们的jSlider插件就基本成型,向用户提供了一个可拖拽的slider。 
有的时候用户却不是那么容易满足,于是有人高呼:“我要自己设置value,为什么不提供这个功能?”。 
那么这时我们就需要为用户公开一个方法,用于设置jSlider的value,首先考虑的是作为方法需要一个作用对象(jSlider),那么此时我又不想将作用对象作为参数传入,那么我们还是将这个方法作为插件来开发,我们将方法命名为setSliderValue,开放2个参数,v(value值)和 callback(设置完成后的回调函数)。 
即:$.fn.setSliderValue(v,callback); 
ok,那么剩下的就是作用对象了,由之前的设计可知,在slider拖动时主要作用于2个对象,slider和completedbar,那么我们在jSlider插件末尾加上一段代码来返回slider对象: 
代码如下:
slider.data = { bar: sliderbar, completed: completedbar }; 
return slider; 
这样我们在初始化jSlider的时候就可以直接用一个变量来获取jSlider对象,然后调用setSliderValue方法了,伪码: 
代码如下:
var slider = $.fn.jSlider({}); 
slider.setSliderValue(v,function(){}); 
setSliderValue代码: 
try { 
//validate 
if (typeof v == 'undefined' || v  0 || v  1) { 
throw new Error(''v' must be a Float variable between 0 and 1.'); 
} 
var s = this; 
//validate 
if (typeof s == 'undefined' || 
typeof s.data == 'undefined' || 
typeof s.data.bar == 'undefined') { 
throw new Error('You bound the method to an object that is not a slider!'); 
} 
$sliderProcess(s, s.data.completed, v * s.data.bar.width()); 
if (typeof callback != 'undefined') { callback(v); } 
} 
catch (e) { 
alert(e.message); 
} 
这里同样调用了全局Function $sliderProcess在设置slider的value值时进行completedbar[width]和slider[left]的更新。由于此处进行了异常处理,所以如果end-user在确保setSliderValue被作用于jSlider对象的时候可以删除此异常处理代码。  
根据jSlider的API我们可以更加方便的为其设定皮肤,为了让jSlider更加专业,我们需要2张图片: 
用来作为completedbar背景的'bar'和用来作为slider背景的'slider',ok,我们更新下样式: 
BlueSkin 
代码如下:
/**//*----blue skin----*/ 
.bluebar 
{}{ 
margin-top: 10px; 
height: 4px; 
background:#F7F7F7; 
border:solid 1px #3e3e3e; 
position: relative; 
} 
.bluebar .jquery-completed 
{}{ 
height: 4px; 
background:url(../images/slider/blue/bar.gif) left center no-repeat; 
top: 0; 
left:0; 
position: absolute; 
} 
.bluebar .jquery-jslider 
{}{ 
height: 17px; 
background:url(../images/slider/blue/slider.gif) center 0 no-repeat; 
top: -4px; 
display: block; 
cursor: pointer; 
position: absolute; 
} 
.bluebar .jquery-jslider-hover 
{}{ 
background-position:center -17px; 
} 
由于在设置样式时我仍然让子节点样式使用了API的默认值,因此在创建jSlider时我们只需要设置barCssName就行了: 
代码如下:
var blue = $.fn.jSlider({ 
renderTo: '#slidercontainer', 
size: { barWidth: 500, sliderWidth: 10 }, 
barCssName: 'bluebar', 
onChanging: function(percentage, e) { 
// code goes here 
} 
}); 
呈现出来的UI: 
我们这样来设置其值: 
代码如下:
//set percentage with a callback function 
blue.setSliderValue(0.65, function(percentage) { 
// code goes here 
}); 
  当然,我们不仅可以将jSlider作为slider使用,有时候它也是一个progressbar: 
(代码我就不贴了,直接在demo里查看 ;-) )  
通篇到这里就结束了,简单的介绍了一款jQuery插件的开发流程,以及开发中应该注意的细节,那么在下一篇的文章中我会向大家介绍如何打造一个通用型的 自动完成 插件。 
(本文来源于图老师网站,更多请访问http://www.tulaoshi.com/webkaifa/)