哈佛公開課:構建動態網站——第七講Ajax

1.google maps其實是ajax應用,在地圖上進行任意操作時,都無需刷新頁面。ajax的實質就是讓我們能夠執行http請求而無需重載頁面

2.DOM的結構


3.通過js來修改html頁面。


4.Ajax現在已經不是原本縮寫的意思:異步js和xml(asynchronous javascript and xml),如今它不一定異步,也不一定用 js,不一定用xml,它如今是一個代名詞,代指在瀏覽器客戶端動態修改html的技術。所謂異步就是指一次可以執行多個請求,具體講就是當執行某個請求時,它不會阻礙網頁剩餘部分的運作。而同步在需要獲得服務器的迴應後纔會有其他動作。

5.XMLHttpRequest對象很標準,但是在不同瀏覽器的實現方式是不同的,這也是爲什麼有個常說的詞:跨瀏覽器應用。

6. 可以看到有個return false意思就是返回失敗,這樣會阻止提交的動作,因此頁面不會提交,故不會跳轉到另一個頁面

<body>
 <form action="quote1.php" method="get" onsubmit="quote(); return false;">
XMLHttpRequest對象的實例化方法大概有四五種,下面介紹了兩種(雖然不全但能包括火狐,safari,chrome,ie),但基本上都是通過try-catch嵌套來嘗試不同的實例化方法。在這一講最後也會講到使用庫來進行實例化的方式
function quote()
29. {
30. // instantiate XMLHttpRequest object
31. try		//首先嚐試用js方式實例化XMLHttpRequest對象
32. {
33. xhr = new XMLHttpRequest(); //實例化XMLHttpRequest對象,實例化的方法有多種,具體使用哪種得取決於用什麼瀏覽器
34. }
35. catch (e)	//如果實例化失敗則js一般會拋出異常,然後catch塊會捕獲異常而不是直接報錯,然後嘗試IE的方式
36. {
37. xhr = new ActiveXObject("Microsoft.XMLHTTP"); //如果是IE瀏覽器就會用到ActiveX,如果是火狐或safari之類則是用上面那種,也即js式的對象實例化
38. }
39. 
40. // handle old browsers
41. if (xhr == null)
42. {
43. alert("Ajax not supported by your browser!");
44. return;
45. }
46. 
47. // construct URL,即構造數據來源的URL,這裏的quote1.php表示在服務器端的與html文件相同路徑下有這樣一個php文件,所以這是個相對路徑,通過document.getElementById獲取到用戶輸入到表單中的值
48. var url = "quote1.php?symbol=" + document.getElementById("symbol").value;lectures/7/src/ajax1.html  
49. 
50. // get quote接下來是要獲取報價,這就需要打開XMLHttpRequest對象連接,因此有了下面的xhr.open
51. xhr.onreadystatechange = handler; //handler是後面的處理函數
52. xhr.open("GET", url, true); //使用get的方式傳入url,通常最後一個參數總爲true,表示需要是異步的
53. xhr.send(null); 	//此時數據才真正發送給quote1.php
54. }
55. 
56. 
57. /*
58. * void
59. * handler()
60. *
61. * Handles the Ajax response.
62. */
63. function handler() //該函數由XMLHttpRequest對象調用,只要狀態改變,不管是首次發送數據,還是連接上服務器,也不管是數據由服務器返回,還是信息得到解析,只要整個請求中狀態發生改變,它都會調用後面定義的handler這個函數
64. {
65. // only handle loaded requests
66. if (xhr.readyState == 4) //這裏只處理了一個狀態也就是XMLHttpRequest對象的狀態爲4,即請求完全載入狀態
67. {
68. if (xhr.status == 200)//而且假設HTTP響應爲200,即一切正常,而不是403權限錯誤或404文件未找到什麼的
69. alert(xhr.responseText);//此時用彈窗方式顯示返回結果,也即是這裏的xhr容納了php文件給回的responseText信息,這讓我們能訪問quote1.php文件中發回的任何內容
70. else
71. alert("Error with Ajax call!");
72. }
73. }
狀態也有很多種


//quote1.php
<?php
13. // get quote
14. $handle = @fopen("http://download.finance.yahoo.com/d/quotes.csv?s={$_GET['symbol']}&f=e1l1", "r");
15. if ($handle !== FALSE)
16. {
17. $data = fgetcsv($handle);
18. if ($data !== FALSE && $data[0] == "N/A")
19. print($data[1]);
20. fclose($handle);
21. }
?>
爲什麼不直接從yahoo獲得數據呢,這是因爲爲了安全的同源策略,我們只能在網頁所在的位置使用 ajax獲取信息,如果從其他不受信任的位置獲取信息,信息源處的人會意識到並且可能故意發送惡意數據,以此來破壞你的網頁,因爲你允許了這些不受信任的數據進入你的頁面。

