JavaScript學習筆記(一)——延遲對象、跨域、模板引擎、彈出層、AJAX示例

1、AJAX示例

AJAX全稱爲「Asynchronous JavaScript And XML」(異步JavaScript和XML) 是指一種建立交互式網頁應用的開發技術、改善用戶體驗,實現無刷新效果。javascript

1.一、優勢

不須要插件支持css

優秀的用戶體驗html

提升Web程序的性能前端

減輕服務器和帶寬的負擔java

1.二、缺點

瀏覽器對XMLHttpRequest對象的支持度不足,幾乎全部瀏覽器如今都支持jquery

破壞瀏覽器「前進」、「後退」按鈕的正常功能,能夠經過簡單的插件彌補git

對搜索引擎的支持不足github

1.三、jQuery AJAX示例

在HTML5中對原生的AJAX核心對象XMLHttpRequest進行升級,也就是XHR2,功能更增強大。 ajax

jQuery對AJAX封裝的很是好,這裏以簡單的商品管理爲示例使用jQuery完成AJAX應用。apache

Product.java bean:

package com.gomall.bean;

/***
 * 產品
 * 
 * @author Administrator
 *
 */
public class Product {
    /** 編號 */
    private int id;
    /** 名稱 */
    private String name;
    /** 價格 */
    private double price;
    /** 圖片 */
    private String picture;
    /** 詳細 */
    private String detail;

    @Override
    public String toString() {
        return "Product [id=" + id + ", name=" + name + ", price=" + price + ", picture=" + picture + ", detail="
                + detail + "]";
    }

    public Product(int id, String name, double price, String picture) {
        super();
        this.id = id;
        this.name = name;
        this.price = price;
        this.picture = picture;
    }

    public Product(int id, String name, double price, String picture, String detail) {
        super();
        this.id = id;
        this.name = name;
        this.price = price;
        this.picture = picture;
        this.detail = detail;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public double getPrice() {
        return price;
    }

    public void setPrice(double price) {
        this.price = price;
    }

    public String getPicture() {
        return picture;
    }

    public void setPicture(String picture) {
        this.picture = picture;
    }

    public String getDetail() {
        return detail;
    }

    public void setDetail(String detail) {
        this.detail = detail;
    }
}

IProductService.java:

package com.gomall.service;

import java.util.List;

import com.gomall.bean.Product;

public interface IProductService {

    /**得到全部*/
    List<Product> getAll();

    /**添加
     * @return */
    boolean add(Product entity);

    /**根據編號得到產品對象*/
    Product findById(int id);

    /**根據編號得到產品對象
     * @return */
    boolean deleteById(int id);

}

ProductService.java:

package com.gomall.service;

import java.util.ArrayList;
import java.util.List;
import java.util.Random;

import com.gomall.bean.Product;

public class ProductService implements IProductService {
    public static ArrayList<Product> products;

    static {
        products = new ArrayList<>();
        Random random = new Random();
        for (int i = 1; i <= 10; i++) {
            Product product = new Product(i, "華爲Mate9MHA-AL00/4GB RAM/全網通華爲超級閃充技術雙後攝設計" + random.nextInt(999), random.nextDouble() * 1000,
                    "pic(" + i + ").jpg", "產品詳細");
            products.add(product);
        }
    }

    /*
     * (non-Javadoc)
     * 
     * @see com.gomall.service.IProductService#getAll()
     */
    @Override
    public List<Product> getAll() {
        return products;
    }

    /*
     * (non-Javadoc)
     * 
     * @see com.gomall.service.IProductService#add(com.gomall.bean.Product)
     */
    @Override
    public boolean add(Product entity) {
        try {
            entity.setId(products.size() + 1);
            entity.setPicture("pic(" + entity.getId() + ").jpg"); // uploadify
                                                                    // 上傳圖片
            products.add(entity);
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
        return true;
    }

    /*
     * (non-Javadoc)
     * 
     * @see com.gomall.service.IProductService#findById(int)
     */
    @Override
    public Product findById(int id) {
        for (Product product : products) {
            if (product.getId() == id) {
                return product;
            }
        }
        return null;
    }

    /*
     * (non-Javadoc)
     * 
     * @see com.gomall.service.IProductService#deleteById(int)
     */
    @Override
    public boolean deleteById(int id) {
        try {
            Product product = findById(id);
            if (product != null) {
                products.remove(product);
            }
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
        return true;
    }
}

ProductAction.java:

package com.gomall.action;

import java.io.IOException;
import java.io.PrintWriter;
import java.util.Date;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.codehaus.jackson.map.ObjectMapper;

import com.gomall.bean.Product;
import com.gomall.service.IProductService;
import com.gomall.service.ProductService;

@WebServlet("/Product")
public class ProductAction extends HttpServlet {
    private static final long serialVersionUID = 1L;

    public ProductAction() {
        super();
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        /*模擬網絡延時*/
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        request.setCharacterEncoding("utf-8");
        response.setCharacterEncoding("utf-8");
        String act = request.getParameter("act");
        IProductService productService = new ProductService();
        /**用於序列化json*/
        ObjectMapper mapper = new ObjectMapper();
        PrintWriter out=response.getWriter();
        
        if (act.equals("getAll")) {
            String json = mapper.writeValueAsString(productService.getAll());
            out.append(json);
        } else if (act.equals("area")) {
            String callback=request.getParameter("callback");
            out.append(callback+"('"+new Date()+"')");
        } else if (act.equals("getJSONP")) {
            String callback=request.getParameter("callback");
            String json = mapper.writeValueAsString(productService.getAll());
            out.append(callback+"("+json+")");
        } else if (act.equals("getAllCORS")) {
            /**向響應的頭部中添加CORS信息*/
            response.addHeader("Access-Control-Allow-Origin", "*");
            response.addHeader("Access-Control-Allow-Methods", "GET,POST");
            
            String json = mapper.writeValueAsString(productService.getAll());
            out.append(json);
        } else if(act.equals("del")){
            /**向響應的頭部中添加CORS信息*/
            response.addHeader("Access-Control-Allow-Origin", "*");
            response.addHeader("Access-Control-Allow-Methods", "GET,POST");
            int id=Integer.parseInt(request.getParameter("id"));
            String json = mapper.writeValueAsString(productService.deleteById(id));
            out.append(json);
        }
        else if(act.equals("add")){
            /**向響應的頭部中添加CORS信息*/
            response.addHeader("Access-Control-Allow-Origin", "*");
            response.addHeader("Access-Control-Allow-Methods", "GET,POST");
            String name=request.getParameter("name");
            double price=Double.parseDouble(request.getParameter("price"));
            String detail=request.getParameter("detail");
            Product entity=new Product(0, name, price, "",detail);
            String json = mapper.writeValueAsString(productService.add(entity));
            out.append(json);
        }
    }

    protected void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        doGet(request, response);
    }
}

運行結果:

刪除:

2、延遲對象(Deferred)

deferred對象就是jQuery1.5版之後新增長的回調函數解決方案。

2.一、回調函數

先看一個示例:

首先,爲何要使用Deferred?

<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <title>回調</title>
    </head>
    <body>
        <script src="js/jQuery1.11.3/jquery-1.11.3.min.js" type="text/javascript" charset="utf-8"></script>
        <script type="text/javascript">
            var student;
            $.get("student.json",function(data){
                student=data;
            },"json");
            alert(student);
        </script>
    </body>
</html>

student.json文件:{"name":"tom","id":"01"}

運行結果:

由於AJAX是異步執行的,相似高級語言中的多線程,當發起ajax請求時會有網絡延遲,而代碼並無在$.get的位置被阻塞,alert先執行,但數據並無從遠程獲取到,因此結果是undefined。

其實初學者常常會犯這種錯誤,如:

