您的位置:首页 > 运维架构 > 网站架构

哈佛公开课:构建动态网站——第七讲Ajax

2014-02-12 20:06 357 查看
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
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: