AJAX:与远程脚本的可用交互

如果您的书签甚至包含一个Web开发博客,那么您无疑会知道远程脚本被吹捧为新的“ Web的未来”。

尽管我感到有些人对此有些过分兴奋,但是最近发布的许多使用远程脚本的高知名度Web应用程序表明,在创建无缝Web时利用这些技术具有明显的优势。应用程序,并增强网页功能。

本文旨在为您介绍远程脚本的基础,特别是新兴的XMLHttpRequest协议。 然后,我们将通过一个示例应用程序演示如何在创建可用接口的同时实现该协议。

首先, 下载代码档案 ,其中包含创建此处提供的工作示例所需的所有文件。

什么是远程脚本?

本质上,远程脚本允许客户端JavaScript从服务器请求数据,而不必刷新Web页面。 而已。 制作无缝Web应用程序所需要进行的其他所有操作都基于已建立的操作文档对象模型的方法。 这可能只是简单地创建一个单词列表(根据Google推荐),就很简单 。 或者,它可能涉及创建用于导航和缩放地图图像的整个界面,例如map.search.ch

但是,除了能够创建新的Web体验之外,远程脚本还使我们能够创建新的不可用的Web体验。 远程脚本和无缝应用程序带来了桌面应用程序设计领域中的许多问题,从而使这些问题在Web上成为可能。 您有责任确保您的远程脚本接口解决这些问题,并为您的用户提供他们所能获得的最佳体验。

远程脚本和可访问性

与任何在线应用程序或网页一样,我们必须始终考虑用户的需求。 某些用户可能具有缺乏JavaScript功能的浏览器,或者即使他们可以执行JavaScript,也可能没有远程脚本功能。

JavaScript交互和远程脚本功能添加到基于Web的内容的基本功能是一种公认​​的最佳实践:没有这些技术,内容仍必须可访问和可用。 对于成熟的Web应用程序,可以为没有JavaScript或远程脚本功能的用户提供完全不同的系统。 GMail团队最近实现了该服务的非JavaScript替代界面。

使用XMLHttpRequest的远程脚本

尽管XMLHttpRequest 不是公共标准,但是大多数现代浏览器始终如一地实现它,并且它已经成为JavaScript数据检索的事实上的标准。 Windows的Internet Explorer 5,Mozilla 1.0,Safari 1.2和即将推出的Opera 8.0版本都将XMLHttpRequest引入为可用对象。

Internet Explorer XMLHttpRequest API可以下载。

您也可以下载Mozilla文档

如果您需要支持早于这些浏览器的浏览器,则使用iframe的方法可以提供可行的解决方案; 但是,为这些浏览器进行编码也会限制您使用标准JavaScript DOM方法的能力。 本文将重点介绍更现代的XMLHttpRequest方法。

创建一个XMLHttpRequest对象

对于除Internet Explorer以外的任何浏览器,我们都可以创建一个XMLHttpRequest对象,如下所示:

var requester = new XMLHttpRequest();

但是,在Internet Explorer中,XMLHttpRequest被实现为ActiveX对象。 对于IE,创建的对象是这样的:

var requester = new ActiveXObject("Microsoft.XMLHTTP");

注意:这也意味着,如果用户在Internet Explorer中禁用了ActiveX对象,则即使启用了JavaScript,他们也将无法使用XMLHttpRequest。

为了解决这些浏览器使用的对象创建语法的差异,最好使用try / catch结构自动为您提供正确的对象,如果XMLHttpRequest对象不可用,则返回错误:

try 
{
 var requester = new XMLHttpRequest();
}
catch (error)
{
 try
 {
   var requester = new ActiveXObject("Microsoft.XMLHTTP");
 }
 catch (error)
 {
   return false;
 }
}

幸运的是,实现之间的区别到此结束,并且无论脚本运行在哪个浏览器上,都可以执行对XMLHttpRequest对象的所有后续方法调用。

使用XMLHttpRequest对象传输数据

创建XMLHttpRequest对象后,我们必须调用两个单独的方法才能使其从服务器检索数据。

 open() initialises the connection we wish to make, and takes two arguments, with several optionals. The first argument is the type of request we want to send; the second argument identifies the location from which we wish to request data. For instance, if we wanted to use a GET request to access feed.xml at the root of our server, we'd initialise the XMLHttpRequest object like this:

requester.open("GET", "/feed.xml");

The URL can be either relative or absolute, but due to cross-domain security concerns, the target must reside on the same domain as the page that requests it.