            function getStudentById(id){
                $.get("students.do",{"id":id},function(stu){
                    return stu;
                },"json");
            }

上面的代碼是有問題的,緣由如前面的示例是同樣的。怎麼解決,若是你認爲是異步帶來的問題,固然經過同步是能夠解決的,如:

            $.ajax({
                type:"get",
                url:"student.json",
                async:false,  /*非異步,同步*/
                success:function(data){
                    student=data;
                }
            });

結果:

若是將全部的ajax請求修改成同步的,則ajax的好處就大打折扣了,若是即要異步又要解決上面的問題,可使用回調方法。

示例:

<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <title>回調</title>
    </head>
    <body>
        <script src="js/jQuery1.11.3/jquery-1.11.3.min.js" type="text/javascript" charset="utf-8"></script>
        <script type="text/javascript">
           /*callback是一個當ajax請求成功時的回調方法*/
            function getStudent(callback) {
                $.get("student.json", function(data) {
                    callback(data);
                }, "json");
            }
            
            /*調用getStudent函數,傳入參數,參數也是一個函數*/
            getStudent(function(stu){
                alert(stu.id);
            });
            
            /*把一個方法做爲參數傳遞給另外一個方法能夠認爲是委託,如C++中的函數指針相似*/
            getStudent(function(stu){
                alert(stu.name);
            });
        </script>
    </body>
</html>

結果:

 

從這裏看回調很完美,其實否則,實際開發中要複雜得多,如當第一個ajax請求完成才能夠完成第二個,當第二個完成才能夠完成第三個,可能最一個請求要等前面的全部請求都成功時才容許執行或纔有條件執行,如

使用ajax編輯用戶信息,先加載用戶對象,再加載省,加載市,加縣,可能代碼會這樣寫:

            $.get("url1",function(){
                $.get("url2",function(){
                    $.get("url3",function(){
                      ...
                   });
                });
            });

當回調愈來愈多,嵌套越深,代碼可讀性就會愈來愈差。若是註冊了多個回調,那更是一場噩夢,幸虧從jQuery1.5開始出現了延遲對象(deferred),能夠解決這個問題。

2.二、deferred.done

$.ajax()操做完成後,若是使用的是低於1.5.0版本的jQuery,返回的是XHR對象,你無法進行鏈式操做;若是高於1.5版,返回的是deferred對象,能夠進行鏈式操做。

當延遲成功時調用一個函數或者數組函數,功能與原success相似。

語法:deferred.done(doneCallbacks[,doneCallbacks]) 

返回值:Deferred Object

該參數能夠是一個函數或一個函數的數組。當延遲成功時,doneCallbacks被調用。回調執行是依照他們添加的順序。一旦deferred.done()返回延遲對象,延遲對象的其它方法也能夠連接到了這裏,包括增長.done()方法。當延遲解決,doneCallbacks執行使用參數提供給 resolveresolveWith方法依照添加的順序調用。

示例代碼:

<!DOCTYPE html>
<html>

    <head>
        <meta charset="UTF-8">
        <title>延遲對象(deferred)</title>
    </head>

    <body>
        <script src="js/jQuery1.11.3/jquery-1.11.3.min.js" type="text/javascript" charset="utf-8"></script>
        <script type="text/javascript">
            console.log("使用方法一");
            $.get("student.json", "json").done(function(stu) {
                console.log(stu.id);
            }).done(function(stu) {
                console.log(stu.name);
            });