舉例來講就是ajax.html如果和quote1.php不在同一個服務器下面,那麼就不能行。一般不會單獨訪問php文件,通常是在ajax.html中執行請求。

7.使用彈窗來顯示數據,用戶體驗不好,所以修改handler中的函數讓數據顯示在DOM中,也即頁面上。下面代碼中的price爲頁面中某個輸入框的id

function handler(){
……
if (xhr.status == 200)
    document.getElementById("price").value = xhr.responseText;
……
}
8.輸入框總有讓人想去填的衝動,所以最符合ajax風格的是修改html頁面本身來顯示數據

這也只是對handler函數那部分進行了這樣的修改document.getElementById("price").innerHTML = xhr.responseText;

其中的price是下面這個標籤的id。a標籤,div標籤,span標籤等等只要我們給了id,都可以通過innerHTML修改其內容也即<span>標籤之間的所有內容包括表示加粗的標籤<b></b>也會被改掉。

<br>
 Price: <span id="price"><b>to be determined</b></span>
9.上面這整個例子有點太簡單化了,實際中我們要注意一些安全問題,也就是得到的數據要經過檢驗,看是否有危害,是否包含其他惡意js代碼,當沒有危害時我們才能將其注入到頁面中。
10.同時獲得多個數據


此時quote2.php打印了多個數據

1. <?
2. /**
3. * quote2.php
4. *
5. * Outputs price, low, and high of given symbol as plain/text.
6. *
7. * David J. Malan
8. * Computer Science S-75
9. * Harvard Summer School
10. */
11. 
12. // send MIME type
13. header("Content-type: text/plain");
14. 
15. // try to get quote
16. $handle = @fopen("http://download.finance.yahoo.com/d/quotes.csv?s={$_GET['symbol']}&f=e1l1hg", "r");
17. if ($handle !== FALSE)
18. {
19. $data = fgetcsv($handle);
20. if ($data !== FALSE && $data[0] == "N/A")
21. {
22. print("Price: {$data[1]}\n");
23. print("High: {$data[2]}\n");
24. print("Low: {$data[3]}");
25. }
26. fclose($handle);
27. }
28. ?>
如果不註明了換行,那麼當注入到html頁面中的時候不會換行,因此需要人爲了的加入html的換行標籤

if ($data !== FALSE && $data[0] == "N/A")
18. {
19. print("Price: {$data[1]}");
20. print("<br />");
21. print("High: {$data[2]}");
22. print("<br />");
23. print("Low: {$data[3]}");
24. }

11.可以利用返回的狀態值前的等待時期做一些花樣,比如在獲得返回查詢數據前會提示用戶正在查詢中等等。爲了達到這個功能就需要處理XMLHttpRequest對象的不同狀態。

var url = "quote4.php?symbol=" + document.getElementById("symbol").value;
50. 
51. // inform user
52. document.getElementById("quote").innerHTML = "Looking up symbol..."; //在數據發送前就更改了提示。
53. 
54. // get quote
55. xhr.onreadystatechange = handler;
56. xhr.open("GET", url, true);
57. xhr.send(null);

當然也可以不用單調的文字提示而是使用動態gif圖片

方法就是首先在發送之前讓圖片塊展示出來:

// show progress
53. document.getElementById("progress").style.display = "block"; //此處也發現了DOM的一大優點不但可以修改對象的html還可以修改元素的css樣式
55. // get quote
56. xhr.onreadystatechange = handler;
57. xhr.open("GET", url, true);
58. xhr.send(null);
在獲得返回數據時,再把圖片所在塊去除開:

function handler()
69. {
70. // only handle requests in "loaded" state
71. if (xhr.readyState == 4)
72. {
73. // hide progress
74. document.getElementById("progress").style.display = "none";
75. 
76. if (xhr.status == 200)
77. document.getElementById("quote").innerHTML = xhr.responseText;
78. else
79. alert("Error with Ajax call!");
80. }
81. }

progress在頁面文件中是一個層div的id,在這個層中有gif圖片

<div id="progress" style="display: none;">
 <img alt="Please Wait" src="19-0.gif">
 <br><br>
 </div>
