jQuery高级应用:优化Web应用程序的最后绝招
[2] jQuery高级应用:优化Web应用程序的最后绝招
第二个示例Web应用程序
我将使用另一个小部件解决本文的最后3个问题,并且在深入研究其代码之前展示和解释它。这个401k小部件并不陌生,因为您已经在前面的文章见过它(参见参考资料部分获取这些文章的链接)。不过,这回有个微妙的不同之处,因为我在同一个页面上两次添加了这个小部件。它被添加到两个不同的表中。这将带来几个有趣的地方。图3显示了这个小部件:
在这个小部件中,我正在做几件事情。第一件是计算文本字段之和并确定它们是否为100。如果它们的和不为100,我将向用户显示一个错误,提示他们没有正确使用该小部件。第二,我在每个选项获取输入之后对选项进行排序。通过这种方式,百分比最高的投资分配将一直出现在表的顶部。这可以在图3中看到,它按百分比对选项进行排序。最后,为了让它更酷,我添加了一些条带。
用于生产这个小部件的HTML代码出奇地简单。清单8详细地显示了这个小部件。
1. <p><tablewidthtablewidth=300class="percentSort"cellpadding=0cellspacing=0>
2. <tbody>
3. <tr><td>S&P500Index</td>
4. <td><inputtypeinputtype=text>%</td></tr>
5. <tr><td>Russell2000Index</td>
6. <td><inputtypeinputtype=text>%</td></tr>
7. <tr><td>MSCIInternationalIndex</td>
8. <td><inputtypeinputtype=text>%</td></tr>
9. <tr><td>MSCIEmergingMarketIndex</td>
10. <td><inputtypeinputtype=text>%</td></tr>
11. <tr><td>REITIndex</td>
12. <td><inputtypeinputtype=text>%</td></tr>
13. </tbody>
14. <tfoot>
15. </tfoot>
16. </table>
用jQuery设置小部件
以上的一小段HTML直接引入了这部分内容,本小节关注如何在jQuery中设置小部件,以及所需的所有代码。要将事件附加到页面元素或在特定情况下需要添加类时,通常需要这样做。有时候还需要更进一步。这些小部件的所有设置代码都是jQuery代码。
我可以提供关于角色分离的理论,让HTML设计师和JavaScript程序员各自完成自己的工作,但是您们可能已经多次听到这种陈词。在这里我仅添加另一样东西,即“类修饰”,这是很多插件创作者都使用的。看看清单8中的HTML代码,仅通过将一个percentSort类添加到表,您就可以显著改变表的功能和外观。这是小部件设计的目标,让添加和删除小部件就像向小部件添加类一样简单。
让我们遵循我曾用jQuery设置小部件的几个步骤。通过查看这些步骤,您可以看到清单9中的设计模式是如何出现的。
$(document).ready(function(){
//thefirststepistofindallthetablesonthepagewith
//aclassofpercentSort.Theseareallthetableswewantto
//convertintoourwidget.
//Afterwefindthem,weneedtoloopthroughthemandtakesome
//actionsonthem
//Attheconclusionofthisblockofcode,eachtablethat'sgoingto
//beapercentSortwidgetwillhavebeentransformed
$("table.percentSort").each(function(i){
//eachtableneedsauniqueID,fornamespaceissues(discussedlater)
//wecansimplycreateauniqueIDfromtheloopcounter
$(this).attr("id","percentSort-"+i);
//withineachtable,let'shighlighteveryotherrowinthetable,to
//giveitthat"zebra"look
$(this).find("tbody>tr").filter(":odd").addClass("highlight");
//becauseeachtableneedstoshowthe"Total"totheuser,let'screateanew
//sectionofthetableinthefooter.We'lladdarowinthetablefooter
//todisplaythewords"Total"andaspanfortheupdatedcount.
$("#"+$(this).attr("id")+"tfoot")
.append("<tr><td>Total</td><td>
<span></span>%</td></tr>");
//finally,let'saddtheCLASSof"percentTotal"tothespanwejust
//createdabove.We'llusethisinformationlatertodisplay
//theupdatedtotals
$("#"+$(this).attr("id")+"tfootspan").addClass("percentTotal");
});
//nowthesecondstep,afterwe'vecompletedsettingupthetablesthemselves
//istosetuptheindividualtablerows.
//Wecansimilarlysortthrougheachofthem,takingtheappropriateactions
//oneachoftheminturn.
//Uponcompletionofthisblockofcode,eachrowineachtablewillbe
//transformedforourwidget
1. $("table.percentSorttbody>tr").each(function(i){
2.
3. //getthenamespace(tobediscussedinthenextsection)
4.
5. varNAMESPACE=$(this).parents("table.percentSort").attr("id");
6.
7. //attachauniqueIDtothisrow.Wecanusetheloopcounter
8. //toensuretheIDisuniqueonthepage(whichisamustoneverypage)
9.
10. $(this).attr("id","row"+i);
11.
12. //now,withinthisrowofthetable,weneedtofindthetextinput,because
13. //weneedtoattachaclasstothem.Weutilizethenamespace,andalso
14. //findthe:textwithinthetablerow,andthenattachthecorrectclass
15.
16. $("#"+$(this).attr("id")+":text").addClass("percent");
17.
18. //Finally,weattachauniqueIDtoeachofthetextinputs,andwedothisby
19. //makingitacombinationofthetablenameandtherowname.
20.
21. $("#"+$(this).attr("id")+".percent").
22. attr("id",NAMESPACE+"-"+$(this).attr("id"));
23. });
24. //Finally,becauseweknowweonlywantnumericalinputs,werestrictthetextentry
25. //tojustnumbers.Wemustdothisnow,becauseupuntilthispoint,thepage
26. //containednoelementswiththeCLASS"percent"
27. $(".percent").numeric();
如您从这个例子中见到的一样,可以通过jQuery代码向HTML代码引入大量功能。这种类型的设计的好处是很明显的。同样,遵循角色分离、代码重用等也是非常有益的。您还将在小部件插件中看到这种类型的设计,因为它将简单的HTML代码转变成适用于插件的小部件。最重要的是,这也是您在这里需要完成的任务,即编写一个插件来将一个简单的表转变成排序和汇总表。
记住:尽量多使用jQuery代码进行设置,并且尽可能少使用HTML。
名称空间
可能处理这种类型的设计和jQuery的最难解方面是理解名称空间。这个例子很好,因为它以一种很直观的方式展示了该问题。知道页面上的所有ID和类时,编写jQuery代码是非常简单的。这就是jQuery的设计目标。当您不知道页面上有几个类时,或这些类开始重复时,会发生什么呢?在这个例子中,您可以直接看到问题,有两个完全相同的小部件!所有东西都是重复的。这似乎是不考虑名称空间引起的问题;您的小部件开始彼此干扰,最终导致不能正常工作。
造成这个问题的原因是您仅调用$(".percentTotal")等,而忽略了应该同步哪个小部件。因为相同的页面上有多个表,因此该页面就有percentTotal类的多个实例。当然,如果页面上仅有一个表,那么您就可以确定它是唯一的。但是随着页面更加高级,以及组件的重用越来越多,这种一个表的假设就不再成立。有些人会问,“在这里使用ID不行吗?”这不能解决问题:您打算给它什么ID?您不可以使用“percentTotal”,因为这会带来歧义。
也不可以使用“percentTotal-1”,因为它不表示页面上的任何东西。(以上数字毕竟是任意创建的)。您可以向页面包含的表添加一些引用来解决问题,比如“percentTotal-percentSort1”,但这会让问题更加复杂。jQuery有一个超级复杂但又十分容易使用的选择语法,从而让这种混合命名模式变得毫无必要。为什么要重新创造已有的东西呢?让我们使用jQuery的选择引擎帮助您解决名称空间问题。
这个小部件的核心问题是决定操作发生在哪个小部件中。当向文本框输入数字时,您可以问自己,jQuery如何知道进入哪个文本框?您在代码中的“百分比”类添加了一个事件,并且可以在代码内部使用$(this)引用它。这将我们带入下一个问题:jQuery如何知道问题发生在哪个小部件中,从而使您能够更新正确的percentTotal字段?我认为这不是一件简单的事情。这确实不简单。尽管您的代码能够向页面上带有“百分比”类的每个文本框添加一个事件,但如果忽略了发生事件的小部件就是不妥当的。
这个问题被归结为名称空间问题的原因是,小部件的命名不清晰,容易导致问题。要使jQuery代码正常工作,每个名称都必须在其自己的空间之内明确定义,这就出现了术语名称空间。您必须避免编写重名的代码,实现每个小部件都是自含的。您必须能够在相同的页面上添加相同小部件的多个实例,同时避免名称重复。最重要的是,每个小部件在页面上都是独立的。
没有能够处理名称空间问题的现成方法,因此我将展示我的解决办法,您可以在自己的代码中使用我的办法,或通过了解问题创建更好的解决办法。我喜欢该代码的原因是它简单易用(只有1行),并且能够让您在某种程度上控制自己的名称空间。看看清单10。
1. //oureventisattachedtoEVERYinputtextfieldwithaCLASSof"percent"
2. //thismakesourcodelookgood,butcanleadtonamespaceissues
3. $("table.percentSortinput.percent").keyup(function(){
4.
5. //thissimplelinecanestablishanamespaceforus,bygettingtheunique
6. //IDattachedtoourtable(thetableisconsideredthe"container"for
7. //ourwidget.Itcouldbeanything,dependingonyourspecificcode.)
8. //WepasstheCLASSofourwidgettotheparents()function,because
9. //thateffectivelyencapsulatesthewidget
10.
11. varNAMESPACE="#"+$(this).parents("table.percentSort").attr("id");
12.
13. //withthenamespaceestablished,wecanusethejQueryselection
14. //syntaxtousethisnamespaceasourprefixforallofourremaining
15. //searches.Noticehowtheambiguityisremovedforoursearch
16. //byCLASSof"percent"and"percentTotal"
17. //Thissolvesournamespaceissues
18.
19. varsum=$(NAMESPACE+"input.percent").sum();
20. vartotalField=$(NAMESPACE+".percentTotal");
因此,仅需添加一行代码,您就能够封装小部件,从而避免它的函数与自身的其他实例重复(或者甚至是其他碰巧对ID或类使用相同名称的小部件)。这种类型的代码编写方式在插件代码中很常见。编写良好的插件应该考虑到名称空间问题,而糟糕的插件忽略了这个问题。从这个例子中可以看到,在代码中使用名称空间也是很简单的,并且随着页面越来越复杂,它可以给您节省大量时间。鉴于这个原因,我建议您现在就开始考虑jQuery代码中的名称空间问题,并在编写代码时使用这种解决办法。
记住:开始编写jQuery代码时,一般就要开始包含名称空间解决办法。您可以使用以上的解决办法,或创建自己的解决办法。通过采用名称空间解决办法,即使代码越来越复杂,也不会带来名称重复问题。
结束语
本文提供一些技巧,帮助您将良好的jQuery代码转变成强大的jQuery代码。jQuery非常简单易用(并且是独立的JavaScript的巨大改进),因此编写良好的jQuery代码也很容易。大部分开发人员在几分钟之内编写完并运行良好的jQuery代码。但是良好的代码和强大的代码是有区别的。强大的jQuery代码考虑随着页面越来越复杂时的性能问题。强大的jQuery代码能够考虑到页面的未来方向,而不是仅看到当前的位置。强大的jQuery代码是为最复杂的应用程序设计的,然后让应用程序处理输入的简单信息。
本文介绍了5个概念,帮助您将良好的jQuery代码转变成强大的jQuery代码。
第一个概念是使用bind()/unbind()方法。当您不希望在页面的生命周期内将事件添加到代码时,这些方法对向页面元素添加/移除事件非常有用。这些方法在页面包含大量事件时对提升性能非常重要,或者用于某些用户界面中。
第二个概念是使用1.3中包含的新特性live()/die()。这些函数允许将事件变成动态的,就像页面元素一样。随着Web应用程序包含的页面元素越来越多,这些函数允许代码随着页面的增长而增长,这在以前的发布版中是无法实现的。您希望事件处理像页面处理一样具有动态性。
第三个新添加的特性是AjaxQueue/Sync插件,它用于规范和控制对服务器发出的Ajax调用,避免它们超出限度(从客户端角度看)。当Ajax调用的响应返回顺序很重要时,该插件也能帮上大忙。
第四个概念是,尽可能多地用jQuery代码编写页面设置代码。这让HTML的编写更加简单,并且在设置页面时能够获得更多的控制。
最后一个概念是,在代码中利用名称空间解决办法,避免因小部件的函数名称重复而导致问题。每个页面元素和小部件都应该是自含的,不与页面的其他方面发生干扰,名称空间解决办法能够阻止该问题。
这5个步骤并不难实现。事实上,其中4个步骤仅需修改一行代码。不过,理解如何在代码中应用它们才是最重要的。像所有东西一样,如果不能正确时使用,它不仅不能提供帮助,反而还有害处。我的建议是,当您用jQuery代码编写页面时,要尽快应用这5个步骤。每个开发人员都会告诉您,利用特性是编程过程的一部分。您不希望仅因开头设计得不好或更改某些部分而重新设计整个Web应用程序。编写代码时就要抱有编写强大应用程序的想法,并且遵循这些建议。