The open() method also takes an optional third boolean argument that specifies whether the request is made asynchronously ( true , the default) or synchronously ( false ). With a synchronous request, the browser will freeze, disallowing any user interaction, until the object has completed. An asynchronous request occurs in the background, allowing other scripts to run and letting the user continue to access their browser. It's recommended that you use asynchronous requests; otherwise, we run the risk of a user's browser locking up while they wait for a request that went awry. open() 's optional fourth and fifth arguments are a username and password for authentication when accessing a password-protected URL.

Once open() has been used to initialise a connection, the send() method activates the connection and makes the request. send() takes one argument, allowing us to send extra data, such as CGI variables, along with the call. Internet Explorer treats it as optional, but Mozilla will return an error if no value is passed, so it's safest to call it using:

requester.send(null);

To send CGI variables using the GET request method, we have to hardcode the variables into the open() URL:

requester.open("GET", "/query.cgi?name=Bob&email=bob@example.com"); 
requester.send(null);

To send CGI variables using the POST request method, the CGI variables can be passed to the send() method like so:

requester.open("POST", "/query.cgi"); 
requester.send("name=Bob&email=bob@example.com");

Once we've called send() , XMLHttpRequest will contact the server and retrieve the data we requested; however, this process takes an indeterminate amount of time. In order to find out when the object has finished retrieving data, we must use an event listener. In the case of an XMLHttpRequest object, we need to listen for changes in its readyState variable. This variable specifies the status of the object's connection, and can be any of the following:

  • 0未初始化
  • 1正在加载
  • 2已加载
  • 3互动
  • 4已完成

可以使用特殊的onreadystatechange侦听器监视readyState变量中的更改,因此我们需要设置一个函数来更改readyState时的事件:

requester.onreadystatechange = stateHandler;
 readyState increments from 0 to 4, and the onreadystatechange event is triggered for each increment, but we really only want to know when the connection has completed ( 4 ), so our handling function needs to realise this. Upon the connection's completion, we also have to check whether the XMLHttpRequest object successfully retrieved the data, or was given an error code, such as 404: "Page not found". This can be determined from the object's status property, which contains an integer code. "200" denotes a successful completion, but this value can be any of the HTTP codes that servers may return. If the request was not successful, we must specify a course of action for our program:

function stateHandler() 
{
 if (requester.readyState == 4)
 {
 if (requester.status == 200)
 {
   success();
 }
 else
 {
   failure();
 }
}

return true;
}

Even though the XMLHttpRequest object allows us to call the open() method multiple times, each object can really only be used for one call, as the onreadystatechange event doesn't update again once readyState changes to "4" (in Mozilla). Therefore, we have to create a new XMLHttpRequest object every time we want to make a remote call.

Parsing the Data in an XMLHttpRequest Object