課堂上爲了更好地演示這個顯示gif圖的效果,於是人爲的在quote.php開頭加入了一個延遲函數sleep(5).讓其延遲5秒

12.通過XML方式來獲得數據。收到來自yahoo的返回數據,加上在quote5中自己打印的根元素quote以及對應的XML子元素,整個返回數據效果如下:


quote5.php
1. <?. 
12. // set MIME type
13. header("Content-type: text/xml");
14. 
15. // output root element's start tag
16. print("<quote symbol='{$_GET['symbol']}'>");
17. 
18. // try to get quote
19. $handle = @fopen("http://download.finance.yahoo.com/d/quotes.csv?s={$_GET['symbol']}&f=e1l1hg", "r");
20. if ($handle !== FALSE)
21. {
22. $data = fgetcsv($handle);
23. if ($data !== FALSE && $data[0] == "N/A")
24. {
25. print("<price>{$data[1]}</price>");
26. print("<high>{$data[2]}</high>");
27. print("<low>{$data[3]}</low>");
28. }
29. fclose($handle);
30. }
31. 
32. // output root element's end tag
33. print("</quote>");
ajax8.html:

function quote() //這個函數基本沒什麼變化
30. {
31. // instantiate XMLHttpRequest object
32. try
33. {
34. xhr = new XMLHttpRequest();
35. }
36. catch (e)
37. {
38. xhr = new ActiveXObject("Microsoft.XMLHTTP");
39. }
40. 
41. // handle old browsers
42. if (xhr == null)
43. {
44. alert("Ajax not supported by your browser!");
45. return;
46. }
47. 
48. // construct URL
49. var url = "quote5.php?symbol=" + document.getElementById("symbol").value;
50. 
51. // get quote
52. xhr.onreadystatechange = handler;
53. xhr.open("GET", url, true);
54. xhr.send(null);
55. }


function handler()
65. {
66. // only handle requests in "loaded" state
67. if (xhr.readyState == 4)
68. {
69. if (xhr.status == 200)
70. {
71. // get XML
72. var xml = xhr.responseXML; //注意這裏變爲了XML
73. 
74. // update price
75. var prices = xml.getElementsByTagName("price");
76. if (prices.length == 1)
77. {
78. var price = prices[0].firstChild.nodeValue;
79. document.getElementById("price").innerHTML = price;
80. }
81. 
82. // update low
83. var lows = xml.getElementsByTagName("low");
84. if (lows.length == 1)
85. {
86. var low = lows[0].firstChild.nodeValue;
87. document.getElementById("low").innerHTML = low;
88. }
89. 
90. // update high
91. var highs = xml.getElementsByTagName("high");
92. if (highs.length == 1)
93. {
94. var high = highs[0].firstChild.nodeValue;
95. document.getElementById("high").innerHTML = high;
96. }lectures/7/src/ajax8.html
97. }
98. else
99. alert("Error with Ajax call!");
100. }
101. }
對應的HTML頁面代碼中:

<body>
Price: <span id="price"></span>
 <br>
 Low: <span id="low"></span>
 <br>
 High: <span id="high"></span>
</body>

13.  在接下來的例子ajax9.html主要爲了展示使用更多DOM元素來插入數據,它請求的頁面是quote1.php,它只會返回一條文本html數據(即只有一條信息,價格),所以在ajax9中用的還是responseText。使用DOM方法創建新節點然後插入數據。在html頁面中有

<div id="quotes"></div>
但數據並非直接插入這裏,innerHTML未用於此div,而是我們在代碼中創建了新div元素,,然後在此div中創建新文本節點。下圖是整個DOM的標籤之間的關係圖,new div和symbol:29.05(其中數字是請求返回的數據)就是我們自己創建的元素(標籤),然後把symbol作爲new div的子元素,最後再把new div作爲div id=quote的子元素。可見通過DOM函數不僅可以修改已存在的元素還可以用於創建