            console.log("使用方法二");
            $.get("student.json", "json").done(function(stu) {
                console.log(stu.id);
            }, function(stu) {
                console.log(stu.name);
            });
        </script>
    </body>

</html>

運行結果:

2.三、deferred.fail

語法:deferred.fail(failCallbacks[,failCallbacks])

返回值:Deferred Object

當延遲失敗時調用一個函數或者數組函數,功能與原回調方法error相似。

該參數能夠是一個函數或一個函數的數組。當延遲失敗時,doneCallbacks被調用。回調執行是依照他們添加的順序。一旦deferred.fail()返回延遲對象,延遲對象的其它方法也能夠連接到了這裏,包括增長.done()方法。當延遲解決,doneCallbacks執行使用參數提供給 resolveresolveWith方法依照添加的順序調用。

示例:

<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <title>延遲對象(deferred)</title>
    </head>
    <body>
        <script src="js/jQuery1.11.3/jquery-1.11.3.min.js" type="text/javascript" charset="utf-8"></script>
        <script type="text/javascript">
            $.get("stu.json", "json").done(function(stu) {
                console.log(stu.name);
            }).fail(function(xhr, status, errorThrown){
                alert("xhr:"+xhr+",status:"+status+",errorThrown:"+errorThrown);
            });
        </script>
    </body>
</html>

運行結果:

2.四、deferred.always

語法:deferred.always(alwaysCallbacks,[alwaysCallbacks])

返回值:Deferred Object

當遞延對象是解決(成功,resolved)或拒絕(失敗,rejected)時被調用添加處理程序,與回調方法complete相似。

示例:

<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <title>延遲對象(deferred)</title>
    </head>
    <body>
        <script src="js/jQuery1.11.3/jquery-1.11.3.min.js" type="text/javascript" charset="utf-8"></script>
        <script type="text/javascript">
            $.get("student.j", "json").done(function(stu) {
                console.log(stu.name);
            }).fail(function(data, status, errorThrown){
                console.log("data:"+data+",status:"+status+",errorThrown:"+errorThrown);
            }).always(function(data, textStatus){
                console.log("ajax執行完成,完成狀態:"+textStatus);
            });
        </script>
    </body>
</html>

運行結果

成功時:

 

失敗時:

2.五、deferred.then

deferred.then(doneFilter [, failFilter ] [, progressFilter ])

添加處理程序被調用時,遞延對象獲得解決或者拒絕,一次指定多個事件。

全部三個參數(包括progressCallbacks ,在jQuery的1.7 )能夠是一個單獨的函數或一個函數的數組。 其中一個參數,也能夠爲空,若是沒有該類型的回調是須要的。或者,使用.done()或.fail()僅設置doneCallbacks或failCallbacks。當遞延解決,doneCallbacks被調用。若遞延代替拒絕,failCallbacks被調用。回調按他們添加的順序執行。一旦deferred.then返回延遲對象,延遲對象的其它方法也能夠連接到了這裏,包括增長.then()方法。

示例:

<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <title>延遲對象(deferred)</title>
    </head>
    <body>
        <script src="js/jQuery1.11.3/jquery-1.11.3.min.js" type="text/javascript" charset="utf-8"></script>
        <script type="text/javascript">
            $.get("student.jsonx", "json").then(function(stu) {
                console.log(stu.name);
            }, function(data, status, errorThrown) {
                console.log("data:" + data + ",status:" + status + ",errorThrown:" + errorThrown);
            });
        </script>
    </body>
</html>

結果:

2.六、應用延遲對象

前面的示例中咱們都是使用jQuery ajax返回的deferred對象,其實咱們也能夠在自定義的代碼中使用deferred對象,恰當的使用deferred對象或以優雅的解決很多問題。

示例:

<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <title>延遲對象(deferred)</title>
    </head>
    <body>
        <script src="js/jQuery1.11.3/jquery-1.11.3.min.js" type="text/javascript" charset="utf-8"></script>
        <script type="text/javascript">
            var myTask=function(){
                //經過jQuery建立一個延遲對象
                var def=$.Deferred();
                setTimeout(function(){
                    //隨機產生一個整數,若是是偶數
                    var n=Math.round(Math.random()*100);
                    if(n%2==0)
                    {
                        console.log("一個耗時的操做終於完成了,n="+n);
                        def.resolve();   //任務成功完成
                    }else{
                        console.log("一個耗時的操做失敗,n="+n);
                        def.reject();  //拒絕,失敗
                    }
                },3000);
                 //返回延遲對象,防止中間修改
                return def.promise();
            }
            
            /*當方法myTask執行完成後,添加回調方法*/
            $.when(myTask()).done(function(){
                console.log("執行成功");
            }).fail(function(){
                console.log("執行失敗");
            });
        </script>
    </body>
</html>

失敗時:

成功時:

promise()在原來的deferred對象上返回另外一個deferred對象,後者只開放與改變執行狀態無關的方法(好比done()方法和fail()方法),屏蔽與改變執行狀態有關的方法(好比resolve()方法和reject()方法),從而使得執行狀態不能被改變。

2.七、總結

  (1) $.Deferred() 生成一個deferred對象。
  (2) deferred.done() 指定操做成功時的回調函數
  (3) deferred.fail() 指定操做失敗時的回調函數
  (4) deferred.promise() 沒有參數時,返回一個新的deferred對象,該對象的運行狀態沒法被改變;接受參數時,做用爲在參數對象上部署deferred接口。
  (5) deferred.resolve() 手動改變deferred對象的運行狀態爲"已完成",從而當即觸發done()方法。
  (6)deferred.reject() 這個方法與deferred.resolve()正好相反,調用後將deferred對象的運行狀態變爲"已失敗",從而當即觸發fail()方法。
  (7) $.when() 爲多個操做指定回調函數。
除了這些方法之外,deferred對象還有二個重要方法,上面的教程中沒有涉及到。
  (8)deferred.then()
有時爲了省事,能夠把done()和fail()合在一塊兒寫,這就是then()方法。
若是then()有兩個參數,那麼第一個參數是done()方法的回調函數,第二個參數是fail()方法的回調方法。若是then()只有一個參數,那麼等同於done()。
  (9)deferred.always()
這個方法也是用來指定回調函數的,它的做用是,無論調用的是deferred.resolve()仍是deferred.reject(),最後老是執行。

3、跨域

互聯網上的主機由IP來標識,爲了方便記憶,建立了域名系統.域名與IP對應,域名的做用是不用讓你記複雜的IP地址,能惟必定位資源,URL的格式是協議://主機名.公司名稱.機構類型.地域類型:端口/路徑,如http://www.zhangguo.com.cn:8080/products/list.do?id=1#a1 

3.一、什麼是跨域

JavaScript同源策略的限制,A域名下的JavaScript沒法操做B或是C域名下的對象,以下所示:

假設頁面:http://store.company.com/index.html

 

 

 

 

 

 

客戶端代碼d05.html,http://localhost:8087/jQuery601_JAVA/d05.html

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>6.4.九、跨域AJAX請求</title>
</head>
<body>
    <h2>6.4.九、跨域AJAX請求</h2>
    <h2 id="message"></h2>
    <button type="button" id="btnAjax">ajax請求</button>
    <script type="text/javascript" src="js/jQuery/jquery.min.js"></script>
    <script type="text/javascript">
        $("#btnAjax").click(function() {
            $.get("http://localhost:12833/Action/FindUserById.ashx",{"id":1001},function(data){
                log(data);
            },"text");
        });

