目錄
一、AJAX示例
1.1、優點
1.2、缺點
1.3、jQuery AJAX示例
二、延遲對象(Deferred)
2.1、回調函數
2.2、deferred.done
三、跨域
3.1、什麼是跨域
3.2、JSONP跨域
3.3、jQuery使用JSONP跨域
3.4、跨域資源共享(CORS)
3.5、小結
四、彈出層
五、模板引擎
六、示例下載
一、AJAX示例
AJAX全稱為“Asynchronous JavaScript And XML”(異步JavaScript和XML) 是指一種創建交互式網頁應用的開發技術、改善用戶體驗,實現無刷新效果。
1.1、優點
不需要插件支持
優秀的用戶體驗
提高Web程序的性能
減輕服務器和帶寬的負擔
1.2、缺點
浏覽器對XMLHttpRequest對象的支持度不足,幾乎所有浏覽器現在都支持
破壞浏覽器“前進”、“後退”按鈕的正常功能,可以通過簡單的插件彌補
對搜索引擎的支持不足
1.3、jQuery AJAX示例
在HTML5中對原生的AJAX核心對象XMLHttpRequest進行升級,也就是XHR2,功能更加強大。
jQuery對AJAX封裝的非常好,這裡以簡單的商品管理為示例使用jQuery完成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); } }
運行結果:
刪除:
二、延遲對象(Deferred)
deferred對象就是jQuery1.5版以後新增加的回調函數解決方案。
2.1、回調函數
先看一個示例:
首先,為什麼要使用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.2、deferred.done
$.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>
運行結果:
2.3、deferred.fail
語法: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>
運行結果:
2.4、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.5、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.6、應用延遲對象
前面的示例中我們都是使用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.7、總結
(1) 生成一個對象。
(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(),最後總是執行。
三、跨域
互聯網上的主機由IP來標識,為了方便記憶,創建了域名系統.域名與IP對應,域名的作用是不用讓你記復雜的IP地址,能唯一定位資源,URL的格式是協議://主機名.公司名稱.機構類型.地域類型:端口/路徑,如http://www.zhangguo.com.cn:8080/products/list.do?id=1#a1
3.1、什麼是跨域
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.9、跨域AJAX請求</title> </head> <body> <h2>6.4.9、跨域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.2、JSONP跨域
JSONP跨域是利用script腳本允許引用不同域下的js實現的,將回調方法帶入服務器,返回結果時回調。
2.1、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.3、jQuery使用JSONP跨域
在jQuery中內置了實現JSONP跨域的功能,如果指定為json類型,則會把獲取到的數據作為一個JavaScript對象來解析,並且把構建好的對象作為結果返回。為了實現這個目的,他首先嘗試使用JSON.parse()。如果浏覽器不支持,則使用一個函數來構建。JSON數據是一種能很方便通過JavaScript解析的結構化數據。如果獲取的數據文件存放在遠程服務器上(域名不同,也就是跨域獲取數據),則需要使用jsonp類型。使用這種類型的話,會創建一個查詢字符串參數 callback=? ,這個參數會加在請求的URL後面。服務器端應當在JSON數據前加上回調函數名,以便完成一個有效的JSONP請求。如果要指定回調函數的參數名來取代默認的callback,可以通過設置$.ajax()的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>
服務器一般處理程序:
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.4、跨域資源共享(CORS)
跨域資源共享(CORS)是一種網絡浏覽器的技術規范,它為Web服務器定義了一種方式,允許網頁從不同的域訪問其資源。
CORS與JSONP相比:
1、 JSONP只能實現GET請求,而CORS支持所有類型的HTTP請求。
2、 使用CORS,開發者可以使用普通的XMLHttpRequest發起請求和獲得數據,比起JSONP有更好的錯誤處理。
3、 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.9、跨域AJAX請求</title> </head> <body> <h2>6.4.9、跨域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.4、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.5、小結
當然除了兼容老浏覽器的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文檔。前後端都有模板引擎,比如T4、FreeMarker、Velocity,這裡主要講前端模板引擎:
上圖是常見的一些前端模板引擎,速度相對快的是artTemplate,與artDialog是同一個作者,當然一個好的模板引擎不僅是速度還有很多方面都關鍵。
源碼與幫助: https://github.com/aui/artTemplate
5.1、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> 看書 </li> <li> 上網 </li> <li> 運動 </li> <li> 電影 </li> <li> 購物 </li> </ul>
5.2、方法
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.3、與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>
運行結果:
六、示例下載
coding: http://wd.jb51.net:81//201612/yuanma/zhangguo5-javascript001-master_jb51.rar
服務器: http://wd.jb51.net:81//201612/yuanma/zhangguo5-javascript001_java-master_jb51.rar
github: http://wd.jb51.net:81//201612/yuanma/javascript01-master_jb51.rar
以上就是本文的全部內容,希望本文的內容對大家的學習或者工作能帶來一定的幫助,同時也希望多多支持!