Ajax 网页异步调用

 Ajax的jquery的api和例子参考:http://api.jquery.com/jQuery.ajax/

15.1. Scripting Languages

So far, pretty much everything we have done has been server-based. However, client-based code - client-side scripting is an alternative that can be used instead of, or (more commonly) as well as server side code.

There are a variety of scripting languages that we can use - however, we will concentrate on JavaScript. JavaScript has, confusingly, nothing much to do with Java - though it has a vaguely similar syntax, and some OOP features. Commonly, scripting languages are based on currently-popular programming languages (there are several C-like ones for example, including the one used for PHP, and since the base syntax of Java is also somewhat C-like, you could argue that JavaScript was one of these).

The first confusion to clear up is the difference between JavaScript and JScript. JavaScript is Netscape's original version - Microsoft came along with JScript, which is very similar but with some differences. This means that you have to be very careful about writing scripts that are not dependent on particular browsers and many people do not bother. We will see some examples of the issues involved later in this chapter.

To help resolve the confusion, the European Computer Manufacturers Association (ECMA) has come up with a standard that tries to unify both languages - called ECMA Script - hardly a catchy name, and one you hardly ever hear in practice (pretty much everyone calls all variants JavaScript, regardless of what they 'really' are). In the opinion of some, it is difficult to write really useful code in the ECMA Script subset.

15.1.1. A Basic Script

At its simplest, we can just insert scripts within our HTML pages surrounded by '<script>' - '</script>' tags. For example, the following automatically resizes a picture to fit a user's browser window width.


<SCRIPT LANGUAGE="JavaScript">
if( typeof( window.innerWidth ) == 'number' ) {
    available_width = window.innerWidth;
  } else if( document.documentElement &&
      ( document.documentElement.clientWidth || document.documentElement.clientHeight ) ) {
    available_width = document.documentElement.clientWidth;
  } else if( document.body && ( document.body.clientWidth || document.body.clientHeight ) ) {
    available_width = document.body.clientWidth;
  }
var image1_width = (available_width - image_edge);
var image1_height = Math.round(image1_width / 5) ;

var image1 = '<img src="bay.jpg" width="' + image1_width + '">';
var navDiv = '<div class="mainNav" id="mainNav" style="top:' + image1_height + '">';


document.write(image1); 
document.write(navDiv);
</script>

<noscript>
    <img src="bay.jpg" width="980" align="left" border="0" />
    <div class="mainNav" id="mainNav" style="top: 200">

</noscript>

This code does the following. First, it works out the available window width. This is, unfortunately, browser dependent: some use a property called window.innerWidth; some use document.clientWidth; and some use document.body.clientWidth (notice the somewhat object-oriented notation). Consequently, we have to use a hideous conditional to work out which, and set the variable available_width. This kind of irritation is very common if you want to get your code to work on at least the most common browsers - a lot of people don't bother of course.

Once we know the width available, we can set the image width and height (actually, the height plus a bit) - image_edge is a constant defined elsewhere - and use these to set two strings: one representing the image with the correct width, and one the left-and navigation, with it's top position set to not overlap the image. Then we add these to the image (document.write). Notice that we also have a `noscript' block, for users who turn scripting off. Notice also that we can access elements of our page in an object-oriented style. There is a 'document' object that has various methods (e.g. 'write') that allows it to be queried and manipulated.

As an aside, you will commonly see advice to write scripting blocks like this:


<script language="JavaScript">
<!-- comment out script for old browsers
...
// finish commenting for old browsers -->
</script>

Old browsers will not recognize the script tag and so will just render the script on the page, usually looking awful. The HTML comment hides it from such browsers, but is ignored on those that understand the script tag. Notice, in fact, that the closing HTML comment is in a JavaScript comment. Since a browser has to be very old to not run Javascript, it is perhaps debatable if there is now much point in this and I've tended not to bother - the practice is dying out. (However, after reading this alarming story about people using browers back to IE 3 perhaps this is not such a good thing.)