        function log(msg) {
            $("#message")[0].innerHTML += msg + "<br/>";
        }
    </script>
</body>
</html>

另外一個域下面通常處理程序,http://localhost:12833/Action/FindUserById.ashx:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;

namespace jQuery601_DotNet.Action
{
    /// <summary>
    /// 根據用戶編號得到用戶
    /// </summary>
    public class FindUserById : IHttpHandler
    {

        public void ProcessRequest(HttpContext context)
        {
            context.Response.ContentType = "text/plain";
            String name = "";
            int id = Convert.ToInt32(context.Request.Params["id"]);
            if (id == 1001)
            {
                name = "Mark";
            }
            else if (id == 1002)
            {
                name = "Jack";
            }
            context.Response.Write(name);
        }

        public bool IsReusable
        {
            get
            {
                return false;
            }
        }
    }
}

運行結果:

3.二、JSONP跨域

JSONP跨域是利用script腳本容許引用不一樣域下的js實現的,將回調方法帶入服務器,返回結果時回調。

2.一、JSONP跨域原理

客戶端:

    <body>
        <script type="text/javascript">
            /*回調方法*/
            function show(data) {
                alert(data);
            }
        </script>
        <script src="http://localhost:8087/JavaScript001/Product?act=area&callback=show" type="text/javascript" charset="utf-8"></script>
    </body>

服務器:

            String callback=request.getParameter("callback");
            out.append(callback+"('"+new Date()+"')");

結果:

 

服務器返回一段javascript,經過指定的方法名調用。從圖中能夠看出,使用JSONP的形式調用已經再也不是經過XMLHTTPRequest對象,而是同步調用。

3.三、jQuery使用JSONP跨域

在jQuery中內置了實現JSONP跨域的功能,若是指定爲json類型,則會把獲取到的數據做爲一個JavaScript對象來解析,而且把構建好的對象做爲結果返回。爲了實現這個目的,他首先嚐試使用JSON.parse()。若是瀏覽器不支持,則使用一個函數來構建。JSON數據是一種能很方便經過JavaScript解析的結構化數據。若是獲取的數據文件存放在遠程服務器上(域名不一樣,也就是跨域獲取數據),則須要使用jsonp類型。使用這種類型的話,會建立一個查詢字符串參數 callback=? ,這個參數會加在請求的URL後面。服務器端應當在JSON數據前加上回調函數名,以便完成一個有效的JSONP請求。若是要指定回調函數的參數名來取代默認的callback,能夠經過設置$.ajax()的jsonp參數。

頁面腳本:

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>6.4.九、跨域AJAX請求</title>
</head>
<body>
    <h2>6.4.九、跨域AJAX請求</h2>
    <h2 id="message"></h2>
    <button type="button" id="btnAjax">ajax請求</button>
    <script type="text/javascript" src="js/jQuery/jquery.min.js"></script>
    <script type="text/javascript">
        $("#btnAjax").click(function() {
            $.get("http://localhost:12833/Action/FindUserById.ashx?callback=?",{"id":1001},function(data){
                log(data);
            },"jsonp");
        });

        function log(msg) {
            $("#message")[0].innerHTML += msg + "<br/>";
        }
    </script>
</body>
</html>

服務器通常處理程序:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;

namespace jQuery601_DotNet.Action
{
    /// <summary>
    /// FindUserById 的摘要說明
    /// </summary>
    public class FindUserById : IHttpHandler
    {

        public void ProcessRequest(HttpContext context)
        {
            context.Response.ContentType = "text/plain";
            String name = "";
            int id = Convert.ToInt32(context.Request.Params["id"]);
            if (id == 1001)
            {
                name = "Mark";
            }
            else if (id == 1002)
            {
                name = "Jack";
            }
            String callback = context.Request["callback"];
            context.Response.Write(callback+"('"+name+"')");
        }

        public bool IsReusable
        {
            get
            {
                return false;
            }
        }
    }
}

運行結果:

服務器Servlet: 

package com.gomall.action;

import java.io.IOException;
import java.io.PrintWriter;
import java.util.Date;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.codehaus.jackson.map.ObjectMapper;

import com.gomall.service.IProductService;
import com.gomall.service.ProductService;

@WebServlet("/Product")
public class Product extends HttpServlet {
    private static final long serialVersionUID = 1L;

    public Product() {
        super();
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        request.setCharacterEncoding("utf-8");
        response.setCharacterEncoding("utf-8");
        String act = request.getParameter("act");
        IProductService productService = new ProductService();
        ObjectMapper mapper = new ObjectMapper();
        PrintWriter out=response.getWriter();
        
        if (act.equals("getAll")) {
            String json = mapper.writeValueAsString(productService.getAll());
            out.append(json);
        } else if (act.equals("area")) {
            String callback=request.getParameter("callback");
            out.append(callback+"('"+new Date()+"')");
        } else if (act.equals("getJSONP")) {
            String callback=request.getParameter("callback");
            String json = mapper.writeValueAsString(productService.getAll());
            out.append(callback+"("+json+")");
        } 
    }

