【温故而知新-Javascript】使用 Ajax(续)

1.
备选向服务器发送数据

Ajax 最普遍的一大用处是向服务器发送数据。最突出的图景是从
客户端发送表单数据,即用户在form成分所含的依次 input
成分里输入的值。上边代码显示了一张简略的表单:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>基本表单</title>
    <style>
        .table{display: table;}
        .row{display: table-row;}
        .cell{display: table-cell;padding: 5px;}
        .lable{text-align: right;}
    </style>
</head>
<body>
<form id="fruitform" method="post" action="http://127.0.0.1:8080/form">
    <div class="lable">
        <div class="row">
            <div class="cell lable">Apples:</div>
            <div class="cell"><input name="apples" value="5" /></div>
        </div>
        <div class="row">
            <div class="cell lable">Bananas:</div>
            <div class="cell"><input name="bananas" value="2" /></div>
        </div>
        <div class="row">
            <div class="cell lable">Cherries:</div>
            <div class="cell"><input name="cherries" value="20" /></div>
        </div>
        <div class="row">
            <div class="cell lable">Total:</div>
            <div id="results" class="cell">0 items</div>
        </div>
    </div>
    <button id="submit" type="submit">Submit Form</button>
</form>
</body>
</html>

其一例子中的表单包罗多个input成分和2个付给button
。这一个input成分让用户能够钦点二种差别的说过各自要订货多少,button则会将表单提交给服务器。

 

1.1 定义服务器

眼看,那里需求为这么些示例成立处理请求的服务器。这里再三回选拔Node.js,原因首倘诺它很简单,而且用的是Javascript。新建
fruitcalc.js脚本文件如下: 

var http = require('http');
var querystring = require('querystring');

function writeResponse(res,data){
    var total = 0;
    for(fruit in data){
        total += Number(data[fruit]);
    }
    res.writeHead(200,"OK",{
        "Content-Type":"text/html",
        "Access-Control-Allow-Origin":"http://localhost:63342"
    });
    res.write('<html><head><title>Fruit Total</title></head><body>');
    res.write('<p>'+total+' item ordered</p></body></html>');
    res.end();
}

http.createServer(function(req,res){
    console.log("[200] "+req.method+" to "+req.url);
    if(req.method == "OPTIONS"){
        res.writeHead(200,"OK",{
            "Access-Control-Allow-Header":"Content-Type",
            "Access-Control-Allow-Methods":"*",
            "Access-Control-Allow-Origin":"*"
        });
        res.end();
    }else if(req.url == '/form'&& req.method == 'POST'){
        var dataObj = new Object();
        var contentType = req.headers["content-type"];
        var fullBody = '';

        if(contentType){
            if(contentType.indexOf("application/x-www-form-urlencode") > -1){
                req.on('data',function(chunk){
                    fullBody += chunk.toString();
                });
                req.on('end',function(){
                    var dBody = querystring.parse(fullBody);
                    dataObj.apples = dBody["apples"];
                    dataObj.bananas = dBody["bananas"];
                    dataObj.cherries = dBody["cherries"];
                    writeResponse(res,dataObj);
                });
            }else if(contentType.indexOf("application/json") > -1){
                req.on('data',function(chunk){
                    fullBody += chunk.toString();
                });
                req.on('end',function(){
                    dataObj = JSON.parse(fullBody);
                    writeResponse(res,dataObj);
                });
            }
        }
    }
}).listen(8080);

本子中高亮部分:writeResponse函数。那么些函数会在提取请求的表单值之后调用,它负责生产对浏览器的响应。当前,这一个函数会创制不难的HTML文书档案,效果如下:

Ajax 1

那个响应很简单,实现效益是让服务器总结出了用户通过form中逐一input成分所订购的鲜果总数。服务器得端脚本的其他部分负责解码客户端用Ajax发送的各类大概数量格式。

 

1.2 通晓难题所在

上边的图形清楚的叙述了想要用Ajax解决的题材。

当提交表单后,浏览器会在新的页面呈现结果。那表示两点:

* 用户必须等待服务器处理数量并扭转响应;

* 全体文书档案上下文音信都不翼而飞了,因为结果是作为新文书档案举办展示的。

这正是应用Ajax的优秀状态了。能够异步转移请求,那样用户就能在表单被拍卖时继续与文书档案举办相互。

 

2. 出殡和埋葬表单