The less than exciting example above just runs when the HTML is interpreted. This example, however, does not show what we can really do because (i) JavaScript is event driven, and can respond to a variety of external events (e.g mouse clicks etc.); and (ii) we can associate JavaScript with forms (see the CGI chapter). Here, for example, is a simple (and somewhat pointless) example that uses form buttons to change the background colour.


    <html>
    <head>
        <title>Colour Change</title>
        <script language=JavaScript>
        function Red(){
            document.bgColor="#ff0000";
        }
        function Green(){
            document.bgColor="#00ff00";
        }
        function Blue(){
            document.bgColor="#0000ff";
        }
        </script>
    </head>
    <body>

    <form>
        <input type="button" value="Red"
            onclick="Red()">
        <input type="button" value="Green"
            onclick="Green()">
        <input type="button" value="Blue"
            onclick="Blue()">
    </form>
    </body>
    </html>

You can see what this does here. Rather than write some code that is run in-line, we have defined three functions Red, Green and Blue. We have actually defined these function in the 'head' section of the page, but we could have put them in the body section. These functions do not run until we call them. To do that, we set the 'onclick' attribute of each button to call the appropriate function. (It would actually be easier in this case to define a single function that took a parameter, but as an illustrative example I think this is clearer. As an exercise, you might want to try to change it.)

Here is another example from the image sizing script above: what if the user resizes the page? Can we get the image to resize as well? Yes:


    <SCRIPT LANGUAGE="JavaScript">
function winResize() {
    history.go(0);
}
...
<body onresize=winResize()>

We've omitted some irrelevant stuff. In this case, we have linked the onresize event to a function that calls the go method of the history object: history.go(0) reloads the current page (from the cache generally, so it's not too slow). Obviously, you could call, say history.go(1) to go back one page - all under program control.

Here is another example - a simple calculator. As calculators go, it's not very good (and I'm afraid it renders poorly on some browers - sorry). In particular, it doesn't do much in the way of error checking. You can view the source - which is heavily commented - within your browser. Study it and see if you can understand what is going on. Also, try the exercises described in the source. (Incidently, calculators are fairly common, because they're such an obvious example - in particular, they are independent of any data that must be held on a server - which was historically the tricky bit with JavaScript until AJAX came along. If you look around, you can find much better examples - though the code is inevitably more complex and harder to follow.)

15.1.2. Security Aside

You may remember our earlier discussion on security - and in particular Cross Site Scripting. The issue with JavaScript is that it's a programmable environment running within your browser with access to at least some (potentially) sensitive data - including cookies and (for some browers at least) passwords. Once you've actually ended up with a malicious piece of JavaScript in your browser, written by someone who knows what they're doing

As an example, try typing alert("hi") into the calculator and hitting equals. OK, not very exciting - but you can do more. For example, show cookies, or even passwords. Of course, you aren't really at risk if you have to actually type this stuff in - but you can just as easily embed this code on a page and persuade users to click on links to it. Once you've got the information - cookies, passwords, whatever - you can use scripted email, or AJAX (see below) to send it wherever you want.

15.1.3. Verifying Form Data

In principle, we can do any kind of computation we wish with JavaScript. However, in practice, for serious applications we need access to remote data - usually a database of some kind. This used to be possible, but not particularly easy: AJAX now makes it much easier with tool support. However, historically, the most usual use for scripting languages is to make a page look good - for example, buttons that respond when the mouse is moved over them are quite easy to create. This comes under the heading of 'making things look pretty', which we are not concerned with here. However, here's a very basic and silly example - the picture(s) are a big button if that's not obvious. Apologies to anyone from California. (Since writing that page, I have found a 'crisis phone' in Wales, and lots of 'no firearms'-style signs - try looking on the back of the National Trust signs all over Gower.) Again, you can view the source to see how it works. (Hint the picture(s) are actually a button you can click.)

The examples above are mostly concerned with appearance. We can, perhaps more usefully, use JavaScript (or any other scripting language) to perform checks on form data before it is submitted - effectively meaning we are splitting the workload between client and server, to a limited extent. Typically, the first thing that needs to be done to form data is to verify it is 'reasonable'. This can be as simple as checking certain required fields are filled in, or it could be more complex. If this can be done before the form is submitted, it will help in two ways:

Percieved performance improves. The user is not faced with a long delay before being told that there was an error in the input.
Actual performance improves. By preventing trivial errors from going to the server, we eliminate a considerable amount of time and effort in setting up a communications transaction, even if the actual processing that would be done on the server to discover the error is trivial.
We cannot always put all error checking on the client - for example, checking that a postcode and address actually correspond is a major task, requiring access to a large(ish) database. (However, checking that a postcode is syntactically legal could be done with client-side scripting.) Also, it would be foolish to, for instance, verify a password this way - since it would be trivially easy for anyone to find out the actual password by simply reading the source text of the page.

Here's a basic (made up) example. The form below (which might be one at the end of a sequence of forms) asks you to either choose for some [unspecified] goods to be delivered (in which case you must enter an address), or to say you will collect them (in which case you must specify a date). Note that this is unrealistically basic (for example no attempt is made to check the format of the date, and there are issues if you hit return rather than clicking the submit button), but it does illustrate the point.


<head>
<title>Delivery or Collect?</title>

    <script language=JavaScript>

        function verify() {
            if (document.UserForm.DelCol.value ==
                 "Delivery") {
                delivery();
            } else {
                collect();}
            }

        function collect() {
                if (document.UserForm.date.value == "") {
                    window.alert("Specify a date.");
                } else {
                    document.SubForm.DelorCol.value=
                        "Collection";
                    document.SubForm.AddrDate.value=
                        document.UserForm.date.value;
                    SubForm.submit();}
                }

        function delivery() {
                if (document.UserForm.address.value ==
                      "") {
                    window.alert("Specify an address.");
                } else {
                    document.SubForm.DelorCol.value=
                         "Delivery";
                    document.SubForm.AddrDate.value=
                        document.UserForm.address.value;
                    SubForm.submit();}
                }
    </script>
</head>

<body>
<form name="UserForm">
    <p>You can choose for your goods to be delivered,
    or you can collect them.<br/>
    If you choose delivery,
    you <i>must</i> specify a delivery address.<br/>
    If you wish to collect them, you <i>must</i> give us
    a collection date.</p>
    <select name="DelCol" size="1">
             <option value="Delivery">Delivery</option>
             <option value="Collect">Collect</option>
          </select>
    <p>Delivery Address:<br/>
    <textarea name="address" rows="7" cols="50">
    </textarea></p>
    <p>Collection Date: <input name="date" type="text"
        maxlength="20" size="10"/></p>
    <p><input type="button" name="blah"
        value="Submit Form"
        onclick="verify()"/></p>
</form>

<form action="http://www.HonestRonsInternetDelivery.com/cgi/blah"
        method="get" name="SubForm">
    <input type="hidden" name="DelorCol" value="">
    <input type="hidden" name="AddrDate" value="">
</form>
</body>
</html>

We actually use two forms here - the first one 'UserForm' collects the data, but instead of a 'submit' button we use an ordinary button that is labelled 'Submit Form' but which calls the JavaScript routine 'verify' when clicked.

verify and its associated subfunctions checks the data, and alerts the user if it is not valid. Otherwise, it stores it in the hidden elements of the second form called 'SubForm'. Hidden elements of forms exist solely to store data - and are (fairly obviously) not displayed by the browser. The JavaScript then automatically submits the [hidden] form 'SubForm' using the 'SubForm.submit()' statement. You can try this form out here - note that the 'SubForm.submit()' statements have been replaced by window alerts.

15.2. AJAX

Javascript is a significant component in a web application development style known as AJAX - Asynchronous Javascript and XML. This name, like many, was made up at the last minute for a presentation, and in practice it is not necessary to use XML - we will just use text strings. Basically, AJAX involves using Javascript to contact servers in the background ('asynchronous') to transfer data and update parts of a page without reloading it completely. You are all probably familar with the usual kind of web application, in which you enter data and the application only responds when you activate some form of submit event - usually resulting in the entire page reloading. However, you've probably increasingly come across pages that don't seem to act like 'traditional' web applications - but more like 'desktop' ones. Chances are these applications rely quite heavily on AJAX