    protected void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        doGet(request, response);
    }
}

客戶端:

<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <title>AJAX</title>
    </head>
    <body>
        <script src="js/jQuery1.11.3/jquery-1.11.3.min.js" type="text/javascript" charset="utf-8"></script>
        <script type="text/javascript">
            $.get("http://localhost:8087/JavaScript001/Product?callback=?",{act:"getJSONP"},function(data){
                $.each(data,function(index,obj){
                    $("<p/>").html(obj.name).appendTo("body");
                });
            },"jsonp");
        </script>
    </body>
</html>

運行結果:

在jQuery中若是使用JSONP只須要將返回數據類型設置爲jsonp就能夠了,可是這種方法只支持get請求,不支持post請求;請求是同步的;服務器返回數據要處理,要添加回調函數,麻煩。

3.四、跨域資源共享(CORS)

跨域資源共享(CORS)是一種網絡瀏覽器的技術規範,它爲Web服務器定義了一種方式,容許網頁從不一樣的域訪問其資源。
CORS與JSONP相比:
一、 JSONP只能實現GET請求,而CORS支持全部類型的HTTP請求。
二、 使用CORS,開發者可使用普通的XMLHttpRequest發起請求和得到數據,比起JSONP有更好的錯誤處理。
三、 JSONP主要被老的瀏覽器支持,它們每每不支持CORS,而絕大多數現代瀏覽器都已經支持了CORS。

CORS有兩種模型能夠實現:

1.簡單模型

支持get/post/put/delete請求,例如返回Access-Control-Allow-Origin:*,可是不容許自定義header且會忽略cookies,且post數據格式有限制,只支持‘text/plain','application/x-www-urlencoded'and'multipart/form-data',其中’text/plain'默認支持,後面兩種須要下面的預檢請求和服務器協商。

2.協商模型/預檢請求(Preflighted Request)
舉例:瀏覽器發出PUT請求,OPTION請求返回Access-Control-Allow-Origin:*,Access-Control-Allow-Methods:’PUT’,服務器贊成全部域的PUT請求,瀏覽器收到並繼續發出真正的PUT請求,服務器響應並再次返回Access-Control-Allow-Origin:*,容許瀏覽器的腳本執行服務器返回的數據。

response.addHeader("Access-Control-Allow-Origin","http://www.company.com");

Access-Control-Allow-Origin:容許跨域訪問的域名,若是是公共的則返回*便可

response.addHeader("Access-Control-Allow-Methods","GET,POST,OPTIONS");

Access-Control-Allow-Methods:容許跨域訪問的謂詞(方法),如GET,POST,DELETE,PUT(REST)

 .Net服務器通常處理程序代碼:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;

namespace jQuery601_DotNet.Action
{
    /// <summary>
    /// FindUserById 的摘要說明
    /// </summary>
    public class FindUserById : IHttpHandler
    {

        public void ProcessRequest(HttpContext context)
        {
            context.Response.ContentType = "text/plain";
            context.Response.Headers.Add("Access-Control-Allow-Origin","*");
            context.Response.Headers.Add("Access-Control-Allow-Methods", "GET,POST");
            String name = "";
            int id = Convert.ToInt32(context.Request.Params["id"]);
            if (id == 1001)
            {
                name = "Mark";
            }
            else if (id == 1002)
            {
                name = "Jack";
            }
            context.Response.Write(name);
        }

        public bool IsReusable
        {
            get
            {
                return false;
            }
        }
    }
}

 客戶端腳本:

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>6.4.九、跨域AJAX請求</title>
</head>
<body>
    <h2>6.4.九、跨域AJAX請求</h2>
    <h2 id="message"></h2>
    <button type="button" id="btnAjax">ajax請求</button>
    <script type="text/javascript" src="js/jQuery/jquery.min.js"></script>
    <script type="text/javascript">
        $("#btnAjax").click(function() {
            $.get("http://localhost:12833/Action/FindUserById.ashx",{"id":1001},function(data){
                log(data);
            },"text");
        });

        function log(msg) {
            $("#message")[0].innerHTML += msg + "<br/>";
        }
    </script>
</body>
</html>

運行結果:

從上圖能夠看到實現跨域且爲異步請求。

Java Servlet後臺腳本:

package com.gomall.action;

import java.io.IOException;
import java.io.PrintWriter;
import java.util.Date;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.codehaus.jackson.map.ObjectMapper;

import com.gomall.service.IProductService;
import com.gomall.service.ProductService;

@WebServlet("/Product")
public class Product extends HttpServlet {
    private static final long serialVersionUID = 1L;

    public Product() {
        super();
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        request.setCharacterEncoding("utf-8");
        response.setCharacterEncoding("utf-8");
        String act = request.getParameter("act");
        IProductService productService = new ProductService();
        ObjectMapper mapper = new ObjectMapper();
        PrintWriter out=response.getWriter();
        
        if (act.equals("getAll")) {
            String json = mapper.writeValueAsString(productService.getAll());
            out.append(json);
        } else if (act.equals("area")) {
            String callback=request.getParameter("callback");
            out.append(callback+"('"+new Date()+"')");
        } else if (act.equals("getJSONP")) {
            String callback=request.getParameter("callback");
            String json = mapper.writeValueAsString(productService.getAll());
            out.append(callback+"("+json+")");
        } else if (act.equals("getAllCORS")) {
            /**向響應的頭部中添加內容*/
            response.addHeader("Access-Control-Allow-Origin", "*");
            response.addHeader("Access-Control-Allow-Methods", "GET,POST");
            
            String json = mapper.writeValueAsString(productService.getAll());
            out.append(json);
        } 
    }

    protected void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        doGet(request, response);
    }
}

客戶端代碼:

<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <title>AJAX</title>
    </head>
    <body>
        <script src="js/jQuery1.11.3/jquery-1.11.3.min.js" type="text/javascript" charset="utf-8"></script>
        <script type="text/javascript">
            $.get("http://localhost:8087/JavaScript001/Product?act=getAllCORS",function(data){
                alert(data);
            });
        </script>
    </body>
</html>

運行結果:

 

 2.四、CORS跨域的問題

