AJAX全稱爲「Asynchronous JavaScript And XML」(異步JavaScript和XML) 是指一種建立交互式網頁應用的開發技術、改善用戶體驗,實現無刷新效果。javascript
不須要插件支持css
優秀的用戶體驗html
提升Web程序的性能前端
減輕服務器和帶寬的負擔java
瀏覽器對XMLHttpRequest對象的支持度不足,幾乎全部瀏覽器如今都支持jquery
破壞瀏覽器「前進」、「後退」按鈕的正常功能,能夠經過簡單的插件彌補git
對搜索引擎的支持不足github
在HTML5中對原生的AJAX核心對象XMLHttpRequest進行升級,也就是XHR2,功能更增強大。 web
jQuery對AJAX封裝的很是好,這裏以簡單的商品管理爲示例使用jQuery完成AJAX應用。ajax
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); } }
客戶端跨域調用:
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>AJAX</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/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/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() { //closest離當前元素最近的td父元素 id = $(this).closest("td").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(); $.each(data, function(index, obj) { var tr = $("<tr/>"); //行 $("<td/>").html(obj.id).appendTo(tr); //編號 var imgTd = $("<td/>"); $("<img/>", { "src": app.url + "images/" + obj.picture, "class": "pic" }).appendTo(imgTd); //圖片 imgTd.appendTo(tr); $("<td/>").html(obj.name).appendTo(tr); $("<td/>").html(Math.ceil(obj.price)).appendTo(tr); $("<td/>").html(obj.detail).appendTo(tr); $("<td/>").html("<a href='#' class='abtn del'>刪除</a>").data("id", obj.id).appendTo(tr); $("#tabGoods").append(tr); }); } }); }, 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); } }); 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>
運行結果:
刪除:
deferred對象就是jQuery1.5版之後新增長的回調函數解決方案。
先看一個示例:
首先,爲何要使用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),能夠解決這個問題。
$.ajax()操做完成後,若是使用的是低於1.5.0版本的jQuery,返回的是XHR對象,你無法進行鏈式操做;若是高於1.5版,返回的是deferred對象,能夠進行鏈式操做。
當延遲成功時調用一個函數或者數組函數,功能與原success相似。
語法:deferred.done(doneCallbacks[,doneCallbacks])
返回值:Deferred Object
該參數能夠是一個函數或一個函數的數組。當延遲成功時,doneCallbacks被調用。回調執行是依照他們添加的順序。一旦deferred.done()返回延遲對象,延遲對象的其它方法也能夠連接到了這裏,包括增長.done()方法。當延遲解決,doneCallbacks執行使用參數提供給 resolve或 resolveWith方法依照添加的順序調用。
示例代碼:
<!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>
運行結果:
語法:deferred.fail(failCallbacks[,failCallbacks])
返回值:Deferred Object
當延遲失敗時調用一個函數或者數組函數,功能與原回調方法error相似。
該參數能夠是一個函數或一個函數的數組。當延遲失敗時,doneCallbacks被調用。回調執行是依照他們添加的順序。一旦deferred.fail()返回延遲對象,延遲對象的其它方法也能夠連接到了這裏,包括增長.done()方法。當延遲解決,doneCallbacks執行使用參數提供給 resolve或 resolveWith方法依照添加的順序調用。
示例:
<!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>
運行結果:
語法: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>
運行結果
成功時:
失敗時:
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>
結果:
前面的示例中咱們都是使用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:
結果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() 爲多個操做指定回調函數。
(8)deferred.then() 有時爲了省事,能夠把done()和fail()合在一塊兒寫,這就是then()方法。若是then()有兩個參數,那麼第一個參數是done()方法的回調函數,第二個參數是fail()方法的回調方法。若是then()只有一個參數,那麼等同於done()。
(9)deferred.always() 這個方法也是用來指定回調函數的,它的做用是,無論調用的是deferred.resolve()仍是deferred.reject(),最後老是執行。
互聯網上的主機由IP來標識,爲了方便記憶,建立了域名系統.域名與IP對應,域名的做用是不用讓你記複雜的IP地址,能惟必定位資源,URL的格式是協議://主機名.公司名稱.機構類型.地域類型:端口/路徑,如http://www.zhangguo.com.cn:8080/products/list.do?id=1#a1
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; } } } }
運行結果:
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對象,而是同步調用。
在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請求;請求是同步的;服務器返回數據要處理,要添加回調函數,麻煩。
同源策略(same origin policy)的限制下非同源的網站之間不能發送 ajax 請求的。
w3c 提出了跨源資源共享CORS即Cross Origin Resource Sharing(跨來源資源共享),就是咱們所熟知的跨域請求。
跨域資源共享(CORS)是一種網絡瀏覽器的技術規範,它爲Web服務器定義了一種方式,容許網頁從不一樣的域訪問其資源。
CORS與JSONP相比:
一、 JSONP只能實現GET請求,而CORS支持全部類型的HTTP請求。
二、 使用CORS,開發者可使用普通的XMLHttpRequest發起請求和得到數據,比起JSONP有更好的錯誤處理。
三、 JSONP主要被老的瀏覽器支持,它們每每不支持CORS,而絕大多數現代瀏覽器都已經支持了CORS。
CORS 將請求分爲兩類:簡單請求和非簡單請求:
支持get/post/put/delete請求,例如返回Access-Control-Allow-Origin:*,可是不容許自定義header且會忽略cookies,且post數據格式有限制,只支持‘text/plain','application/x-www-urlencoded'and'multipart/form-data',其中’text/plain'默認支持,後面兩種須要下面的預檢請求和服務器協商。
簡單請求對應該規則,所以對簡單請求的定義爲:
(1) 請求方法是如下三種方法之一:
HEAD GET POST
(2)HTTP的頭信息不超出如下幾種字段:
Accept Accept-Language Content-Language Last-Event-ID Content-Type:只限於三個值application/x-www-form-urlencoded、multipart/form-data、text/plain
簡單請求的部分響應頭及解釋以下:
Access-Control-Allow-Origin(必含)- 不可省略,不然請求按失敗處理。該項控制數據的可見範圍,若是但願數據對任何人均可見,能夠填寫"*"。
Access-Control-Allow-Credentials(可選) – 該項標誌着請求當中是否包含cookies信息,只有一個可選值:true(必爲小寫)。若是不包含cookies,請略去該項,而不是填寫false。這一項與XmlHttpRequest2對象當中的withCredentials屬性應保持一致,即withCredentials爲true時該項也爲true;withCredentials爲false時,省略該項不寫。反之則致使請求失敗。
Access-Control-Expose-Headers(可選) – 該項肯定XmlHttpRequest2對象當中getResponseHeader()方法所能得到的額外信息。一般狀況下,getResponseHeader()方法只能得到以下的信息:
Cache-Control Content-Language Content-Type Expires Last-Modified Pragma
當你須要訪問額外的信息時,就須要在這一項當中填寫並以逗號進行分隔
示例:
服務器端:
package com.zhangguo.springmvc08.action; 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 java.io.IOException; @WebServlet(name = "ProductServlet",value = "/pdt") public class ProductServlet extends HttpServlet { protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doGet(request,response); } protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { //設置容許CORS的域名,若是是全部則使用* response.addHeader("Access-Control-Allow-Origin","*"); response.getWriter().write("{\"name\":\"Book\"}"); } }
客戶端:
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>跨域</title> </head> <body> <script src="../js/jquery-1.11.3.min.js" type="text/javascript" charset="utf-8"></script> <script type="text/javascript"> //跨域Get請求 $.get("http://localhost:8080/mvc08/pdt", function(data) { console.log("Get:"+data.name); }, "json"); //跨域Post請求 $.post("http://localhost:8080/mvc08/pdt", function(data) { console.log("Post:"+data.name); }, "json"); </script> </body> </html>
結果:
如說你須要發送PUT、DELETE等HTTP動做,或者發送Content-Type: application/json的內容就須要使用複雜請求了。
最早發送的是一種"預請求",此時做爲服務端,也須要返回"預迴應"做爲響應。預請求其實是對服務端的一種權限請求,只有當預請求成功返回,實際請求才開始執行。預請求以OPTIONS形式發送,當中一樣包含域,而且還包含了兩項CORS特有的內容
代碼:
<script type="text/javascript"> $.ajax({ type:"PUT", url:"http://localhost:8080/mvc08/pdt", contentType:"application/json;charset=utf-8", dataType:"json", success:function(data){ console.log(data); } }); </script>
結果:
Access-Control-Allow-Origin(必含) – 和簡單請求同樣的,必須包含一個域,不能是*號。
Access-Control-Allow-Methods(必含) – 這是對預請求當中Access-Control-Request-Method的回覆,這一回復將是一個以逗號分隔的列表。儘管客戶端或許只請求某一方法,但服務端仍然能夠返回全部容許的方法,以便客戶端將其緩存。
Access-Control-Allow-Headers(當預請求中包含Access-Control-Request-Headers時必須包含) – 這是對預請求當中Access-Control-Request-Headers的回覆,和上面同樣是以逗號分隔的列表,能夠返回全部支持的頭部。
Access-Control-Allow-Credentials(可選) – 和簡單請求當中做用相同。
Access-Control-Max-Age(可選) – 以秒爲單位的緩存時間,容許時應當儘量緩存。
CORSFilter是Apache官方提供一個支持CORS跨域的過濾器,詳細說明: http://tomcat.apache.org/tomcat-7.0-doc/config/filter.html
依賴:
<!--CORS--> <dependency> <groupId>com.thetransactioncompany</groupId> <artifactId>cors-filter</artifactId> <version>2.6</version> </dependency>
添加過濾器,儘可能添加在最前面:
<filter> <filter-name>CORS</filter-name> <filter-class>com.thetransactioncompany.cors.CORSFilter</filter-class> <init-param> <param-name>cors.allowOrigin</param-name> <param-value>http://127.0.0.1:8020</param-value> </init-param> <init-param> <param-name>cors.supportedMethods</param-name> <param-value>POST,GET,OPTIONS,DELETE,PUT</param-value> </init-param> <init-param> <param-name>cors.supportedHeaders</param-name> <param-value>Content-Type,Accept,Origin,XRequestedWith,ContentType,LastModified</param-value> </init-param> <init-param> <param-name>cors.exposedHeaders</param-name> <param-value>SetCookie</param-value> </init-param> <init-param> <param-name>cors.supportsCredentials</param-name> <param-value>true</param-value> </init-param> </filter> <filter-mapping> <filter-name>CORS</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>
若是使用了Spring MVC,請開啓Spring對OPTIONS的支持:
<init-param> <param-name>dispatchOptionsRequest</param-name> <param-value>true</param-value> </init-param>
客戶端:
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>跨域</title> </head> <body> <script src="../js/jquery-1.11.3.min.js" type="text/javascript" charset="utf-8"></script> <script type="text/javascript"> $.ajax({ type:"PUT", url:"http://localhost:8080/mvc08/u", contentType:"application/json;charset=utf-8", dataType:"json", success:function(data){ console.log(data); } }); </script> </body> </html>
服務器:
package com.zhangguo.springmvc08.action; 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 java.io.IOException; import java.util.Enumeration; @WebServlet(name = "UserServlet", value = "/u") public class UserServlet extends HttpServlet { protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doGet(request, response); } protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.getWriter().write("{\"name\":\"Book\"}"); } @Override protected void doOptions(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { doGet(req, resp); } @Override protected void doPut(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { doGet(req, resp); } @Override protected void doDelete(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { doGet(req, resp); } }
結果:
web.xml
經過修改請求頭部門信息能夠實現Servlet完成複雜跨域功能,示例以下:
後臺:
package com.zhangguo.springmvc08.action; 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 java.io.IOException; import java.util.Enumeration; @WebServlet(name = "ProductServlet", value = "/pdt") public class ProductServlet extends HttpServlet { protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doGet(request, response); } protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { //得到全部頭部信息 Enumeration<String> items=request.getHeaderNames(); String headers="Content-Type,Accept,Origin,XRequestedWith,ContentType,LastModified,Content-Type,ContentType,content-type"; while(items.hasMoreElements()){ headers+=","+items.nextElement(); } //設置容許CORS的域名,若是是全部則使用* response.addHeader("Access-Control-Allow-Origin", "http://127.0.0.1:8020"); response.addHeader("Access-Control-Allow-Methods", "GET, HEAD, POST, TRACE, OPTIONS,PUT,DELETE"); response.addHeader("Access-Control-Request-Headers", "Origin,X-Requested-With,Content-Type,Accept"); response.addHeader("Access-Control-Allow-Credentials", "true"); response.getWriter().write("{\"name\":\"Book\"}"); } @Override protected void doOptions(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { resp.addHeader("Access-Control-Allow-Headers", "Content-type"); doGet(req, resp); } @Override protected void doPut(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { doGet(req, resp); } @Override protected void doDelete(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { doGet(req, resp); } }
這裏在實際使用中有遇到,全部支持的頭部一時可能不能徹底寫出來,而又不想在這一層作過多的判斷,不要緊,事實上經過request的header能夠直接取到Access-Control-Request-Headers,直接把對應的value設置到Access-Control-Allow-Headers便可
前臺:
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>跨域</title> </head> <body> <script src="../js/jquery-1.11.3.min.js" type="text/javascript" charset="utf-8"></script> <script type="text/javascript"> $.ajax({ type:"PUT", url:"http://localhost:8080/mvc08/pdt", contentType:"application/json;charset=utf-8", dataType:"json", success:function(data){ console.log(data); } }); </script> </body> </html>
結果:
Spring MVC4.2 及以上增長了對CORS的支持
一個應用可能會有多個 CORS 配置,而且能夠設置每一個 CORS 配置針對一個接口或一系列接口或者對全部接口生效。
對第一種狀況,若是想要對某一接口配置 CORS,能夠在方法上添加 CrossOrigin 註解:
@CrossOrigin(origins = {"http://localhost:9000", "null"}) @RequestMapping(value = "/test", method = RequestMethod.GET) public String greetings() { return "{\"project\":\"just a test\"}"; }
第二種狀況,若是想對一系列接口添加 CORS 配置,能夠在類上添加註解,對該類聲明全部接口都有效:
@CrossOrigin(origins = {"http://localhost:8080", "null"}) @RestController public class HomeController{ }
第三種狀況,添加全局配置,則須要添加一個配置類:
@Configuration public class WebConfig extends WebMvcConfigurerAdapter { @Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping("/**") .allowedOrigins("http://localhost:9000", "null") .allowedMethods("POST", "GET", "PUT", "OPTIONS", "DELETE") .maxAge(3600) .allowCredentials(true); } }
能夠經過添加 Filter 的方式,配置 CORS 規則,並手動指定對哪些接口有效。
@Bean public FilterRegistrationBean corsFilter() { UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); CorsConfiguration config = new CorsConfiguration(); config.setAllowCredentials(true); config.addAllowedOrigin("http://localhost:9000"); config.addAllowedOrigin("null"); config.addAllowedHeader("*"); config.addAllowedMethod("*"); source.registerCorsConfiguration("/**", config); // CORS 配置對全部接口都有效 FilterRegistrationBean bean = newFilterRegistrationBean(new CorsFilter(source)); bean.setOrder(0); return bean; }
也能夠修改配置文件:
<mvc:cors> <mvc:mapping path="/**" allowed-origins="http://127.0.0.1:8020" allowed-methods="POST,GET, OPTIONS,DELETE,PUT" allowed-headers="Content-Type,ContentType,Access-Control-Allow-Headers, Authorization, X-Requested-With" allow-credentials="true"/> </mvc:cors>
員工管理的跨域綜合示例
後臺REST服務:
Spring配置文件:
前端:
運行結果:
其它跨域示例:
.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>
運行結果:
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>
運行結果:
固然除了兼容老瀏覽器的jsonp跨域與HTML5中的CORS跨域還有不少其它辦法如利用iframe和location.hash、window.name實現的跨域數據傳輸、使用HTML5 postMessage、利用flash等辦法。我的認爲CORS應該纔是將來主要的跨域選擇,其它的方法都只是hack。
前面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 */
該插件使用比較簡單,能夠看示例與源代碼。
在AJAX示例中javascript中有大量的html字符串,html中有一些像onclick樣的javascript,這樣javascript中有html,html中有javascript,代碼的偶合度很高,不便於修改與維護,使用模板引擎能夠解決問題。
模板引擎(這裏特指用於Web開發的模板引擎)是爲了使用戶界面與業務數據(內容)分離而產生的,它能夠生成特定格式的文檔,用於網站的模板引擎就會生成一個標準的HTML文檔。先後端都有模板引擎,好比T四、FreeMarker、Velocity,這裏主要講前端模板引擎:
上圖是常見的一些前端模板引擎,速度相對快的是artTemplate,與artDialog是同一個做者,固然一個好的模板引擎不只是速度還有不少方面都關鍵。
源碼與幫助: https://github.com/aui/artTemplate
示例代碼:
<!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>
當前最新的版本是4.2與3.x版本有一些區別,4.x版本的方法與選項以下:
// 模板名 filename: null, // 模板語法規則列表 rules: [nativeRule, artRule], // 是否開啓對模板輸出語句自動編碼功能。爲 false 則關閉編碼輸出功能 // escape 能夠防範 XSS 攻擊 escape: true, // 啓動模板引擎調試模式。若是爲 true: {cache:false, minimize:false, compileDebug:true} debug: detectNode ? process.env.NODE_ENV !== 'production' : false, // bail 若是爲 true,編譯錯誤與運行時錯誤都會拋出異常 bail: true, // 是否開啓緩存 cache: true, // 是否開啓壓縮。它會運行 htmlMinifier,將頁面 HTML、CSS、CSS 進行壓縮輸出 // 若是模板包含沒有閉合的 HTML 標籤,請不要打開 minimize,不然可能被 htmlMinifier 修復或過濾 minimize: true, // 是否編譯調試版 compileDebug: false, // 模板路徑轉換器 resolveFilename: resolveFilename, // 子模板編譯適配器 include: include, // HTML 壓縮器。僅在 NodeJS 環境下有效 htmlMinifier: htmlMinifier, // HTML 壓縮器配置。參見 https://github.com/kangax/html-minifier htmlMinifierOptions: { collapseWhitespace: true, minifyCSS: true, minifyJS: true, // 運行時自動合併:rules.map(rule => rule.test) ignoreCustomFragments: [] }, // 錯誤事件。僅在 bail 爲 false 時生效 onerror: onerror, // 模板文件加載器 loader: loader, // 緩存中心適配器(依賴 filename 字段) caches: caches, // 模板根目錄。若是 filename 字段不是本地路徑,則在 root 查找模板 root: '/', // 默認後綴名。若是沒有後綴名,則會自動添加 extname extname: '.art', // 忽略的變量。被模板編譯器忽略的模板變量列表 ignore: [], // 導入的模板變量 imports: runtime
方法:
與3.x版本相似,但config方法被取消,更多新查看以下連接
https://aui.github.io/art-template/zh-cn/docs/api.html
方法:
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>
運行結果:
4.x示例:
結果:
示例腳本:
<!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>
運行結果:
coding: https://coding.net/u/zhangguo5/p/javascript001/git
服務器: https://coding.net/u/zhangguo5/p/javascript001_java/git
github: https://github.com/zhangguo5/javascript01
第二版:
前端:https://git.coding.net/zhangguo5/javascript_01.git
後臺:https://git.coding.net/zhangguo5/SpringMVC08.git
http://www.bilibili.com/video/av17173253/
https://www.bilibili.com/video/av16991874/
8.一、請完成一個簡單的分佈式應用,使用Java做爲服務器對外發布服務,PC客戶端實現「品牌或商品」的管理,移動端實現以下列表:
系統結構以下:
該博客轉自@張果老師