function quote()
{
 // instantiate XMLHttpRequest object
 try
 {
   xhr = new XMLHttpRequest();
 }
 catch (e)
 {
   xhr = new ActiveXObject("Microsoft.XMLHTTP");
 }
 // handle old browsers
 if (xhr == null)
 {
44. alert("Ajax not supported by your browser!");
45. return;
 }
 
 // get symbol
 var symbol = document.getElementById("symbol").value;
 
 // construct URL
 var url = "quote1.php?symbol=" + symbol;
 
 // get quote
 xhr.onreadystatechange =  function(){   // 這裏讓onreadystatechange等於匿名函數(沒有名字的函數),由於不需要用在別處只用在這裏所以不需要名字xhr.onreadyst				          //atechange字段並不需要函數有名字
 // only handle loaded requests
   if (xhr.readyState == 4)
   {
      if (xhr.status == 200)
     {
     // insert quote into DOM
       var div = document.createElement("div");
       var text = document.createTextNode(symbol + ": " + xhr.responseText);
       div.appendChild(text);
       document.getElementById("quotes").appendChild(div);
     }
     else
        alert("Error with Ajax call!");
   }
 }
 xhr.open("GET", url, true);
 xhr.send(null);
}

14.XML不錯,但使用起來還是有點麻煩,需要遍歷樹結構,這並不簡潔。因此除了使用XML格式外,還有別的方式可用。接下來要介紹的可以說是ajax中使用最多的,JSON。

它能序列化數據,比如一個二維數組,它能將其序列化爲一串數據,也即一個一維數組。JSON被大多數語言廣泛支持,在PHP中通過內建的函數也能夠簡單創建JSON對象,比如說一個PHP數組可以輕鬆轉化爲JSON對象。在PHP端,可以使用函數json_encode,傳入一些變量比如數組(可以是關聯數組或者二維數組),絕大多數PHP類型都能轉爲JSON然後發回到Ajax頁面。唯一有點麻煩的是js端需要使用eval函數,使用該函數需要注意安全,因爲不管你取的什麼text內容,然後你對它求值,js將像代碼一樣實際執行,這對於JSON對象倒是沒什麼,但如果裏面是一些js代碼那就會有問題,因爲如果是代碼那顯然會被執行,所以要注意。

使用了JSON後在本例子中獲得的數據就是這麼一串{price: 304.18, high: 305.60, low:302.20},下面來看看實際產生這個數據的quote文件:

<?
2. /**
3. * quote6.php
4. *
5. * Outputs price, low, and high of given symbol in JSON format.
6. *
7. * David J. Malan
8. * Computer Science S-75
9. * Harvard Summer School
10. */
11. 
12. // set MIME type
13. header("Content-type: application/json");
14. 
15. // try to get quote
16. $handle = @fopen("http://download.finance.yahoo.com/d/quotes.csv?s={$_GET['symbol']}&f=e1l1hg", "r");
17. if ($handle !== FALSE)
18. {
19. $data = fgetcsv($handle);
20. if ($data !== FALSE && $data[0] == "N/A")
21. {
22. if (is_numeric($data[1]))
23. $price = $data[1];
24. if (is_numeric($data[2]))
25. $high = $data[2];
26. if (is_numeric($data[3]))
27. $low = $data[3];
28. }
29. fclose($handle);
30. }
31. 
32. // output JSON
33. print("{ price: $price, high: $high, low: $low }"); //直接輸出原始JSON對象
34. ?>
優點在於在ajax10.html中可以發現獲取JSON對象的值飛航簡單。

function quote()
30. {
31. // instantiate XMLHttpRequest object
32. try
33. {
34. xhr = new XMLHttpRequest();
35. }
36. catch (e)
37. {
38. xhr = new ActiveXObject("Microsoft.XMLHTTP");
39. }
40. 
41. // handle old browsers
42. if (xhr == null)
43. {
44. alert("Ajax not supported by your browser!");
45. return;
46. }
47. 
48. // get symbol
49. var symbol = document.getElementById("symbol").value;
50. 
51. // construct URL
52. var url = "quote6.php?symbol=" + symbol;
53. 
54. // get quote
55. xhr.onreadystatechange =
56. function()
57. {
58. // only handle loaded requests
59. if (xhr.readyState == 4)
60. {
61. if (xhr.status == 200)
62. {
63. // evaluate JSON
64. var quote = eval("(" + xhr.responseText + ")"); //周圍加上括號表示它是對象,求值結束後quote變量就成爲了對象,然後就可以向JSON對象那樣處理它了
65. 
66. // show JSON in textarea
67. document.getElementById("code").value = xhr.responseText;
68. 
69. // insert quote into DOM
70. var div = document.createElement("div");
71. var text = document.createTextNode(symbol + ": " + quote.price); //此處我只想取得price數據,因此quote.price,可見這個功能非常強大易用,而不用如XML那樣還得遍歷整棵XML樹,直接用點符號獲取數據就行了。
72. div.appendChild(text);
73. document.getElementById("quotes").appendChild(div);
74. }
75. else
76. alert("Error with Ajax call!");
77. }
78. }
79. xhr.open("GET", url, true);
80. xhr.send(null);
81. }