a)、若是認爲每次須要修改HTTP頭部比較麻煩,在java中可使用過濾器,.Net可使用Module或HttpHandler全局註冊(註冊到Web.Config中,部署時還須要注意)。

b)、若是須要考慮IE8實現CORS則要插件支持,由於IE8並無徹底支持CORS。

插件名稱:javascript-jquery-transport-xdr

github: https://github.com/gfdev/javascript-jquery-transport-xdr

示例代碼:

<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <title>AJAX</title>
    </head>
    <body>
        <script src="js/jQuery1.11.3/jquery-1.11.3.min.js" type="text/javascript" charset="utf-8"></script>
        <!--[if (IE 8)|(IE 9)]>
        <script src="js/jquery.transport.xdr.min.js" type="text/javascript" charset="utf-8"></script>
        <![endif]-->

        <script type="text/javascript">
            $.get("http://localhost:8087/JavaScript001/Product?act=getAllCORS&type=meat-and-filler&format=json",{},function(data){
                alert(data);
            },"json");
        </script>
    </body>
</html>

運行結果:

c)、Apache官方提供一個支持CORS跨域的過濾器,詳細說明: http://tomcat.apache.org/tomcat-7.0-doc/config/filter.html

3.五、小結

固然除了兼容老瀏覽器的jsonp跨域與HTML5中的CORS跨域還有不少其它辦法如利用iframe和location.hash、window.name實現的跨域數據傳輸、使用HTML5 postMessage、利用flash等辦法。我的認爲CORS應該纔是將來主要的跨域選擇,其它的方法都只是hack。

4、彈出層

前面AJAX示例中添加功能若是放在一個彈出層中佈局會更加緊湊一些,像登陸,提示信息常常會須要彈出層。

常見的彈出層有:FancyBox,LightBox,colorBox,artDialog,BlockUI,Layer等,這裏介紹騰訊開源的artDialog,輕量,實用。

artDialog是一個設計得十分巧妙的對話框組件,小巧身材卻擁有豐富的接口與漂亮的外觀。

特色是自適應內容、優雅的接口、細緻的體驗、跨平臺兼容、輕量實用。

項目源碼: https://github.com/aui/artDialog

幫助信息: http://img0.zz91.com/huanbao/mblog/artDialog-5.0.4

文檔與示例: http://aui.github.io/artDialog/doc/index.html

AngularJS 版本: https://github.com/aui/angular-popups

使用方法:

<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <title>artDialog</title>
    </head>
    <body>
        <button onclick="btn_dialog()">
            彈出框
        </button>
        <button onclick="btn_loading()">
            加載中
        </button>
        <script src="js/jQuery1.11.3/jquery-1.11.3.js" type="text/javascript" charset="utf-8"></script>
        <script src="js/artDialog6/dialog-min.js" type="text/javascript" charset="utf-8"></script>
        <link rel="stylesheet" type="text/css" href="js/artDialog6/ui-dialog.css" />
        <script type="text/javascript">
            function btn_dialog() {
                var d = dialog({
                    title: '消息',
                    content: '風吹起的青色衣衫,夕陽裏的溫暖容顏,你比之前更加美麗,像怒放的花<br>——許巍《難忘的一天》',
                    okValue: '確 定',
                    ok: function() {
                        var that = this;
                        setTimeout(function() {
                            that.title('提交中..');
                        }, 2000);
                        return false;
                    },
                    cancelValue: '取消',
                    cancel: function() {
                        alert('你點了取消按鈕')
                    }
                });

                d.show();
            }
            
            function btn_loading(){
                dialog({
                    modal:true
                }).show();
            }
        </script>
    </body>

</html>

運行結果:

屬性:

 // 對齊方式
    //align: 'bottom left',
    
    // 是否固定定位
    //fixed: false,
    
    // 對話框疊加高度值(重要:此值不能超過瀏覽器最大限制)
    //zIndex: 1024,

    // 設置遮罩背景顏色
    backdropBackground: '#000',

    // 設置遮罩透明度
    backdropOpacity: 0.7,

    // 消息內容
    content: '<span class="ui-dialog-loading">Loading..</span>',
    
    // 標題
    title: '',

    // 對話框狀態欄區域 HTML 代碼
    statusbar: '',
    
    // 自定義按鈕
    button: null,
    
    // 肯定按鈕回調函數
    ok: null,
    
    // 取消按鈕回調函數
    cancel: null,

    // 肯定按鈕文本
    okValue: 'ok',
    
    // 取消按鈕文本
    cancelValue: 'cancel',

    cancelDisplay: true,
    
    // 內容寬度
    width: '',
    
    // 內容高度
    height: '',
    
    // 內容與邊界填充距離
    padding: '',
    
    // 對話框自定義 className
    skin: '',

    // 是否支持快捷關閉(點擊遮罩層自動關閉)
    quickClose: false,

    // css 文件路徑,留空則不會使用 js 自動加載樣式
    // 注意:css 只容許加載一個
    cssUri: '../css/ui-dialog.css',

事件:

/**
     * 顯示對話框
     * @name artDialog.prototype.show
     * @param   {HTMLElement Object, Event Object}  指定位置(可選)
     */
    
    /**
     * 顯示對話框(模態)
     * @name artDialog.prototype.showModal
     * @param   {HTMLElement Object, Event Object}  指定位置(可選)
     */

    /**
     * 關閉對話框
     * @name artDialog.prototype.close
     * @param   {String, Number}    返回值,可被 onclose 事件收取(可選)
     */

    /**
     * 銷燬對話框
     * @name artDialog.prototype.remove
     */

    /**
     * 重置對話框位置
     * @name artDialog.prototype.reset
     */

    /**
     * 讓對話框聚焦(同時置頂)
     * @name artDialog.prototype.focus
     */

    /**
     * 讓對話框失焦(同時置頂)
     * @name artDialog.prototype.blur
     */

    /**
     * 添加事件
     * @param   {String}    事件類型
     * @param   {Function}  監聽函數
     * @name artDialog.prototype.addEventListener
     */

    /**
     * 刪除事件
     * @param   {String}    事件類型
     * @param   {Function}  監聽函數
     * @name artDialog.prototype.removeEventListener
     */

    /**
     * 對話框顯示事件,在 show()、showModal() 執行
     * @name artDialog.prototype.onshow
     * @event
     */

    /**
     * 關閉事件,在 close() 執行
     * @name artDialog.prototype.onclose
     * @event
     */

    /**
     * 銷燬前事件,在 remove() 前執行
     * @name artDialog.prototype.onbeforeremove
     * @event
     */

    /**
     * 銷燬事件,在 remove() 執行
     * @name artDialog.prototype.onremove
     * @event
     */

    /**
     * 重置事件,在 reset() 執行
     * @name artDialog.prototype.onreset
     * @event
     */

    /**
     * 焦點事件,在 foucs() 執行
     * @name artDialog.prototype.onfocus
     * @event
     */

    /**
     * 失焦事件,在 blur() 執行
     * @name artDialog.prototype.onblur
     * @event
     */