向服务器发送数据的最基本办法是投机征集并格式化它。上面代码浮现了充分到前边的HTML文书档案example.html 的一段脚本。用的就是这种办法:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>手动收集和发送数据</title>
    <style>
        .table{display: table;}
        .row{display: table-row;}
        .cell{display: table-cell;padding: 5px;}
        .lable{text-align: right;}
    </style>
</head>
<body>
<form id="fruitform" method="post" action="http://127.0.0.1:8080/form">
    <div class="lable">
        <div class="row">
            <div class="cell lable">Apples:</div>
            <div class="cell"><input name="apples" value="5" /></div>
        </div>
        <div class="row">
            <div class="cell lable">Bananas:</div>
            <div class="cell"><input name="bananas" value="2" /></div>
        </div>
        <div class="row">
            <div class="cell lable">Cherries:</div>
            <div class="cell"><input name="cherries" value="20" /></div>
        </div>
        <div class="row">
            <div class="cell lable">Total:</div>
            <div id="results" class="cell">0 items</div>
        </div>
    </div>
    <button id="submit" type="submit">Submit Form</button>
</form>
<script>
    document.getElementById("submit").onclick = handleButtonPress;

    var httpRequest;
    function handleButtonPress(e){
        //对表单里的button元素而言,其默认行为是用常规的非Ajax方式提交表单。这里不想让他发生,所以调用了preventDefault方法
        e.preventDefault();
        var form = document.getElementById("fruitform");
        //收集并格式化各个input的值
        var formData ="";
        var inputElements = document.getElementsByTagName("input");
        for (var i = 0; i < inputElements.length; i++){
            formData += inputElements[i].name + "=" + inputElements[i].value +"&";
        }
        httpRequest = new XMLHttpRequest();
        httpRequest.onreadystatechange = handleResponse;
        //数据必须通过POST方法发送给服务器,并读取了HTMLFormElement的action属性获得了请求需要发送的URL
        httpRequest.open("POST",form.action);
        //添加标头来告诉服务器准备接受的数据格式
        httpRequest.setRequestHeader('Content-Type','application/x-www-form-urlencoded');
        //把想要发送给服务器的字符串作为参数传递给send方法
        httpRequest.send(formData);
    }

    function handleResponse(){
        if(httpRequest.readyState == 4 && httpRequest.status == 200){
            document.getElementById("results").innerHTML = httpRequest.responseText;
        }
    }
</script>
</body>
</html>

 

效益图如下:

Ajax 2

服务器响应表单提交后归来的HTML文书档案会突显在同样页,而且该请求是异步执行的。

  

3.
发送JSON数据

Ajax,Ajax不止用来发送表单数据,大概能够发送任何数据,包蕴JavaScript对象表示法(JavaScript
Object
Notation,JSON)数据,而它差不离已经化为一种流行的数额格式了。Ajax扎根于XML,但这一格式很麻烦。当运转的Web应用程序必须传输多量XML文书档案时,繁琐就表示带宽和系统体积方面包车型大巴实际开销。

JSON平日被称作XML的“脱脂”替代品。JSON易于阅读和编写制定,比XML更紧密,而且早已获得了广泛援救。JSON发源于JavaScript,但它的前行已经超(英文名:jīng chāo)过了
JavaScript,被很多的程序包和连串精晓并动用。

以下是三个粗略的JavaScript对象用JSON表明的例子:

{"bananas":"2","apples":"5","cherries":"20"}

那些指标有多个本性:bananas、apples和cherries。这么些属性的值分别是② 、5和20。

JSON的效果比不上XML丰盛,但对很多应用程序来说,那1个功效是用不到的。JSON简单、轻量和全数表现力。上面例子演示了发送JSON数据到服务器有多不难,修改前例的JavaScript部分如下:

<script>
    document.getElementById("submit").onclick = handleButtonPress;

    var httpRequest;
    function handleButtonPress(e){
        e.preventDefault();
        var form = document.getElementById("fruitform");

        var formData = new Object();
        var inputElements = document.getElementsByTagName("input");
        for(var i=0;i<inputElements.length;i++){
            formData[inputElements[i].name] = inputElements[i].value;
        }

        httpRequest = new XMLHttpRequest();
        httpRequest.onreadystatechange = handleResponse;
        httpRequest.open("POST",form.action);
        httpRequest.setRequestHeader("Content-Type","application/json");
        httpRequest.send(JSON.stringify(formData));
    }

    function handleResponse(){
        if(httpRequest.readyState == 4 && httpRequest.status == 200){
            document.getElementById("results").innerHTML = httpRequest.responseText;
        }
    }