對應用到的html頁面body標籤中的子元素:

<div id="quotes"></div>
<br><br>
<textarea cols="80" id="code" rows="16"></textarea>

最終獲取數據效果如下圖:



15. 這裏用的是quote7.php,之前說過可以任意使用數據類型,這裏在php中模仿JSON對象,建立了一個叫Stock的類,用到了json_encode函數

<?
2. /**
3. * quote7.php
4. *
5. * Outputs price, low, and high of given symbol in JSON format
6. * using PHP's JSON extension.
7. *
8. * David J. Malan
9. * Computer Science S-75
10. * Harvard Summer School
11. */
12. 
13. // defines a stock
14. class Stock
15. {
16. public $price;
17. public $high;
18. public $low;
19. }
20. 
21. // set MIME type
22. header("Content-type: application/json"); //這個要注意,它的內容類型一定不要搞錯了,要分清是JSON還是文本text還是XML
23. 
24. // try to get quote
25. $handle = @fopen("http://download.finance.yahoo.com/d/quotes.csv?s={$_GET['symbol']}&f=e1l1hg", "r");
26. if ($handle !== FALSE)
27. {
28. $data = fgetcsv($handle);
29. if ($data !== FALSE && $data[0] == "N/A")
30. {
31. $stock = new Stock();	//創建一個新的stock實例
32. 
33. if (is_numeric($data[1]))
34. $stock->price = $data[1];
35. if (is_numeric($data[2]))
36. $stock->high = $data[2];
37. if (is_numeric($data[3]))
38. $stock->low = $data[3];
39. }
40. fclose($handle);
41. }
42. 
43. // output JSON
44. print(json_encode($stock)); //json_encode將返回JSON編碼後對象的字符串,不管是用於數組還是類都可以使用這個函數輕鬆應對
45. ?>
json_encode會在各個兩邊加上引號,這不用在意



16.ajax12.html是使用了第三方框架讓工作簡單,例子中是使用的YUI庫來減少代碼量並且能支持更多瀏覽器。


首先需要包含一些YUI js文件,然後和原來一樣需要帶上股票代碼去連接quote7,然後告訴YUI執行異步請求,請求類型爲get,傳入URL,以及還有一個JSON對象,花括號表示JSON對象,鍵爲success,值爲handler。這表示YUI中有這麼個success字段,要求handler函數做些什麼,這類似於前面講過的onreadystatechange字段。把handler函數傳給它,所以狀態值要改變,就會調用handler函數


這裏handler函數中傳入了一個對象參數o,其作用相當於前面的xhr,於是可以從quote7中得回responseText,並用eval求JSON對象的值


17.不同於YUI庫,下面是使用的是JQuery庫。總之使用第三方庫能讓Ajax請求變得更加容易。

<head>
18. <script src="http://code.jquery.com/jquery-latest.js"></script>
19. <script>
20. 
21. $(document).ready(function() {
22. $("#form").submit(function() {
23. $.ajax({
24. url: "quote7.php",
25. data: {
26. symbol: $("#symbol").val()
27. },
28. success: function(data) {
29. $("#price").html(data.price);
30. $("#high").html(data.high);
31. $("#low").html(data.low);
32. }
33. });
34. return false;
35. });
36. });
37. 
38. </script>
39. <title></title>
40. </head>

18.由於google map中提供了api,因此可以將谷歌地圖用到我們的應用中,在initialize函數中創建對象設定參數,myLatlng是一個經緯度變量。myOptions是選項,可以確定縮放等級,zoom:8表示一般的縮放等級。mapTypeId是地圖類型,然後後面一句話是實例化一張地圖給既存的div,可以在html頁面代碼部分看到這個div,而在css中可以設置這個div大小,讓地圖以合適尺寸顯示到網頁,這樣你可以把地圖顯示在一邊而不用佔據整個頁面。


從google maps API指南能查看到關於參數的信息。比如MapOptions. ,在課程項目中會把谷歌地圖和BART提供的XML地鐵數據整合起來做出一個實時地鐵信息查詢地圖。同理整合兩個資源做出一個網站的還有通過谷歌地圖和WiFi熱點數據之間來整合起來做出查詢附近wifi熱點。這類整合兩個以上資源的應用稱作mashup

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章