該插件使用比較簡單,能夠看示例與源代碼。

5、模板引擎

在AJAX示例中javascript中有大量的html字符串,html中有一些像onclick樣的javascript,這樣javascript中有html,html中有javascript,代碼的偶合度很高,不便於修改與維護,使用模板引擎能夠解決問題。

模板引擎(這裏特指用於Web開發的模板引擎)是爲了使用戶界面與業務數據(內容)分離而產生的,它能夠生成特定格式的文檔,用於網站的模板引擎就會生成一個標準的HTML文檔。先後端都有模板引擎,好比T四、FreeMarker、Velocity,這裏主要講前端模板引擎:

上圖是常見的一些前端模板引擎,速度相對快的是artTemplate,與artDialog是同一個做者,固然一個好的模板引擎不只是速度還有不少方面都關鍵。

源碼與幫助: https://github.com/aui/artTemplate

5.一、Hello World

示例代碼:

<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <title>artTemplate</title>
    </head>
    <body>
        <div id="result">
        </div>
        <script src="js/artTemplate3/template.js" type="text/javascript" charset="utf-8"></script>
        <script type="text/html" id="template1">
            {{if isShow}}
              <h2>姓名:{{name}}</h2>
              <ul>
              {{each hobbies as hobby index}}
                 <li>
                     {{index+1}} {{hobby}}
                 </li>
              {{/each}}
              </ul>
            {{/if}}
        </script>
        <script type="text/javascript">
            var data={
                isShow:true,
                name:"Tom",
                hobbies:["看書","上網","運動","電影","購物"]
            };
            //用數據與模板渲染(render)出結果
            var html=template("template1",data);
            document.getElementById("result").innerHTML=html;
        </script>
    </body>
</html>

運行結果:

生成的代碼:

<h2>姓名:Tom</h2>
<ul>
    <li>
        1 看書
    </li>
    <li>
        2 上網
    </li>
    <li>
        3 運動
    </li>
    <li>
        4 電影
    </li>
    <li>
        5 購物
    </li>
</ul>

5.二、方法

1)、template(id, data)

根據 id 渲染模板。內部會根據document.getElementById(id)查找模板。

若是沒有 data 參數,那麼將返回一渲染函數。

2)、template.compile(source, options)

將返回一個渲染函數。演示

3)、template.render(source, options)

將返回渲染結果。

4)、template.helper(name, callback)

添加輔助方法,讓模板引擎調用自定義的javascript方法。

5)、template.config(name, value)

示例代碼:

<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <title>artTemplate</title>
    </head>
    <body>
        <div id="result">
        </div>
        <script src="js/artTemplate3/template.js" type="text/javascript" charset="utf-8"></script>
        <script type="text/html" id="template1">
            $$if isShow##
              <h2>姓名:$$name##</h2>
              <ul>
                  $$include "template2"##  <!--包含模板2-->
              </ul>
            $$/if##
        </script>
        
        <script type="text/html" id="template2">
            $$each hobbies as hobby index##
                 <li>
                     $$index+1## $$#hobby##  <!--默認會轉義,加#號不轉義-->
                 </li>
              $$/each##
        </script>
        <script type="text/javascript">
            var data={
                isShow:true,
                name:"Tom",
                hobbies:["看書","上網","運動","<b>電影</b>","<i>購物</i>"]
            };
            //邏輯語法開始標籤
            template.config("openTag","$$");
            //邏輯語法結束標籤
            template.config("closeTag","##");
            //不轉義
            template.config("escape",false);
            //用數據與模板渲染(render)出結果
            var html=template("template1",data);            
            document.getElementById("result").innerHTML=html;
        </script>
    </body>
</html>

運行結果:

 

5.三、與AJAX結合應用

示例腳本:

