[IE8]Internet Explorer 8 中的JavaScript (2)

  • 39529
  • 0
  • IE
  • 2009-04-13

本文主題涵蓋 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的特性,將這些值預先儲存在客戶端,這樣就不用得常常回後端取得先前已取過的值了。
 

本文範例下載