Ajax对象 上面提到的共通方法非常好,但是面对它吧,它们不是最高级的那类东西。它们是吗?你很可能自己编写了这些甚至在你的脚本里面有类似功能的方法。但是这些方法只是冰山一角。 我很肯定你对prototype.js感兴趣的原因很可能是由于它的AJAX能力。所以让我们解释当你需要完成AJAX逻辑的时候,这个包如何让它更容易。 Ajax 对象是一个预定义对象,由这个包创建,为了封装和简化编写AJAX 功能涉及的狡猾的代码。 这个对象包含一系列的封装AJAX逻辑的类。我们来看看其中几个类。 使用Ajax.Request类 如果你不使用任何的帮助程序包,你很可能编写了整个大量的代码来创建XMLHttpRequest对象并且异步的跟踪它的进程, 然后解析出响应 然后处理它。当你不需要支持多于一种类型的浏览器时你会感到非常的幸运。 为了支持 AJAX 功能。这个包定义了 Ajax.Request 类。 假如你有一个应用程序可以通过url http://yoursever/app/get_sales?empID=1234&year=1998与服务器通信。它返回下面这样的XML 响应。 <?xml version="1.0" encoding="utf-8" ?> <ajax-response> <response type="object" id="productDetails"> <monthly-sales> <employee-sales> <employee-id>1234</employee-id> <year-month>1998-01</year-month> <sales>$8,115.36</sales> </employee-sales> <employee-sales> <employee-id>1234</employee-id> <year-month>1998-02</year-month> <sales>$11,147.51</sales> </employee-sales> </monthly-sales> </response> </ajax-response> 用 Ajax.Request对象和服务器通信并且得到这段XML是非常简单的。下面的例子演示了它是如何完成的。 <script> function searchSales() { var empID = $F('lstEmployees'); var y = $F('lstYears'); var url = 'http://yoursever/app/get_sales'; var pars = 'empID=' + empID + '&year=' + y; var myAjax = new Ajax.Request( url, { method: 'get', parameters: pars, onComplete: showResponse }); } function showResponse(originalRequest) { //put returned XML in the textarea $('result').value = originalRequest.responseText; } </script> <select id="lstEmployees" size="10" onchange="searchSales()"> <option value="5">Buchanan, Steven</option> <option value="8">Callahan, Laura</option> <option value="1">Davolio, Nancy</option> </select> <select id="lstYears" size="3" onchange="searchSales()"> <option selected="selected" value="1996">1996</option> <option value="1997">1997</option> <option value="1998">1998</option> </select> <br><textarea id=result cols=60 rows=10 ></textarea> 你注意到传入 Ajax.Request构造方法的第二个对象了吗? 参数{method: 'get', parameters: pars, onComplete: showResponse} 表示一个匿名对象的真实写法。他表示你传入的这个对象有一个名为 method 值为 'get'的属性,另一个属性名为 parameters 包含HTTP请求的查询字符串,和一个onComplete 属性/方法包含函数showResponse。 还有一些其它的属性可以在这个对象里面定义和设置,如 asynchronous,可以为true 或 false 来决定AJAX对服务器的调用是否是异步的(默认值是 true)。 这个参数定义AJAX调用的选项。在我们的例子中,在第一个参数通过HTTP GET命令请求那个url,传入了变量 pars包含的查询字符串, Ajax.Request 对象在它完成接收响应的时候将调用showResponse 方法。 也许你知道, XMLHttpRequest在HTTP请求期间将报告进度情况。这个进度被描述为四个不同阶段:Loading, Loaded, Interactive, 或 Complete。你可以使 Ajax.Request 对象在任何阶段调用自定义方法 ,Complete 是最常用的一个。想调用自定义的方法只需要简单的在请求的选项参数中的名为 onXXXXX 属性/方法中提供自定义的方法对象。 就像我们例子中的 onComplete 。你传入的方法将会被用一个参数调用,这个参数是 XMLHttpRequest 对象自己。你将会用这个对象去得到返回的数据并且或许检查包含有在这次调用中的HTTP结果代码的 status 属性。 还有另外两个有用的选项用来处理结果。我们可以在onSuccess 选项处传入一个方法,当AJAX无误的执行完后调用, 相反的,也可以在onFailure选项处传入一个方法,当服务器端出现错误时调用。正如onXXXXX 选项传入的方法一样,这两个在被调用的时候也传入一个带有AJAX请求的XMLHttpRequest对象。 我们的例子没有用任何有趣的方式处理这个 XML响应, 我们只是把这段XML放进了一个文本域里面。对这个响应的一个典型的应用很可能就是找到其中的想要的信息,然后更新页面中的某些元素, 或者甚至可能做某些XSLT转换而在页面中产生一些HTML。 在1.4.0版本中,一种新的事件回传外理被引入。如果你有一段代码总是要为一个特殊的事件执行,而不管是哪个AJAX调用引发它,那么你可以使用新的Ajax.Responders对象。 假设你想要在一个AJAX调用正在运行时,显示一些提示效果,像一个不断转动的图标之类的,你可以使用两个全局事件Handler来做到,其中一个在第一个调用开始时显示图标,另一个在最后一个调用完成时隐藏图标。看下面的例子。 <script> var myGlobalHandlers = { onCreate: function(){ Element.show('systemWorking'); }, onComplete: function() { if(Ajax.activeRequestCount == 0){ Element.hide('systemWorking'); } } }; Ajax.Responders.register(myGlobalHandlers); </script> <div id='systemWorking'><img src='spinner.gif'>Loading...</div> 更完全的解释,请参照 Ajax.Request 参考 和 Ajax选项参考。 使用Ajax.Updater类 如果你的服务器的另一端返回的信息已经是HTML了,那么使用这个程序包中 Ajax.Updater 类将使你的生活变得更加得容易。用它你只需提供哪一个元素需要被AJAX请求返回的HTML填充就可以了,例子比我写说明的更清楚。 <script> function getHTML() { var url = 'http://yourserver/app/getSomeHTML'; var pars = 'someParameter=ABC'; var myAjax = new Ajax.Updater( 'placeholder', url, { method: 'get', parameters: pars }); } </script> <input type=button value=GetHtml onclick="getHTML()"> <div id="placeholder"></div> 你可以看到,这段代码比前面的例子更加简洁,不包括 onComplete 方法,但是在构造方法中传入了一个元素id。 我们来稍稍修改一下代码来描述如何在客户端处理服务器段错误成为可能。 我们将加入更多的选项, 指定处理错误的一个方法。这个是用 onFailure 选项来完成的。我们也指定了一个 placeholder 只有在成功请求之后才会被填充。为了完成这个目的我们修改了第一个参数从一个简单的元素id到一个带有两个属性的对象, success (一切OK的时候被用到) 和 failure (有地方出问题的时候被用到) 在下面的例子中没有用到failure属性,而仅仅在 onFailure 处使用了 reportError 方法。 <script> function getHTML() { var url = 'http://yourserver/app/getSomeHTML'; var pars = 'someParameter=ABC'; var myAjax = new Ajax.Updater( {success: 'placeholder'}, url, { method: 'get', parameters: pars, onFailure: reportError }); } function reportError(request) { alert('Sorry. There was an error.'); } </script> <input type=button value=GetHtml onclick="getHTML()"> <div id="placeholder"></div> 如果你的服务器逻辑是连同HTML 标记返回JavaScript 代码, Ajax.Updater对象可以执行那段JavaScript代码。为了使这个对象对待响应为JavaScript,你只需在最后参数的对象构造方法中简单加入evalScripts: true属性。但是值得提醒的是,像这个选项名evalScripts暗示的,这些脚本会被执行,但是它们不会被加入到Page的脚本中。“有什么区别?”,可能你会这样问。我们假定请求地址返回的东东像这样: <script language="javascript" type="text/javascript"> function sayHi(){ alert('Hi'); } </script> <input type=button value="Click Me" onclick="sayHi()"> 如果你以前这样尝试过,你知道这些脚本不会如你所期望的那样工作,原因是这段脚本会被执行,但像上面这样的脚本执行并不会创建一个名叫sayHi的函数,它什么也不做。如果要创建一个函数,我们应当把代码改成下面这个样子: <script language="javascript" type="text/javascript"> sayHi = function(){ alert('Hi'); }; </script> <input type=button value="Click Me" onclick="sayHi()"> 为什么我们在上面的代码中不使用var关键字来声明这个变量呢(指sayHi ),因为那样做创建出来的函数将只是当前脚本块的一个局部变量(至少在IE中是这样)。不写var关键字,创建出来的对象的作用域就是我们所期望的window。 更多相关知识,请参看 Ajax.Updater reference 和options reference. 枚举... 噢!噢! 你知道,我们都是这样来做循环的,建一个Array,用elements组织它们,再建一个循环结构(例如for,foreach,while)通过index数字来访问每一个element,再用这个element做一些动作。 当你想到这时,你会发现几乎每次写循环代码你都会迟早用到一个Array。那么,如果Array对象能够提供更多的功能给它们的迭代器使用不是很爽吗?确实是这样,事实上很多的编程语言都在它们的Array或其它类似的结构中(如Collections,Lists)提供一些这样的功能。 现在好了,prototype.js了给我们一个 Enumerable对象,它实现了很多和可迭代数据进行交互的窍门。和原有的JS对象相比prototype.js更上一层楼,它对Array 类s扩展了所有枚举要用的函数。 循环, Ruby样式的 在标准的javascript中,如果你想把一个array中的所有elements显示出来,你可以像下面代码这样写得很好: <script> function showList(){ var simpsons = ['Homer', 'Marge', 'Lisa', 'Bart', 'Meg']; for(i=0;i<simpsons.length;i++){ alert(simpsons[i]); } } </script> <input type="button" value="Show List" onclick="showList();" > 使用我们新的最好的朋友,prototype.js,我们可以把它生写成这样 function showList(){ var simpsons = ['Homer', 'Marge', 'Lisa', 'Bart', 'Meg']; simpsons.each( function(familyMember){ alert(familyMember); }); } 你可能会想“非常奇怪的方式...相对旧的,这种语法太怪异了”。哦,在上面的例子,确实什么也没有,在这个简单得要死例子中,也没有改变太多啊,尽管如此,请继续读下去。 在继续下面内容之前,你注意到那个被做为一个参数传递给each函数的函数?我们把它理解成迭代器函数。 Your arrays on steroids 就如我们上面提到的,把你的Array中的elements当成相同的类型使用相同的属性和函数是很通用(Common,不知该翻译成通用还是庸俗)的。让我们看看怎么样利用我们新的马力强劲的Arrays的迭代功能吧。 依照标准找到一个element。 <script> function findEmployeeById(emp_id){ var listBox = $('lstEmployees') var options = listBox.getElementsByTagName('option'); options = $A(options); var opt = options.find( function(employee){ return (employee.value == emp_id); }); alert(opt.innerHTML); //displays the employee name } </script> <select id="lstEmployees" size="10" > <option value="5">Buchanan, Steven</option> <option value="8">Callahan, Laura</option> <option value="1">Davolio, Nancy</option> </select> <input type="button" value="Find Laura" onclick="findEmployeeById(8);" > 现在我们再下一城,看看如何过滤一个Array中的元素,从每个元素中得到我们想要的成员。 <script> function showLocalLinks(paragraph){ paragraph = $(paragraph); var links = $A(paragraph.getElementsByTagName('a')); //find links that do not start with 'http' var localLinks = links.findAll( function(link){ var start = link.href.substring(0,4); return start !='http'; }); //now the link texts var texts = localLinks.pluck('innerHTML'); //get them in a single string var result = texts.inspect(); alert(result); } </script> <p id="someText"> This <a href="http://othersite.com/page.html">text</a> has a <a href="#localAnchor">lot</a> of <a href="#otherAnchor">links</a>. Some are <a href="http://wherever.com/page.html">external</a> and some are <a href="#someAnchor">local</a> </p> <input type=button value="Find Local Links" onclick="showLocalLinks('someText')"> 上面的代码仅仅是一点小小的实践让人爱上这种语法。请参看 Enumerable和Array的所有函数