</script>

这段脚本,创制了一个新的Object,并定义了有的属性来对应表单内各样input成分的name属性值。能够选择任何数据,但
input成分很便利,而且能和事先的事例保持一致。

为了告诉服务器正在发送JSON数据,把请求的Content-Type标头设为
application/json,就好像这么:

httpRequest.setRequestHeader("Content-Type","application/json");

用JSON对象和JSON格式进行互动的变换。(大部分浏览器能间接支持那么些目的,但也能够用上边包车型大巴网址里的本子来给旧版的浏览器添加相同的功能:https://github.com/douglascrockford/JSON-js/blob/master/json2.js )JSON对象提供了七个格局:

Ajax 3

在地点的例证中,使用了stringify方法,然后把结果传递给XMLHttpRequest
对象的send方法。此例中唯有多少的编码方式发生了变动。提交表单的效果照旧一如既往。

 

4.
选择FormData对象发送表单数据

另一种更简短的表单收集方式是利用二个FormData对象,它是在XMLHttpRequest的第三级正式中定义的。

出于原先的Node.js代码有点难题,此处用C#新建文件 fruitcalc.aspx作为拍卖请求的服务器。其cs代码如下:

using System;

namespace Web4Luka.Web.ajax.html5
{
    public partial class fruitcalc : System.Web.UI.Page
    {
        protected void Page_Load(object sender, EventArgs e)
        {
            int total = 0;
            if (Request.HttpMethod == "POST")
            {
                if (Request.ContentType.IndexOf("multipart/form-data") > -1)
                {
                    for (int i = 0; i < Request.Form.Count; i++)
                    {
                        total += Int32.Parse(Request.Form[i]);
                    }
                }
                writeResponse(Response, total);
            }
        }

        private void writeResponse(System.Web.HttpResponse Response, int total)
        {
            string strHtml;
            Response.AddHeader("Access-Control-Allow-Origin", "http://localhost:63342");
            strHtml = total + " item ordered";
            Response.Write(strHtml);
        }
    }
}

4.1 创建 FormData 对象

创设FormData对象时可以传递1个HTMLFormElement对象,那样表单里具有的要素的值都会被活动收集起来。示例如下:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>使用FormData对象</title>
    <style>
        .row{display: table-row;}
        .cell{display: table-cell;padding: 5px;}
        .lable{text-align: right;}
    </style>
</head>
<body>
<form id="fruitform" method="post" action="http://localhost:53396/ajax/html5/fruitcalc.aspx">
    <div class="lable">
        <div class="row">
            <div class="cell lable">Apples:</div>
            <div class="cell"><input name="apples" value="5" /></div>
        </div>
        <div class="row">
            <div class="cell lable">Bananas:</div>
            <div class="cell"><input name="bananas" value="2" /></div>
        </div>
        <div class="row">
            <div class="cell lable">Cherries:</div>
            <div class="cell"><input name="cherries" value="20" /></div>
        </div>
        <div class="row">
            <div class="cell lable">Total:</div>
            <div id="results" class="cell">0 items</div>
        </div>
    </div>
    <button id="submit" type="submit">Submit Form</button>
</form>
<script>
    document.getElementById("submit").onclick = handleButtonPress;

    var httpRequest;
    function handleButtonPress(e){
        e.preventDefault();
        var form = document.getElementById("fruitform");
        var formData = new FormData(form);

        httpRequest = new XMLHttpRequest();
        httpRequest.onreadystatechange = handleResponse;
        httpRequest.open("POST",form.action);
        httpRequest.send(formData);
    }

    function handleResponse(){
        if(httpRequest.readyState == 4 && httpRequest.status == 200){
            document.getElementById("results").innerHTML = httpRequest.responseText;
        }
    }
</script>
</body>
</html>

自然,关键的变型是选拔了FormData对象:

var formData = new FormData(form);

任何急需留意的地点是不再设置Content-Type标头的值了。如若应用FormData对象,数据连接会被编码为multipart/form-data
。本例提交表单后,展现效果如下:

Ajax 4

 

4.2 修改FormData对象

FormData对象定义了一个格局,它同意给要发送到服务器的数据增加名称/值对。

Ajax 5

能够用append方法增补从表单中收集的多少,也足以在不使用HTMLFormElement的景观下开创FormData对象。那就意味着可以选取append方法来摘取向客户端发送哪些数据值。修改上一示例的JavaScript代码如下:

<script>
    document.getElementById("submit").onclick = handleButtonPress;

    var httpRequest;
    function handleButtonPress(e){
        e.preventDefault();
        var form = document.getElementById("fruitform");

        var formData = new FormData();
        var inputElements = document.getElementsByTagName("input");
        for(var i=0;i<inputElements.length;i++){
            if(inputElements[i].name != "cherries"){
                formData.append(inputElements[i].name,inputElements[i].value);
            }
        }

        httpRequest = new XMLHttpRequest();
        httpRequest.onreadystatechange = handleResponse;
        httpRequest.open("POST",form.action);
        httpRequest.send(formData);
    }

    function handleResponse(){
        if(httpRequest.readyState == 4 && httpRequest.status == 200){
            document.getElementById("results").innerHTML = httpRequest.responseText;
        }
    }
</script>

 此段脚本里,创制FormData对象时并不曾提供HTMLFormElement对象。随后用DOM找到文书档案里装有的input成分,并为那么些name属性的值不是cherries的因素添加名称/值对。此例提交表单后,显示效果如下:

 Ajax 6

 

5.
出殡和埋葬文书

能够动用FormData 对象和type 属性为 file 的input
成分向服务器发送文件。当表单提交时,FormData对象会自动确定保证用户挑选的文件内容与其它的表单值一同上传。下边包车型客车例子显示了怎样以那种措施接纳FormData对象。

 

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>使用FormData对象发送文件到服务器</title>
    <style>
        .row{display: table-row;}
        .cell{display: table-cell;padding: 5px;}
        .lable{text-align: right;}
    </style>
</head>
<body>
<form id="fruitform" method="post" action="http://localhost:53396/ajax/html5/fruitcalc.aspx">
    <div class="lable">
        <div class="row">
            <div class="cell lable">Apples:</div>
            <div class="cell"><input name="apples" value="5" /></div>
        </div>
        <div class="row">
            <div class="cell lable">Bananas:</div>
            <div class="cell"><input name="bananas" value="2" /></div>
        </div>
        <div class="row">
            <div class="cell lable">Cherries:</div>
            <div class="cell"><input name="cherries" value="20" /></div>
        </div>
        <div class="row">
            <div class="cell lable">File:</div>
            <div class="cell"><input type="file" name="file" /></div>
        </div>
        <div class="row">
            <div class="cell lable">Total:</div>
            <div id="results" class="cell">0 items</div>
        </div>
    </div>
    <button id="submit" type="submit">Submit Form</button>
</form>
<script>
    document.getElementById("submit").onclick = handleButtonPress;

    var httpRequest;
    function handleButtonPress(e){
        e.preventDefault();
        var form = document.getElementById("fruitform");
        var formData = new FormData(form);
        httpRequest = new XMLHttpRequest();
        httpRequest.onreadystatechange = handleResponse;
        httpRequest.open("POST",form.action);
        httpRequest.send(formData);
    }

    function handleResponse(){
        if(httpRequest.readyState == 4 && httpRequest.status == 200){
            document.getElementById("results").innerHTML = httpRequest.responseText;
        }
    }
</script>
</body>
</html>

此例里,最理解的成形产生在
form成分上。添加了input成分后,FormData对象就会上传用户所选的轻易文件。

修改 fruitcalc.aspx 的cs文件如下:

using System;
using System.Web;

namespace Web4Luka.Web.ajax.html5
{
    public partial class fruitcalc : System.Web.UI.Page
    {
        protected void Page_Load(object sender, EventArgs e)
        {
            int total = 0;
            if (Request.HttpMethod == "POST")
            {
                if (Request.ContentType.IndexOf("multipart/form-data") > -1)
                {
                    for (int i = 0; i < Request.Form.Count; i++)
                    {
                        total += Int32.Parse(Request.Form[i]);
                    }
                    if (Request.Files["file"] != null)
                    {
                        HttpPostedFile file = Request.Files["file"];
                        file.SaveAs(Server.MapPath("/upload/pictures/" + file.FileName));
                    }
                }
                writeResponse(Response, total);
            }
        }

        private void writeResponse(System.Web.HttpResponse Response, int total)
        {
            string strHtml;
            Response.AddHeader("Access-Control-Allow-Origin", "http://localhost:63342");
            strHtml = total + " item ordered";
            Response.Write(strHtml);
        }
    }
}

留神在劳动器端创建对应上传文件保留的文书夹,,此例的来得效果如下:

Ajax 7

 

 

来源:《HTML5高尚指南》(《The Definitive Guide to
HTML5》)

 

相关文章