本文主題涵蓋 XDR-Cross Domain Request , DOM Storage , Online/Offline Detecting API , AJAX Navigation
Internet Explorer 8 中的JavaScript (2)
文/黃忠成
本文主題涵蓋
1. XDR-Cross Domain Request
2. DOM Storage
3. Online/Offline Detecting API
4. AJAX Navigation
XDR-Cross Domain Request
所有人都知道,AJAX技術所依賴的XMLHttpRequest不能跨網域執行,這是受限於瀏覽器安全模型所致,以往要跨越此界限的方式有兩種,
一是透過瀏覽器的安全設定允許,二是使用JavaScript Hacking技巧,也就是JSONP的手法運行。
IE 8提供了另一種方式,那就是改用XDomainRequest物件,程式9是這樣一個例子。
程式9
XDRRequest.aspx |
<%@ Page Language="C#" AutoEventWireup="true" CodeFile="XDRRequest.aspx.cs" Inherits="XDRRequest" %> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head runat="server"> <title></title> </head> <body> <form id="form1" runat="server"> <div> <script language="javascript" type="text/javascript"> var xdr; function loadd() { alert(xdr.reponseText); } function aclick() { xdr =new XDomainRequest(); xdr.onload = function() { alert("success: " + xdr.responseText); } xdr.onerror = error; xdr.open("GET", "http://localhost:20339/HTMLInspect/XDRPage.aspx?msg=c"); xdr.send("tt"); } function error() { alert("XDR failed"); } </script> <a href="#" onclick="aclick();">Test Link</a> </div> </form> </body> </html> |
XDomainRequest用法與XMLHttpRequest相同,不同的是他可以跨網域執行,前題是要求的頁面必須包含一個特定的Header,如程式10。
程式10
XDRPage.aspx |
using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.UI; using System.Web.UI.WebControls; public partial class XDRPage : System.Web.UI.Page { protected void Page_Load(object sender, EventArgs e) { if (Request.QueryString["msg"] != null) { Response.Clear(); Response.AppendHeader("Access-Control-Allow-Origin", "http://localhost:20339"); Response.Write("i am cross-domain response"); Response.Flush(); Response.End(); } } } |
以本例來說,當啟動XDomainRequest的網域不是http://localhost:20339的話,那麼即會丟出圖15的錯誤訊息。
圖15
反之則成功跨網域。
圖16
圖17是此流程示意圖。
圖17
DOM Storage
HTML 5 中規範了DOM Storage規格,DOM Storage指的是瀏覽器提供一塊儲存空間供網頁使用,目的與Cookies類似,都是讓網頁用來儲存暫時性資料,
不過與Cookies不同,DOM Storage的容量較大(Cookies只有4KB),目前IE 8所支援的DOM Storage有兩類,一是Session Storage,儲存於此的資料會在瀏覽器
的該Tab(頁籤)關閉時被清空,空間限定為10 MB,只有該Tab內的網頁能存取到session Storage內資料。另一種是Local Storage,儲存於此的資料會一直存在,
直到使用者透過【刪除歷程記錄】為止,Local Storage內的資料可被所有Tab內網頁存取,空間限定為10 MB。程式11是使用session Storage的例子。
程式11
UseDOMStorage.htm |
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <title></title> <script language="javascript" type="text/javascript"> function putSession(value) { sessionStorage["mystate"] = value; } function loadSession() { return sessionStorage["mystate"]; } </script> </head> <body> <input type="button" value="put state" onclick="putSession(document.getElementById('txtState').value)" /> <input type="button" value="get state" onclick="document.getElementById('txtState').value = loadSession()" /> <input type="text" id="txtState" maxlength="10" /> </body> </html> |
使用DOM Storage的方式很簡單,以sessionStorage來說,只要以sessionStorage[<key>] = value方式即可將值存入,反之
則是以sessionStorage[<key>]將值取出。程式12是使用Local Storage的例子。
程式12
UseGlobalStorage.htm |
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <title></title> <script language="javascript" type="text/javascript"> function putSession(value) { localStorage["mystate"] = value; } function loadSession() { return localStorage["mystate"]; } </script> </head> <body> <input type="button" value="put state" onclick="putSession(document.getElementById('txtState').value)" /> <input type="button" value="get state" onclick="document.getElementById('txtState').value = loadSession()" /> <input type="text" id="txtState" maxlength="10" /> </body> </html> |
Online/Offline Detecting API
IE 8是少數完整呈現navigator.onLine屬性的瀏覽器,此屬性可用來偵測瀏覽器是否處於網路已連線狀態,在IE 7前,這個屬性僅能反應
使用者點選檔案選單中離線瀏覽與否的狀態,在IE 8,這個屬性已經可以正確反應網路連線狀態,因此在IE 8中,我們可以透過navigator.onLine
屬性來判斷目前是離線或是連線狀態,更甚之,IE 8還提供了online及offline事件,分別在瀏覽器於線上及離線切換時觸發(包含網路斷線及重新連線、
或是由檔案選單中選取離線工作)。
程式13
DetectingOnOffline.htm |
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html> <head> <script language="javascript" type="text/javascript"> function updateStatus() { if (navigator.onLine) { document.getElementById("state").innerHTML = "Online"; } else { document.getElementById("state").innerHTML = "Offline"; } document.body.ononline = OnLine; document.body.onoffline = OffLine; } function OnLine() { document.getElementById("state").innerHTML = "Online"; } function OffLine() { document.getElementById("state").innerHTML = "Offline"; } </script> </head> <body onload="updateStatus();"> <p id="state"></p> </body> </html> |
AJAX Navigation
AJAX技術的熱門程度,遠超過當初提出人的想像,也因為其太熱門,一些問題也慢慢的浮現,最常見的就是Back(上一頁)按紐無法運作,基本上,
瀏覽器的上一頁按紐是依據URL來產生的,但在AJAX技術下,URL是不會有變動的,所以自然上一頁按紐也無法運作了。
解決此問題的辦法有很多,多半是運用hash技巧,IE 8是少數將此功能內建的瀏覽器,IE 8在遭遇AJAX存取時,會在URL上插入一小段Tag,藉此
讓上一頁可以正常運作,雖說如此,大部份的程式碼還是要設計師自己寫,程式14是使用此技巧的例子。
程式14
JQueryNavigator.htm |
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <title></title> <script language="javascript" type="text/javascript" src="jquery-1.3.2.min.js"></script> <script language="javascript" type="text/javascript"> var currentAction = -1; var sysAction = false; function OnHashChanged() { if (sysAction) { sysAction = false; return; } var hashAction = Number(document.location.hash.substr(7)); this.goToPage(hashAction); } function goToPage(hashAction) { $.get("AjaxService.aspx?actionID=Action" + hashAction.toString(),HandleBackCallback); } function DoAction() { sysAction = true; $.get("AjaxService.aspx", HandleCallback); } function HandleBackCallback(data, textStatus) { document.title = data; $("#content").get(0).innerHTML = data; } function HandleCallback(data) { currentAction++; document.title = data; document.location.hash = "Action" + currentAction.toString(); $("#content").get(0).innerHTML = data; } </script> </head> <body onload="document.body.onhashchange = OnHashChanged;"> <div id="content"></div> <input type="button" value="call action" onclick="DoAction()" /> </body> </html> |
AJAXService.aspx |
using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.UI; using System.Web.UI.WebControls; public partial class AjaxService : System.Web.UI.Page { protected void Page_Load(object sender, EventArgs e) { Response.Expires = -1; if (Request.QueryString["actionID"] != null) { Dictionary<string, string> sessionStorage = Session["Actions"] as Dictionary<string, string>; if (sessionStorage != null) { Response.Write(sessionStorage[Request.QueryString["actionID"]]); Response.Flush(); Response.End(); return; } else { return; } } else { Dictionary<string, string> sessionStorage = Session["Actions"] as Dictionary<string, string>; string data = DateTime.Now.ToString(); if (sessionStorage == null) { sessionStorage = new Dictionary<string, string>(); Session["Actions"] = sessionStorage; sessionStorage.Add("Action0", data); } else sessionStorage.Add("Action" + sessionStorage.Count.ToString(), data); Response.Write(data); Response.Flush(); Response.End(); } } } |
圖18是執行畫面。
圖18
這個例子有點複雜,讓我拆解開來談,首先是AjaxService.aspx的程式碼,這裡會為每個AJAX Request建立一個Action記錄,這些記錄存放在一個
Dictionary Collections中,這個Collection中每個元素代表一個Action,以Action<Request序號>為Key,值則是當時AJAX Request時的回傳值。
當JQueryNavigator.htm中以get來啟動一個AJAX Request成功後,HandleCallback會被呼叫,此處產生一個Action序號(currentAction),並將其以Action<Action序號>
設定給document.location.hash,然後設定document.title後結束。HandleCallback的一連串動作會引發IE 8記錄歷程的動作,其會以document.title為歷程項目的標題,
以document.location.hash為點選該歷程記錄時的值,當使用者點選上一頁或是某個歷程項目時,document.body.onhashchange事件會被觸發,而我們所設定的
OnHashChanged函式就會被呼叫,此時的document.location.hash值正是當初IE 8所記錄的,於此我們再以get函式將Action<序號>做為URL附加參數傳入,
最後AjaxService.aspx中以Action<序號>來取出當初預儲的值回傳。
整個流程順下來,你會發現IE 8扮演的角色是在記錄歷程記錄時將hash視為是URL的一部份一起記錄下來,但hash值的產生及管理是由我們用JavaScript處理的,
後端也必須預先記錄每個Request,因為當使用者企圖回到某個歷程記錄時,IE 8會觸發onhashchange,而我們需要在此處重新回到後端來取得先前取過的值。
另一種更精巧的設計方式,還可以利用DOM Storage的特性,將這些值預先儲存在客戶端,這樣就不用得常常回後端取得先前已取過的值了。
本文範例下載