<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <title>商品管理</title>
        <meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no" />
        <style type="text/css">
            @CHARSET "UTF-8";
            * {
                margin: 0;
                padding: 0;
                font-family: microsoft yahei;
                font-size: 14px;
            }
            
            body {
                padding-top: 20px;
            }
            
            .main {
                width: 90%;
                margin: 0 auto;
                border: 1px solid #777;
                padding: 20px;
            }
            
            .main .title {
                font-size: 20px;
                font-weight: normal;
                border-bottom: 1px solid #ccc;
                margin-bottom: 15px;
                padding-bottom: 5px;
                color: blue;
            }
            
            .main .title span {
                display: inline-block;
                font-size: 20px;
                background: blue;
                color: #fff;
                padding: 0 8px;
                background: blue;
            }
            
            a {
                color: blue;
                text-decoration: none;
            }
            
            a:hover {
                color: orangered;
            }
            
            .tab td,
            .tab,
            .tab th {
                border: 1px solid #777;
                border-collapse: collapse;
            }
            
            .tab td,
            .tab th {
                line-height: 26px;
                height: 26px;
                padding-left: 5px;
            }
            
            .abtn {
                display: inline-block;
                height: 20px;
                line-height: 20px;
                background: blue;
                color: #fff;
                padding: 0 5px;
            }
            
            .btn {
                height: 20px;
                line-height: 20px;
                background: blue;
                color: #fff;
                padding: 0 8px;
                border: 0;
            }
            
            .abtn:hover,
            .btn:hover {
                background: orangered;
                color: #fff;
            }
            
            p {
                padding: 5px 0;
            }
            
            fieldset {
                border: 1px solid #ccc;
                padding: 5px 10px;
            }
            
            fieldset legend {
                margin-left: 10px;
                font-size: 16px;
            }
            
            .pic {
                height: 30px;
                width: auto;
            }
            
            #divFrom {
                display: none;
            }
        </style>
    </head>

    <body>

        <div class="main">
            <h2 class="title"><span>商品管理</span></h2>
            <table border="1" width="100%" class="tab" id="tabGoods">
                <tr>
                    <th>編號</th>
                    <th>圖片</th>
                    <th>商品名</th>
                    <th>價格</th>
                    <th>詳細</th>
                    <th>操做</th>
                </tr>
            </table>
            <p style="color: red" id="message"></p>
            <p>
                <a href="#" class="abtn" id="btnSave">添加</a>
                <input type="submit" value="刪除選擇項" class="btn" />
            </p>
            <div id="divFrom">
                <form id="formPdt">
                    <fieldset>
                        <legend>添加商品</legend>
                        <p>
                            <label for="name">
                    名稱:
                </label>
                            <input type="text" name="name" id="name" />
                        </p>
                        <p>
                            <label for="price">
                    價格:
                </label>
                            <input type="text" name="price" id="price" />
                        </p>
                        <p>
                            <label for="detail">
                    詳細:
                      </label>
                            <textarea id="detail" name="detail" cols="60"></textarea>
                        </p>
                    </fieldset>
                </form>
            </div>
        </div>

        <link rel="stylesheet" type="text/css" href="js/artDialog6/ui-dialog.css" />
        <script src="js/artTemplate3/template.js" type="text/javascript" charset="utf-8"></script>
        <script src="js/jQuery1.11.3/jquery-1.11.3.min.js" type="text/javascript" charset="utf-8"></script>
        <script src="js/artDialog6/dialog-min.js" type="text/javascript" charset="utf-8"></script>
        <!--[if (IE 8)|(IE 9)]>
        <script src="js/jquery.transport.xdr.min.js" type="text/javascript" charset="utf-8"></script>
        <![endif]-->
        <script type="text/html" id="tmpl">
            {{each list as pdt}}
            <tr>
                <td>{{pdt.id}}</td>
                <td><img src="http://localhost:8087/JavaScript001/images/{{pdt.picture}}" class="pic"></td>
                <td>{{pdt.name}}</td>
                <td>{{pdt.price | round:''}}</td>
                <td>{{pdt.detail}}</td>
                <td>
                    <a href="#" class="abtn del" data-id={{pdt.id}}>刪除</a>
                </td>
            </tr>
            {{/each}}
        </script>
        <script type="text/javascript">
            var app = {
                url: "http://localhost:8087/JavaScript001/", //提供服務的域名
                add: function() {
                    var d = dialog({
                        title: '添加商品',
                        content: $('#divFrom').html(),
                        okValue: '添加',
                        modal: true,
                        backdropOpacity: 0.3,
                        ok: function() {
                            var that = this;
                            $.ajax({
                                type: "post",
                                data: $(".ui-dialog #formPdt").serialize() + "&act=add",
                                success: function(data) {
                                    if(data) {
                                        app.log("添加成功!");
                                        app.loadAll();
                                        that.close();
                                    } else {
                                        app.log("添加失敗!");
                                    }
                                }
                            });
                            return false;
                        },
                        cancelValue: '關閉',
                        cancel: function() {
                            alert('你點了取消按鈕')
                        },
                        onclose: function() {
                            alert("關閉了");
                        }
                    });

                    d.show();
                },
                del: function() {
                    id = $(this).data("id");
                    var that = $(this);
                    $.ajax({
                        type: "get",
                        data: {
                            "id": id,
                            "act": "del"
                        },
                        success: function(data) {
                            if(data) {
                                that.closest("tr").remove();
                                app.log("刪除成功!");
                            } else {
                                app.log("刪除失敗!");
                            }
                        }
                    });
                },
                loadAll: function() {
                    $.ajax({
                        type: "get",
                        data: {
                            "act": "getAllCORS"
                        },
                        success: function(data) {
                            $("#tabGoods tr:gt(0)").remove();
                            $("#tabGoods").append(template("tmpl",{list:data}));
                        }
                    });
                },
                init: function() {
                    /*動態綁定刪除事件*/
                    $("#tabGoods").on("click", "a.del", {}, app.del);
                    /*綁定添加事件*/
                    $("#btnSave").click(app.add);
                    /*設置全局AJAX默認值*/
                    $.ajaxSetup({
                        dataType: "json",
                        url: app.url + "Product?type=meat-and-filler&format=json",
                        beforeSend: app.ajaxBefore,
                        complete: app.clearMsg,
                        error: function(xhr, textStatus, errorThrown) {
                            app.log("錯誤" + textStatus + errorThrown);
                        }
                    });
                    
                    //爲模板引擎定義輔助函數
                    template.helper("round",function(value,mark){
                        return (mark||"")+Math.round(value);
                    });
                    
                    this.loadAll();
                },
                clearMsg: function() {
                    this.box.remove();
                },
                ajaxBefore: function() {
                    this.box = dialog({
                        modal: true
                    });
                    this.box.show();
                },
                log: function(msg) {
                    $("#message").html(msg);
                }
            };

            app.init();
        </script>
    </body>
</html>

運行結果:

6、示例下載

coding: https://coding.net/u/zhangguo5/p/javascript001/git

服務器: https://coding.net/u/zhangguo5/p/javascript001_java/git

github: https://github.com/zhangguo5/javascript01

參照:http://www.cnblogs.com/best/

相關文章
相關標籤/搜索