The technique centres around the XMLHttpRequest class, which allows javascript to programatically construct HTTP requests, invoke them, and collect the resulting response text. Here's a simple PHP page that checks is a string is a palindrome:


<?php
$inString=$_POST["string"];
    
    $String = strtolower($inString);
    $String = str_replace(array("\n","\t","\r"," "),"",$String);
    $String = ereg_replace("[^A-Za-z0-9]+","",$String);
    
    if ($String == strrev($String)) {
        print "$inString  is a palindrome.";
    } else {
        print "$inString  is not a palindrome.";
    }

?>

The code simply normalizes case (to lower case) and then strips out non alphanumeric characters, before checking to see if a string is equal to its reverse.

We would traditionally invoke code like this using a form like this:


<html>

<body>
  <FORM action="palindrome.php" method="POST">
            
  <p>
    <INPUT type="Submit" value="Submit" >
  </p>
  <p>
  <input type="text" name="string" size="32"/>
  <p>
    <input type="text" name="output" size="32">
  </p>
  </FORM>
</body>
</html>

Which works, but is a bit primitive. Here's the AJAX version, which we will explain below:



<head>
    <script language="JavaScript">
    function submitForm()
    { 
        var req = null; 

        
        if(window.XMLHttpRequest)
            req = new XMLHttpRequest(); 
        else if (window.ActiveXObject)
            req  = new ActiveXObject(Microsoft.XMLHTTP); 

        req.onreadystatechange = function()
        { 
            
            if(req.readyState == 4)
            {
                if(req.status == 200)
                {
                    document.ajax.output.value=req.responseText;    
                }    
                else    
                {
                    document.ajax.output.value="Argh!";
                }    
            } 
        }; 
        req.open("POST", "palindrome.php", true); 
        req.setRequestHeader("Content-Type", "application/x-www-form-urlencoded"); 
        req.send("string="+document.ajax.string.value); 
    } 
    </script>
</head>

<body>
  <FORM name="ajax" method="POST" action="">
            
  <p>
    <INPUT type="BUTTON" value="Submit"  ONCLICK="submitForm()">
  </p>
  <p>
  <input type="text" name="string" size="32"/>
  <p>
    <input type="text" name="output" size="32">
  </p>
  </FORM>
</body>
</html>
The work is all in the function submitForm, which is invoked by the form button. The first part creates an XMLHttpRequest object - note that we are again facing the different-browsers-do-things-differently problem:


if(window.XMLHttpRequest)
    req = new XMLHttpRequest(); 
else if (window.ActiveXObject)
    req  = new ActiveXObject(Microsoft.XMLHTTP); 

For most browsers (Firefox, Safari etc.) we can simply call the XMLHttpRequest constructor - and an easy way to check is to see if the window.XMLHttpRequest object exists (i.e. the XMLHttpRequest object for the current window. If not, then we're probably using IE - and for version 6 and later, then an XMLHttpRequest object is actually an ActiveX control. (This code doesn't work for older browsers anyway). Also, it's not the only way of doing it - you could check for the browser being used by reading the HTTP_USER_AGENT environment variable, and another common way is to use exceptions:


  try
    {
    xmlHttp=new XMLHttpRequest();
    }
  catch (e)
    {
      try
        {
        xmlHttp=new ActiveXObject("Microsoft.XMLHTTP");
        }
      catch (e)
        {
        alert("Nope...");
        }
    }
    
You can easily think of others as well.

Once the XMLHttpRequest object has been created, we need to trap and handle the onreadystatechange event - in this case, by linking it to an anonymous function:


req.onreadystatechange = function()...

The onreadystatechange event is fired every time the readystate property changes - it has five numeric values (0-4), and we are only interested in this case in the last one, representing request completion.


        req.onreadystatechange = function()
        { 
            
            if(req.readyState == 4)
            {
                if(req.status == 200)
                {
                    document.ajax.output.value=req.responseText;    
                }    
                else    
                {
                    document.ajax.output.value="Argh!";
                }    
            } 
        }; 