If we've made a successful request, two properties of the XMLHttpRequest object may contain data:

  • responseXML存储由对象检索的任何XML数据的DOM结构对象。 使用标准JavaScript DOM访问方法和属性(例如getElementsByTagName()childNodes[ ]parentNode getElementsByTagName()可以导航该对象。
  • responseText将数据存储为一个完整的字符串。 如果服务器提供的数据的内容类型是text / plain或text / html,则这是将包含数据的唯一属性。 任何text / xml数据的副本都将被展平并放置在此处,以替代responseXML

根据数据的复杂性,可能更容易简单地以纯文本字符串形式返回数据,从而使XMLHttpRequest中的XML变得多余。 但是,对于更复杂的数据类型,您可能需要使用XML格式,例如:

<?xml version="1.0" ?>
<user>
<name>John Smith</name>
<email>john@smith.com</email>
</user>

我们能够使用标准的DOM访问方法访问数据的不同部分。 请记住,标记之间包含的数据被认为代表父节点的子文本节点,因此在检索数据时,我们必须考虑到该额外的结构层:

var nameNode = requester.responseXML.getElementsByTagName("name")[0]; 
var nameTextNode = nameNode.childNodes[0];
var name = nameTextNode.nodeValue;

我们还必须注意空格:在XML文件中缩进值可能会在值中产生不必要的空格,或添加其他文本节点。

一旦我们从XMLHttpRequest对象中解析了数据,就可以随意更改,删除并将其写到我们的Web页面上!

示例远程脚本应用程序

为了演示如何在远程脚本应用程序中使用XMLHttpRequest协议,我创建了一个简单的单页示例。 它假定可以使用JavaScript和XMLHttpRequest以便使代码更具可读性,但是在任何实际应用程序中,您应始终检查XMLHttpRequest是否可用,并进行备用(即正常表单提交)。

该示例应用程序将允许用户向朋友的电子邮件地址发送免费的电子贺卡。 为此,用户必须首先输入一个收据号,该收据号是他们在先前购买商品时收到的,并且此后已存储在ExampleCo的数据库中。 然后,用户必须在发送电子贺卡之前完成其余字段,输入收件人的电子邮件地址,消息以及将用于该贺卡的图形图像:

1482_figure1

在此示例中,远程脚本用于以下三个操作:

  • 最小化验证收据编号的等待时间
  • 验证收据编号后,自动将检索到的数据输入表格
  • 电子贺卡发送成功后,重新编写页面内容

除了这些操作外,该示例还包含JavaScript,该JavaScript可在提交之前验证其他表单字段,并允许用户选择电子贺卡图形。

该示例已在两个单独的版本中创建。 这些版本中的第一个演示了在应用程序中XMLHttpRequest协议的实现,但是它包含一些不太理想的可用性问题。 在第二个示例中解决了这些问题,该示例旨在突出显示您从基于页面的应用程序模型转向更具动态性和交互性的环境时可能遇到的一些问题。

示例1:实现XMLHttpRequest

在传统的服务器/客户端应用程序中,必须先将整个电子贺卡表格提交给服务器,进行检查并返回给浏览器,然后才能使客户端知道其收据号是否有效。 使用远程脚本模型,我们可以在用户处理完该字段后立即检查收据编号。 因此,当用户提交表单时,浏览器已经识别出数据是否有效。

远程检查数据的第一步是知道用户何时在收据编号字段中输入了一个值。 可以使用该字段的onchange事件处理程序来检测到。 每当用户修改文本字段的值,然后在该字段上“模糊”(即,他们制表符或单击远离该字段)时,就会在该文本字段上注册“更改”。 通常,这很好地表明用户已完成填写该字段,并且可以处理其中包含的数据。 通过捕获此onchange事件,我们可以告诉脚本开始验证字段的内容:

receipt.onchange = onchangeReceipt;
onchangeReceipt
var requester = null;  
 
function onchangeReceipt()  
{  
 /* Check for running connections */  
 if (requester != null && requester.readyState != 0 && requester.readyState != 4)  
 {  
   requester.abort();  
 }  
 
 try  
 {  
   requester = new XMLHttpRequest();  
 }  
 catch (error)  
 {  
   try  
   {  
     requester = new ActiveXObject("Microsoft.XMLHTTP");  
   }  
   catch (error)  
   {  
     requester = null;  
 
     return false;  
   }  
 }  
 
 requester.onreadystatechange = requesterExecuteAction;  
 
 requester.open("GET", "receipt.php?receipt=" + this.value);  
 requester.send(null);  
 
 return true;  
}

You might recognise some of that syntax from the first part of this article, namely the forked try/catch structure, and the open() and send() methods that control the XMLHttpRequest object.

The first if statement checks to see whether or not an XMLHttpRequest object already exists and is currently running; if so, it aborts that connection. This ensures that a number of conflicting XMLHttpRequest calls aren't run simultaneously, which would clog up the network. The function then continues on, to create a new XMLHttpRequest object and open a connection to the server-side validation script, receipt.php.

In receipt.php, the CGI variable receipt is checked and, if its value is "1234567" , some XML data is returned; otherwise, a plain text string of "empty" is returned, indicating that the receipt number is invalid:

if ($receipt == "1234567")  
{  
 header("Content-type: text/xml");  
 
 $filePointer = fopen("example.xml", "r");  
 $exampleXML = fread($filePointer, filesize("example.xml"));  
 fclose($filePointer);  
 
 print($exampleXML);  
}  
else  
{  
 header("Content-type: text/plain");  
 print("empty");  
}

Hard-coded values and data have been used in this example to simplify the code, but in the real world, this PHP script would check the receipt number against a database, and return the appropriate data for that number.

Note that if receipt number is invalid, the content-type header sent is "text/plain" . This simplifies the message printing process somewhat, but it also means that, on the client side, the responseXML property of the XMLHttpRequest object will not contain anything. As such, you should always be aware of what your server-side scripts return, and keep an eye on responseXML or responseText appropriately.

As well as calling the server-side script, onchangeReceipt() also assigns onreadystatechangeReceipt() to monitor the status of the connection via the onreadystatechange event, and it is this function that determines when the connection is finished and further action should be taken. To do this, we use the previously discussed readyState / status condition nesting:

function onreadystatechangeReceipt()  
{  
 /* If XMLHR object has finished retrieving the data */  
 if (requester.readyState == 4)  
 {  
   /* If the data was retrieved successfully */  
   if (requester.status == 200)  
   {  
     writeDetails();  
   }  
   /* IE returns a status code of 0 on some occasions, so ignore this case */  
   else if (requester.status != 0)  
   {  
     alert("There was an error while retrieving the URL: " + requester.statusText);  
   }  
 }  
 
 return true;  
}

When a successful status code is returned, writeDetails() is invoked. It is this function that parses the returned data and determines what to do to the Web page:

function writeDetails()  
{  
 var receipt = document.getElementById("receipt");  
 
 if (requester.responseText.charAt(0) == "<")  
 {  
   var email = document.getElementById("email");  
   var name = document.getElementById("name");  
 
   receipt.valid = true;  
   email.value = requester.responseXML.getElementsByTagName("email")[0].  
childNodes[0].nodeValue;  
 }  
 else  
 {  
   receipt.valid = false;  
 }  
 
 return true;  
}

This function firstly checks the responseText property of the XMLHttpRequest object, to see whether the receipt number was valid or not. If it is valid, the data will be in XML format and its first character will be an opening angled bracket ( < ); otherwise, it will be a plain string. In each case, the extended property valid is set appropriately on the receipt number field. Additionally, if the receipt number is valid, extra data is added to the email field, having been parsed from the responseXML property of the XMLHttpRequest object.

The execution of writeDetails() marks the end of the remote scripting process for receipt number validation. With the extended valid property set on the field, the browser knows whether or not the data is OK, and can alert users of any errors when they try to submit the form:

orderForm.onsubmit = checkForm;  
 
function checkForm()  
{  
if (!receipt.valid)  
{  
 receipt.focus();  
 alert("Please enter a valid receipt number.");  
 
 return false;  
}  
 
...

If there is an error with the form, an alert() dialog appears when the submit button is clicked, asking the user to correct the error before the form is submitted:

1482_figure2

 checkForm() also handles the submission of the form data via remote scripting (though, in reality, normal form submission would probably suffice for an application like this). The remote scripting for the data submission uses the same code we used for validation, but a different server-side script is supplied to process the data, and instead of onreadystatechangeReceipt() being called once the connection has finished, onreadystatechangeForm() is called.

 onreadystatechangeForm() triggers sentForm() to re-write the Web page and inform the user that the ecard was either successfully or unsuccessfully sent, depending upon the data returned from the server:

function sentForm()  
{  
 var body = document.getElementsByTagName("body")[0];  
 
 body.innerHTML = "<h1>Send someone an e-card from ExampleCo!</h1>";  
 
 if (formRequester.responseText == "success")  
 {  
   body.innerHTML += "<h1>Send someone an e-card from ExampleCo!</h1><p>Your ExampleCo e-card has been sent!</p>";  
 }  
 else  
 {  
   body.innerHTML += "<p>There was an error while sending your ExampleCo e-card.</p>";  
 }  
 
 return true;  
}

This removes the initial form presented to the user, and inserts a final status message:

1482_figure3

While this application re-writes almost the whole page, it's easy to see how specific parts of the DOM could be changed using remote scripting, which would enable separate parts of an application interface to update independently of the Web page itself.

Example 2: Create a Usable Remote Scripting Interface

The remote scripting model is quite different from the standard page-based interaction that permeates most of the Web, and with that difference comes new usability pitfalls that can too easily be introduced into your projects. These pitfalls typically arise either from the dynamic manipulation of the interface while the user is accessing it, or from the need to access data that's external to the Web page.

Example 1 used remote scripting to validate the receipt number, and to automatically insert data that was retrieved from the database; however, none of this information was used particularly well, nor was it obvious to the user what was going on. Example 2 aims to correct this and other deficiencies in the first example, and make the experience a whole lot quicker, easier and more understandable for the user. The five tips below explain some of the changes that can be used to turn a bad experience into a good one.

Tip #1: Tell Users Why they're Waiting

Remote scripting is not instantaneous. Regardless of the speed of your Web connection, communication time with an external source will vary. So, while communication with a server occurs, it's imperative that you tell the user why they're waiting. (The example PHP scripts use sleep() calls to highlight the waiting periods that can be caused by network traffic or other factors.)

Because remote scripting applications do not make calls using the normal browser interface, the status bar -- which normally notifies the user of transfer status and activity -- does not function as it normally does, Thus, we have to provide feedback to the user ourselves.

In example 2, while the receipt number is being verified, a label displays next to the receipt number field to explain the wait.

1482_figure4

The label changes to indicate completion once the XMLHttpRequest connection has finished.

1482_figure5

The status message is initialised just before the XMLHttpRequest connection, when the onchange event for the receipt number field is triggered:

receipt.onchange = onchangeReceipt;   
 
function onchangeReceipt()  
{  
 message(this, "loadingMessage", "Verifying receipt number");  
 
 /* Check for running connections */  
 if (requester != null && requester.readyState != 0 && requester.readyState != 4)  
 {  
   requester.abort();  
 }  
 
...

Once the remote scripting operation has finished, the message is updated to tell the user whether the receipt number was valid or not:

function writeDetails()   
{  
 if (requester.responseText.charAt(0) == "<")  
 {  
   message(receipt, "statusMessage", "Your receipt details were retrieved");  
...  
 
 else  
 {  
   message(receipt, "errorMessage", "Please enter a valid receipt number");  
...

Updating the message to indicate completion is important, as it provides closure for the user. If the loading message simply disappeared, users could not be certain that it had been successful.

In the two code samples above, the message function is a custom function that dynamically creates a status label for a form element, and positions it visually adjacent to the related element. It also accepts a class for the status label, which allows CSS styles to be applied differently for loading, error and completion messages:

function message(element, classString, errorMessage)   
{  
 var messageDiv = document.createElement("div");  
 
 element.parentNode.insertBefore(messageDiv, element);  
 messageDiv.className = classString;  
 messageDiv.appendChild(document.createTextNode(errorMessage));  
 
 return true;  
}

While the XMLHttpRequest process is running, the label animates to indicate that the action is ongoing and still alive. In example 2, this is performed via CSS styling with an animated GIF, but it could also be effected using JavaScript animation.

The same feature is applied to the form submission button. Again, this alerts the user that some action is being undertaken, and also lets them know that they did click the button, which will help to discourage users from pressing the button more than once:

1482_figure6

To achieve this, simply change the value and the CSS class of the submit button:

submit.className = "submit loading";   
submit.value = "Contacting server";

Tip #2: Don't Interfere with the User's Interaction

Users become frustrated with interfaces that interfere with the completion of their task. In example 1, such interference might occur after users have entered a receipt number: if they begin to fill in their names and email addresses before the receipt number has been verified, those details will be overwritten once their user data is received from the server.

To rectify this, example 2 checks whether a user has changed the values of the text fields before the script enters any data into them. The default values of the text fields can be detected when the page loads, and recorded using custom DOM properties:

email.defaultValue = email.value;

The default value of a field can then be checked against its current contents before the script attempts to write any data into it:

if (email.value == email.defaultValue)   
{  
 email.value = newValue;  
}

This makes sure that the user -- who probably knows his or her own name better than we do -- doesn't have any entries overwritten by over-zealous automation.

Some other common cases of interference that you should avoid include moving the cursor to a field while the user is filling out another field, and locking the user out of the interface (which is why XMLHttpRequest should be used asynchronously).

Tip #3: Catch Errors Early, but not Too Early

It's best to catch errors as soon as they occur. Many forms that currently appear on the Web rely upon the user to submit the form before any form errors will be shown, either using server-side scripts or inelegant JavaScript alerts (as per example 1). These methods have several disadvantages for the user:

  • 提交表单的过程占用了用户的时间。
  • JavaScript警报不会永久标记所有需要更正的字段。
  • 在错误已完全落实之后立即进行指示,要求用户在脑海中重新收集错误字段对错误的要求。
  • 即使用户知道要纠正的表单元素,他们也必须重新提交表单以查明这些元素是否已正确纠正。

出于这些原因,最好将错误通知用户。 在示例2中,如果用户输入了无效的电子邮件地址,则应用程序会立即告诉他们。 使用提示#1中的message()函数,将通知放置在电子邮件字段的旁边:

1482_notification

但是,您不应该在用户开始输入内容后立即检查其有效性,因为这样做会分散您的注意力,更不用说烦人了,因为这样会告诉您在输入数据之前就已经犯了一个错误。 仅当用户完成输入后,即离开输入时,才应进行字段检查。 对于文本字段,最好使用onchange事件捕获这种类型的操作:

email.onchange = onchangeEmail;

然后,由事件触发的函数可以检查该字段并确保其中包含的数据对该数据类型有效:

function onchangeEmail()   
{  
 if (!this.value.match(/^[w.-]+@([w-]+.)+[a-zA-Z]+$/))  
 {  
   field.valid = false;  
   message(field, "errorMessage", "Please enter a valid e-mail address");  
   field.className = "text error";  
 }  
 
 return true;  
}

提示4:修正错误时让使用者知道

一旦发现一个字段不正确,并且已向用户发出错误提示,让用户知道他或她将其更改为正确的时间也同样重要,否则用户将被困在表单提交周期中再来一次。

在这种情况下,等待浏览器的onchange事件触发是不够的,因为通常仅当用户使表单元素散焦时才会发生。 因此,最好使用onkeyup事件来检查已知不正确的字段的正确性:

email.onkeyup = onkeyupEmail;

在继续检查该字段是否正确之前, onkeyupEmail()函数检查电子邮件字段旁边是否显示错误消息。 因此,一旦用户对该字段进行了适当的更正,该错误消息就会消失; 但是,如果用户是第一次输入该字段,则不会出现任何消​​息:

function onkeyupEmail()   
{  
 /* If an error message is displayed */  
 if (this.message != null && this.message.className == "errorMessage")  
 {  
   if (this.value.match(/^[w.-]+@([w-]+.)+[a-zA-Z]+$/))  
   {  
     this.valid = true;  
 
     /* Remove error message */  
message(this);  
 
/* Remove error CSS class */  
     this.className = "text";  
   }  
...

这些情况不会捕获跳过了必填字段的情况,因此,最好允许用户提交不完整的表单,因为这可以使程序准确地突出显示需要完成的内容,而不是搜索详细信息尚未填写。

提示5:提供界面反馈

创建无缝的Web应用程序可以让您探索浏览器中尚未发现的新功能,但是在这样做时,我们仍然必须记住可用界面设计的基础。 这样的基础之一就是提供界面反馈:让用户知道他们可以做什么,以及他们做了什么。

在示例1中,尚不清楚用户是否可以单击电子贺卡图形的缩略图。 如果我们在任何给定时间为光标所处的图像提供灰色轮廓,这很容易抵消。

1482_figure7

:hover伪类对于使用CSS的任何人都是熟悉的。 当光标移到该对象上时,它允许对象更改其外观。 尽管从理论上讲,仅通过CSS即可实现鼠标悬停效果,但当前版本的Internet Explorer不允许:hover对除锚标记之外的任何元素产生效果。 因此,为了在图像元素上实现悬停效果,示例2附加了onmouseoveronmouseout事件处理程序:

var cards = document.getElementById("ecardSet").   
getElementsByTagName("img");  
 
for (var i = 0; i < cards.length; i++)  
{  
 cards[i].onmouseover = onmouseoverCard;  
 cards[i].onmouseout = onmouseoutCard;  
}

然后,这些事件处理程序可以更改每个图像的类,并允许我们使用CSS提供视觉反馈:

function onmouseoverCard()   
{  
 this.className = "hover";  
 
 return true;  
}  
 
function onmouseoutCard()  
{  
 this.className = "";  
 
 return true;  
}

更改光标以指示其“可点击性”还可以帮助向用户提供反馈。 可以使用CSS中的一条简单规则完成此操作:

img.hover   
{  
 cursor: pointer;  
}
结论

在对示例1进行所有这些更改之后,示例2成为一个更加有用和可用的应用程序。

1482_figure8

此处提供的提示中的共同主题始终是使用户感到舒适和掌控。 如果用户不具备他们所需要的信息以了解正在发生的事情,他们将不安地查看您的应用程序,结果将影响其性能。

尽管本文主要侧重于远程脚本编写的过程及其可用性问题,但是在创建无缝的Web应用程序时,还应考虑可访问性。 示例3是ecard应用程序的更为复杂的版本,它使用功能更强大的脚本,对于没有JavaScript或没有XMLHttpRequest的用户,可访问性降低。 掌握了上述技术后,您可能希望看一下最后一个示例,并开始使您的应用程序真正可靠。

From: https://www.sitepoint.com/remote-scripting-ajax/

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值