This code checks to see if the return code is 200 (success), and if so sets the value of document component called document.ajax.output - in this case, a text box called output in the form called ajax - to the result of the request. Hopefully, the behaviour in other cases is obvious. Note that this function is called everytime a state change occurs, but is does nothing until the state reaches 4, indicating request completion.

The last part actually makes the request:


        req.open("POST", "palindrome.php", true); 
        req.setRequestHeader("Content-Type", "application/x-www-form-urlencoded"); 
        req.send("string="+document.ajax.string.value); 

The first line sets the basic parameters of the request - in this case it's a POST request to palindrome.php. The final true parameter means the request can be made asynchronously - that is, immediately it is invoked. The second line sets the MIME type; and the final line sends the request, with the parameter called string set to be the value of the text box called string.

This is not a particularly exciting example - and it's so short that it is not obvious (probably) to the user that the whole page is not reloading (especially since in our demo the server and client are on the same machine). The next example is a bit more interesting - basically, we just delete the form's submit button, and change it so that submitFunction is invoked evertime a change is made to the input text box:


<FORM name="ajax" method="POST" action="">
  <p>
  <input type="text" name="string" onkeydown="submitForm()" size="32"/>
  <p>
    <input type="text" name="dyn" size="32">
  </p>
</FORM>

Now, the page responds - without completely reloading - everytime a new character is typed (or deleted).

This is interesting behaviour, though notice we were able to manage something that behaved similarly in the Web Service chapter, where a simple web application invoked a web service and updated a label every time a characters was typed (again without reloading the page).

15.3. AJAX in Practice

You're probably not too excited about the prospect of using the rather convoluted and difficult code above - and in practice you don't have to. As with much routine code now, this can be automatically generated for you - in this case, when a page is actually sent to a browser.

The .NET framework now has AJAX built in. Here's a very simple example (leaving out the non-essential stuff).


    <form id="form1" runat="server">
   
        <asp:ScriptManager ID="ScriptManager1" runat="server">
        </asp:ScriptManager>
  
    <asp:UpdatePanel ID="UpdatePanel1" runat="server">
    <ContentTemplate>
       <asp:Button ID="Button1" runat="server" Text="Button" style="height: 26px" OnClick="Button1_Click" />
         
    <asp:Label ID="Label1" runat="server" Text=""></asp:Label>
    </ContentTemplate>
        
    </asp:UpdatePanel>
    
    </form>

To 'AJAXify' a page you need to add a ScriptManager control (which generates the 'general' code to handle all AJAX requests on a page) and then embed the components we wish to be updated with partial page loads in UpdatePanel controls (which define which bits of a page get updated). You can have as many of these UpdatePanel controls as you want, though things can get a bit complicated if you want some to be updated by some actions and some by others. Within each UpdatePanel you put the content in a (mandatory) ContentTemplate. In this case, we have a label and a button (though the button could be outside). As well as a ContentTemplate you can also have an (optional) Trigger control, which specifies under what circumstances a panel gets updated. In our case, we don't need one as it's always going to be updated whenever the button is clicked (code below). But if you have multiple panels and don't want them all to be updated everytime, one way to do it is to specify an appropriate trigger.

The actual code for the button click method is...


    String[] StarWars = { "Luke Skywalker", "Darth Vader", "Yoda", "Obi Wan Kenobi" };

    protected void Button1_Click(object sender, EventArgs e)
    {
        if (Session["StarWars"] == null)
        {
            Session["StarWars"] = "0";
        }
        int count = Convert.ToInt32(Session["StarWars"]);
        Label1.Text = StarWars[count];
        count += 1;
        if (count > 3)
        {
            count = 0;
        }
        Session["StarWars"] = count.ToString();
    }

...which is exactly the same as for a non-AJAX page (which shouldn't be suprising since that was also true of the 'manually' built AJAX example above).

Other things we can do include specifying timers (so events happen without user input at all), and progress monitors (so you can see how a slow operation is progressing). It's fair to say that AJAX provision in .NET (and similar environments) is a bit basic at the moment - there are only a few controls and the need to manually add a ScriptManager is a bit annoying (expect that to go in the next 'major' release).
原文地址:https://www.cnblogs.com/hzhida/p/2640854.html