Ajax概述,封裝以及聯合模板引擎進行資料互動

冰山一樹Sankey發表於2022-03-19

歡迎大家去部落格冰山一樹Sankey,瀏覽效果更好。直接右上角搜尋該標題即可
冰山一樹Sankey

部落格園主頁:部落格園主頁-冰山一樹Sankey
CSDN主頁:CSDN主頁-冰山一樹Sankey

更多資料可參考Ajax 介紹篇丨慕課網教程 (imooc.com)

下面這些都是一些學習筆記。臨淵羨魚,不如退而結網!!願我自己學有所成,也願每個前端愛好者學有所成

一. Ajax概述

在網站中存在以下問題:

  • 網速慢的情況下,頁面載入時間長,使用者只能等待
  • 表單提交後,如果一項內容不合格,需要重新填寫所有表單內容
  • 頁面跳轉,重新載入頁面,造成資源浪費,增加使用者等待時間

於是Ajax誕生了,Ajax:標準讀音 [ˈeɪˌdʒæks] ,中文音譯:阿賈克斯

它是瀏覽器提供的一套方法,可以實現頁面無重新整理更新資料,提高使用者瀏覽網站應用的體驗。

錄製_2022_03_17_21_37_32_165

二. Ajax執行環境及實現

2.1 Ajax執行環境

Ajax 相當於瀏覽器傳送請求與接收響應的代理人,以實現在不影響使用者瀏覽頁面的情況下,區域性更新頁面資料,從而提高使用者體驗。在瀏覽器端與伺服器端進行請求響應是不可以控制的,但使用Ajax後,開發人員人員可控制其過程。

image-20220317223015626

那如何實現Ajax呢?

  1. 建立 Ajax 物件
var xhr = new XMLHttpRequest();
  1. 告訴 Ajax 請求地址以及請求方式
xhr.open('get', 'http://www.example.com');
  1. 傳送請求
xhr.send();
  1. 獲取伺服器端給與客戶端的響應資料
xhr.onload = function () {
     console.log(xhr.responseText);
 }

因為Ajax 技術需要執行在網站環境中才能生效,下面使用Node建立的伺服器作為網站伺服器。

手寫在建立一個資料夾server,在其根目錄下再建立public,使用npm安裝express和body-parser,其package.jion檔案如下

{
  "name": "code",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "",
  "license": "ISC",
  "dependencies": {
    "body-parser": "^1.18.3",
    "express": "^4.16.4"
  }
}

然後再在根目錄下建立app.js檔案,這裡編寫服務端程式碼

2.2 傳遞get請求引數

在public中新建傳遞get請求引數.html檔案

<!DOCTYPE html>
<html lang="en">
<head>
	<meta charset="UTF-8">
	<title>Document</title>
</head>
<body>
	<p>
		<input type="text" id="username">
	</p>
	<p>
		<input type="text" id="age">
	</p>
	<p>
		<input type="button" value="提交" id="btn">
	</p>
	<script type="text/javascript">
		// 獲取按鈕元素
		var btn = document.getElementById('btn');
		// 獲取姓名文字框
		var username = document.getElementById('username');
		// 獲取年齡文字框
		var age = document.getElementById('age');
		// 為按鈕新增點選事件
		btn.onclick = function () {
			// 建立ajax物件
			var xhr = new XMLHttpRequest();
			// 獲取使用者在文字框中輸入的值
			var nameValue = username.value;
			var ageValue = age.value;
			// 拼接請求引數
			var params = 'username='+ nameValue +'&age=' + ageValue;
			// 配置ajax物件
			xhr.open('get', 'http://localhost:3000/get?'+params);
			// 傳送請求
			xhr.send();
			// 獲取伺服器端響應的資料
			xhr.onload = function () {
				console.log(xhr.responseText)
			}
		}
	</script>
</body>
</html>

在服務的app.js,新增

// 引入express框架
const express = require('express');
// 路徑處理模組
const path = require('path');
const bodyParser = require('body-parser');
const fs = require('fs');
// 建立web伺服器
const app = express();

// 靜態資源訪問服務功能
app.use(express.static(path.join(__dirname, 'public')));

// 傳遞get請求引數
app.get('/get', (req, res) => {
    //req.query得到json字串
	res.send(req.query);
});

這裡放在app.js的最後,
// 監聽埠
app.listen(3000);
// 控制檯提示輸出
console.log('伺服器啟動成功');

在server下呼叫命令列啟動nodejs伺服器,為了方便除錯,可使用

nodemon app.js

然後在瀏覽器開啟http://localhost:4000/傳遞get請求引數.html,輸入資料後,可返回輸入值。

解釋下這裡的過程: 用get的請求方法把輸入的資料從瀏覽器端傳送到服務端,服務端再把得到的資料再返回到瀏覽器端進行輸出,當然這裡為了測試,從服務端傳送到瀏覽器端的資料也可以是其他內容

image-20220317231137737

可以看到這裡的請求的url是域名+?key=value&key=value的形式以及請求方法為get

image-20220317231737308

2.3 傳遞post請求引數

傳遞post請求引數有兩種方式,第一種格式和get相同,傳遞的資料格式為key=value&key=value

在public中新建傳遞post請求引數.html檔案

<!DOCTYPE html>
<html lang="en">
<head>
	<meta charset="UTF-8">
	<title>Document</title>
</head>
<body>
	<p>
		<input type="text" id="username">
	</p>
	<p>
		<input type="text" id="age">
	</p>
	<p>
		<input type="button" value="提交" id="btn">
	</p>
	<script type="text/javascript">
		// 獲取按鈕元素
		var btn = document.getElementById('btn');
		// 獲取姓名文字框
		var username = document.getElementById('username');
		// 獲取年齡文字框
		var age = document.getElementById('age');
		// 為按鈕新增點選事件
		btn.onclick = function () {
			// 建立ajax物件
			var xhr = new XMLHttpRequest();
			// 獲取使用者在文字框中輸入的值
			var nameValue = username.value;
			var ageValue = age.value;
			// 拼接請求引數
			var params = 'username='+ nameValue +'&age=' + ageValue;
			// 配置ajax物件
			xhr.open('post', 'http://localhost:3000/post');
			// 設定請求引數格式的型別(post請求必須要設定)
			xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
			// 傳送請求
			xhr.send(params);
			// 獲取伺服器端響應的資料
			xhr.onload = function () {
				console.log(xhr.responseText)
			}
		}
	</script>
</body>
</html>

在原app.js的基礎上再新增

//解析傳遞的post資料,格式為
const bodyParser = require('body-parser');
app.use(bodyParser.urlencoded());
// 傳遞post請求引數
app.post('/post', (req, res) => {
	res.send(req.body);
});

開啟瀏覽器中輸入http://localhost:3000/post請求引數.html, 開啟瀏覽器除錯,點選網路,輸入資料後,可以得到post,請求url以及請求方法

image-20220318153531163

這裡的表單資料為輸入的值,說明資料傳遞成功。

image-20220318153805465

第二種傳遞的格式為json資料,主要區別在於xhr.setRequestHeader('Content-Type', 'application/json');,以及服務端的app.use(bodyParser.json());具體看下面的程式碼

注意:get 請求是不能提交 json 物件資料格式的,傳統網站的表單提交也是不支援 json 物件資料格式的。

在public中新建向伺服器端傳遞JSON格式的請求引數.html檔案

<!DOCTYPE html>
<html lang="en">
<head>
	<meta charset="UTF-8">
	<title>Document</title>
</head>
<body>
	<script type="text/javascript">
		// 1.建立ajax物件
		var xhr = new XMLHttpRequest();
		// 2.告訴Ajax物件要向哪傳送請求,以什麼方式傳送請求
		// 1)請求方式 2)請求地址
		xhr.open('post', 'http://localhost:3000/json');
		// 通過請求頭告訴伺服器端客戶端向伺服器端傳遞的請求引數的格式是什麼
		xhr.setRequestHeader('Content-Type', 'application/json');
		// JSON.stringify() 將json物件轉換為json字串
		// 3.傳送請求
		xhr.send(JSON.stringify({name: 'lisi', age:50}));
		// 4.獲取伺服器端響應到客戶端的資料
        xhr.onload = function () {
            console.log(xhr.responseText)
        }
	</script>
</body>
</html>

在原app.js的基礎上再新增

//將app.use(bodyParser.urlencoded())改為app.use(bodyParser.json());
app.use(bodyParser.json());
//向伺服器端傳遞JSON格式的請求引數
app.post('/json', (req, res) => {
	res.send(req.body);
});

注意:還需要將app.use(bodyParser.urlencoded());改為app.use(bodyParser.json());

2.5 獲取服務端響應

在說明之前,先來介紹一個概念——報文

在 HTTP 請求和響應的過程中傳遞的資料塊就叫報文,包括要傳送的資料和一些附加資訊,這些資料和資訊要遵守規定好的格式。

image-20220318154706440

上面的get與post請求,我們均是使用onload來獲取服務端的報文,但也可以通過另外的方法來獲取:

在建立ajax物件,配置ajax物件,傳送請求,以及接收完伺服器端響應報文,這個過程中的每一個步驟都會對應一個數值,這個數值就是ajax狀態碼。使用onreadystatechange 事件,可以檢測狀態碼的變化,當狀態碼為 4 時就可以通過 xhr.responseText 獲取伺服器端的響應資料。

0:請求未初始化(還沒有呼叫open())
1:請求已經建立,但是還沒有傳送(還沒有呼叫send())
2:請求已經傳送
3:請求正在處理中,通常響應中已經有部分資料可以用了
4:響應已經完成,可以獲取並使用伺服器的響應了

即下面方法可onload方法等效

// 當Ajax狀態碼發生變化時
 xhr.onreadystatechange = function () {
     // 判斷當Ajax狀態碼為4時
     if (xhr.readyState == 4) {
         // 獲取伺服器端的響應資料
         console.log(xhr.responseText);
     }
 }

2.4 Ajax錯誤處理

  • Ajax狀態碼: 表示Ajax請求的過程狀態 ajax物件返回的
  • Http狀態碼: 表示請求的處理結果 是伺服器端返回的
  1. 網路暢通,伺服器端能接收到請求,伺服器端返回的結果不是預期結果。

可以判斷伺服器端返回的狀態碼,分別進行處理。xhr.status 獲取http狀態碼

  1. 網路暢通,伺服器端沒有接收到請求,返回404狀態碼。

檢查請求地址是否錯誤。

  1. 網路暢通,伺服器端能接收到請求,伺服器端返回500狀態碼。

伺服器端錯誤,找後端程式設計師進行溝通。

  1. 網路中斷,請求無法傳送到伺服器端。

會觸發xhr物件下面的onerror事件,在onerror事件處理函式中對錯誤進行處理。

<!DOCTYPE html>
<html lang="en">
<head>
	<meta charset="UTF-8">
	<title>Document</title>
</head>
<body>
	<button id="btn">傳送Ajax請求</button>
	<script type="text/javascript">
		var btn = document.getElementById('btn');

		btn.onclick = function () {
			// 1.建立ajax物件
			var xhr = new XMLHttpRequest();
			// 2.告訴Ajax物件要向哪傳送請求,以什麼方式傳送請求
			// 1)請求方式 2)請求地址
			xhr.open('get', 'http://localhost:3000/error');
			// 3.傳送請求
			xhr.send();
			// 4.獲取伺服器端響應到客戶端的資料
			xhr.onload = function (){
				// xhr.status 獲取http狀態碼
				console.log(xhr.responseText);

				if (xhr.status == 400) {
					alert('請求出錯')
				}
			}
			// 當網路中斷時會觸發onerrr事件
			xhr.onerror = function () {
				alert('網路中斷, 無法傳送Ajax請求')
			}
		}

		// Ajax狀態碼: 表示Ajax請求的過程狀態 ajax物件返回的
		// Http狀態碼: 表示請求的處理結果 是伺服器端返回的
	</script>
</body>
</html>

服務端app.js

app.get('/error', (req, res) => {
	//console.log(abc);
	res.status(400).send('not ok');
});

三. Ajax封裝

傳送一次請求程式碼過多,傳送多次請求程式碼冗餘且重複。我們可以將請求程式碼封裝到函式中,發請求時呼叫函式即可。

function ajax (options) {
	// 預設值
	var defaults = {
		type: 'get',
		url: '',
		async: true,
		data: {},
		header: {
			'Content-Type': 'application/x-www-form-urlencoded'
		},
		success: function () {},
		error: function () {}
	}
	// 使用使用者傳遞的引數替換預設值引數
	Object.assign(defaults, options);
	// 建立ajax物件
	var xhr = new XMLHttpRequest();
	// 引數拼接變數
	var params = '';
	// 迴圈引數
	for (var attr in defaults.data) {
		// 引數拼接
		params += attr + '=' + defaults.data[attr] + '&';
		// 去掉引數中最後一個&
		params = params.substr(0, params.length-1)
	}
	// 如果請求方式為get
	if (defaults.type == 'get') {
		// 將引數拼接在url地址的後面
		defaults.url += '?' + params;
	}

	// 配置ajax請求
	xhr.open(defaults.type, defaults.url, defaults.async);
	// 如果請求方式為post
	if (defaults.type == 'post') {
		// 設定請求頭
		xhr.setRequestHeader('Content-Type', defaults.header['Content-Type']);
		// 如果想伺服器端傳遞的引數型別為json
		if (defaults.header['Content-Type'] == 'application/json') {
			// 將json物件轉換為json字串
			xhr.send(JSON.stringify(defaults.data))
		}else {
			// 傳送請求
			xhr.send(params);
		}
	} else {
		xhr.send();
	}
	// 請求載入完成
	xhr.onload = function () {
		// 獲取伺服器端返回資料的型別
		var contentType = xhr.getResponseHeader('content-type');
		// 獲取伺服器端返回的響應資料
		var responseText = xhr.responseText;
		// 如果伺服器端返回的資料是json資料型別
		if (contentType.includes('application/json')) {
			// 將json字串轉換為json物件
			responseText = JSON.parse(responseText);
		}
		// 如果請求成功
		if (xhr.status == 200) {
			// 呼叫成功回撥函式, 並且將伺服器端返回的結果傳遞給成功回撥函式
			defaults.success(responseText, xhr);
		} else {
			// 呼叫失敗回撥函式並且將xhr物件傳遞給回撥函式
			defaults.error(responseText, xhr);
		} 
	}
	// 當網路中斷時
	xhr.onerror = function () {
		// 呼叫失敗回撥函式並且將xhr物件傳遞給回撥函式
		defaults.error(xhr);
	}
}

四. 模板引擎

4.1 下載

使用模板引擎提供的模板語法,可以將資料和 HTML 拼接起來。官方地址: https://aui.github.io/art-template/zh-cn/index.html

先去下載art-template檔案

進入官網,點選Docs

image-20220318160106408

然後點選安裝,將temlate-web.js另存到本地就下載好了

image-20220318160147295

4.2 使用步驟

  1. 下載 art-template 模板引擎庫檔案並在 HTML 頁面中引入庫檔案
<script src="./js/template-web.js"></script>
  1. 準備 art-template 模板
<script id="tpl" type="text/html">
     <div class="box"></div>
 </script>
  1. 告訴模板引擎將哪一個模板和哪個資料進行拼接
var html = template('tpl', {username: 'zhangsan', age: '20'});
  1. 將拼接好的html字串新增到頁面中
 document.getElementById('container').innerHTML = html;
  1. 通過模板語法告訴模板引擎,資料和html字串要如何拼接
<script id="tpl" type="text/html">
     <div class="box"> {{ username }} </div>
 </script>

4.3 案例

下面介紹三個案例說明如何使用模板引擎,一起利用Ajax進行資料的互動。

所有服務端均在app.js中。

// 引入express框架
const express = require('express');
// 路徑處理模組
const path = require('path');
const formidable = require('formidable');
// 建立web伺服器
const app = express();

// 靜態資源訪問服務功能
app.use(express.static(path.join(__dirname, 'public')));

// 郵箱地址驗證
app.get('/verifyEmailAdress', (req, res) => {
	// 接收客戶端傳遞過來的郵箱地址
	const email = req.query.email;
	// 判斷郵箱地址註冊過的情況
	if (email == 'itheima@itcast.cn') {
		// 設定http狀態碼並對客戶端做出響應
		res.status(400).send({message: '郵箱地址已經註冊過了, 請更換其他郵箱地址'});
	} else {
		// 郵箱地址可用的情況
		// 對客戶端做出響應
		res.send({message: '恭喜, 郵箱地址可用'});
	} 
});

// 輸入框文字提示
app.get('/searchAutoPrompt', (req, res) => {
	// 搜尋關鍵字
	const key = req.query.key;
	// 提示文字列表
	const list = [
		'黑馬程式設計師',
		'黑馬程式設計師官網',
		'黑馬程式設計師順義校區',
		'黑馬程式設計師學院報名系統',
		'傳智播客',
		'傳智部落格前端與移動端開發',
		'傳智播客大資料',
		'傳智播客python',
		'傳智播客java',
		'傳智播客c++',
		'傳智播客怎麼樣'
	];
	// 搜尋結果
	let result = list.filter(item => item.includes(key));
	// 將查詢結果返回給客戶端
	res.send(result);
});

// 獲取省份
app.get('/province', (req, res) => {
	res.json([{
		id: '001',
		name: '黑龍江省'
	},{
		id: '002',
		name: '四川省'
	},{
		id: '003',
		name: '河北省'
	},{
		id: '004',
		name: '江蘇省'
	}]);
});

// 根據省份id獲取城市
app.get('/cities', (req, res) => {
	// 獲取省份id
	const id = req.query.id;
	// 城市資訊
	const cities = {
		'001': [{
			id: '300',
			name: '哈爾濱市'
		}, {
			id: '301',
			name: '齊齊哈爾市'
		}, {
			id: '302',
			name: '牡丹江市'
		}, {
			id: '303',
			name: '佳木斯市'
		}],
		'002': [{
			id: '400',
			name: '成都市'
		}, {
			id: '401',
			name: '綿陽市'
		}, {
			id: '402',
			name: '德陽市'
		}, {
			id: '403',
			name: '攀枝花市'
		}],
		'003': [{
			id: '500',
			name: '石家莊市'
		}, {
			id: '501',
			name: '唐山市'
		}, {
			id: '502',
			name: '秦皇島市'
		}, {
			id: '503',
			name: '邯鄲市'
		}],
		'004': [{
			id: '600',
			name: '常州市'
		}, {
			id: '601',
			name: '徐州市'
		}, {
			id: '602',
			name: '南京市'
		}, {
			id: '603',
			name: '淮安市'
		}]
	}
	// 響應
	res.send(cities[id]);
});

// 根據城市id獲取縣城
app.get('/areas', (req, res) => {
	// 獲取城市id
	const id = req.query.id;
	// 縣城資訊
	const areas = {
		'300': [{
			id: '20',
			name: '道里區',
		}, {
			id: '21',
			name: '南崗區'
		}, {
			id: '22',
			name: '平房區',
		}, {
			id: '23',
			name: '松北區'
		}],
		'301': [{
			id: '30',
			name: '龍沙區'
		}, {
			id: '31',
			name: '鐵鋒區'
		}, {
			id: '32',
			name: '富拉爾基區'
		}]
	};
	// 響應
	res.send(areas[id] || []);
});

// 監聽埠
app.listen(3000);
// 控制檯提示輸出
console.log('伺服器啟動成功');

4.3.1 驗證郵箱地址唯一性

驗證郵箱唯一性

在public資料夾下面新建驗證郵箱地址唯一性.html

<!DOCTYPE html>
<html lang="en">
<head>
	<meta charset="UTF-8">
	<title>驗證郵箱地址是否已經註冊</title>
	<link rel="stylesheet" href="/assets/bootstrap/dist/css/bootstrap.min.css">
	<style type="text/css">
		p:not(:empty) {
			padding: 15px;
		}
		.container {
			padding-top: 100px;
		}
	</style>
</head>
<body>
	<div class="container">
		<div class="form-group">
			<label>郵箱地址</label>
			<input type="email" class="form-control" placeholder="請輸入郵箱地址" id="email">
		</div>
		<!-- 錯誤 bg-danger 正確 bg-success -->
		<p id="info"></p>
	</div>
	<script src="/js/ajax.js"></script>
	<script>
		// 獲取頁面中的元素
		var emailInp = document.getElementById('email');
		var info = document.getElementById('info');

		// 當文字框離開焦點以後
		emailInp.onblur = function () {
			// 獲取使用者輸入的郵箱地址
			var email = this.value;
			// 驗證郵箱地址的正規表示式
			var reg = /^[A-Za-z\d]+([-_.][A-Za-z\d]+)*@([A-Za-z\d]+[-.])+[A-Za-z\d]{2,4}$/;
			// 如果使用者輸入的郵箱地址不符合規則
			if (!reg.test(email)) {
				// 給出使用者提示
				info.innerHTML = '請輸入符合規則的郵箱地址';
				// 讓提示資訊顯示為錯誤提示資訊的樣式
				info.className = 'bg-danger';
				// 阻止程式向下執行
				return;
			}

			// 向伺服器端傳送請求
			ajax({
				type: 'get',
				url: 'http://localhost:3000/verifyEmailAdress',
				data: {
					email: email
				},
				success: function (result) {
					console.log(result);
					info.innerHTML = result.message;
					info.className = 'bg-success';
				},
				error: function (result) {
					console.log(result)
					info.innerHTML = result.message;
					info.className = 'bg-danger';
				}
			});

		}
	</script>
</body>
</html>

執行app.js,然後在瀏覽器中輸入http://localhost:3000/驗證郵箱地址唯一性.html

4.3.2 搜尋框內容自動提示

搜尋框內容自動提示

在public資料夾下面新建搜尋框內容自動提示.html

<!DOCTYPE html>
<html lang="en">
<head>
	<meta charset="UTF-8">
	<title>搜尋框輸入文字自動提示</title>
	<link rel="stylesheet" href="/assets/bootstrap/dist/css/bootstrap.min.css">
	<style type="text/css">
		.container {
			padding-top: 150px;
		}
		.list-group {
			display: none;
		}
	</style>
</head>
<body>
	<div class="container">
		<div class="form-group">
			<input type="text" class="form-control" placeholder="請輸入搜尋關鍵字" id="search">
			<ul class="list-group" id="list-box">
				
			</ul>
		</div>
	</div>
	<script src="/js/ajax.js"></script>
	<script src="/js/template-web.js"></script>
	<script type="text/html" id="tpl">
		{{each result}}
			<li class="list-group-item">{{$value}}</li>
		{{/each}}
	</script>
	<script>
		// 獲取搜尋框
		var searchInp = document.getElementById('search');
		// 獲取提示文字的存放容器
		var listBox = document.getElementById('list-box');
		// 儲存定時器的變數
		var timer = null;
		// 當使用者在文字框中輸入的時候觸發
		searchInp.oninput = function () {
			// 清除上一次開啟的定時器
			clearTimeout(timer);
			// 獲取使用者輸入的內容
			var key = this.value;
			// 如果使用者沒有在搜尋框中輸入內容
			if (key.trim().length == 0) {
				// 將提示下拉框隱藏掉
				listBox.style.display = 'none';
				// 阻止程式向下執行
				return;
			}

			// 開啟定時器 讓請求延遲傳送
			timer = setTimeout(function () {
				// 向伺服器端傳送請求
				// 向伺服器端索取和使用者輸入關鍵字相關的內容
				ajax({
					type: 'get',
					url: 'http://localhost:3000/searchAutoPrompt',
					data: {
						key: key
					},
					success: function (result) {
						// 使用模板引擎拼接字串
						var html = template('tpl', {result: result});
						// 將拼接好的字串顯示在頁面中
						listBox.innerHTML = html;
						// 顯示ul容器
						listBox.style.display = 'block';
					}
				})
			}, 800)
			
		}
	</script>
</body>
</html>

執行app.js,然後在瀏覽器中輸入http://localhost:3000/搜尋框內容自動提示.html

4.3.3 省市區聯動

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片儲存下來直接上傳(img-kLWXExa1-1647700819349)(C:/Users/27532/Desktop/省市區三級聯動.gif)]

在public資料夾下面新建省市區聯動.html

<!DOCTYPE html>
<html lang="en">
<head>
	<meta charset="UTF-8">
	<title>搜尋框輸入文字自動提示</title>
	<link rel="stylesheet" href="/assets/bootstrap/dist/css/bootstrap.min.css">
	<style type="text/css">
		.container {
			padding-top: 150px;
		}
	</style>
</head>
<body>
	<div class="container">
		<div class="form-inline">
			<div class="form-group">
				<select class="form-control" id="province"></select>
			</div>
			<div class="form-group">
				<select class="form-control" id="city">
					<option>請選擇城市</option>
				</select>
			</div>
			<div class="form-group">
				<select class="form-control" id="area">
					<option>請選擇縣城</option>
				</select>
			</div>
		</div>
	</div>
	<script src="/js/ajax.js"></script>
	<script src="/js/template-web.js"></script>
	<!-- 省份模板 -->
	<script type="text/html" id="provinceTpl">
		<option>請選擇省份</option>
		{{each province}}
			<option value="{{$value.id}}">{{$value.name}}</option>
		{{/each}}
	</script>
	<!-- 城市模板 -->
	<script type="text/html" id="cityTpl">
		<option>請選擇城市</option>
		{{each city}}
			<option value="{{$value.id}}">{{$value.name}}</option>
		{{/each}}
	</script>
	<!-- 縣城模板 -->
	<script type="text/html" id="areaTpl">
		<option>請選擇縣城</option>
		{{each area}}
			<option value="{{$value.id}}">{{$value.name}}</option>
		{{/each}}
	</script>
	<script>
		// 獲取省市區下拉框元素
		var province = document.getElementById('province');
		var city = document.getElementById('city');
		var area = document.getElementById('area');
		// 獲取省份資訊
		ajax({
			type: 'get',
			url: 'http://localhost:3000/province',
			success: function (data) {
				// 將伺服器端返回的資料和html進行拼接
				var html = template('provinceTpl', {province: data});
				// 將拼接好的html字串顯示在頁面中
				province.innerHTML = html;
			}
		});

		// 為省份的下拉框新增值改變事件
		province.onchange = function () {
			// 獲取省份id
			var pid = this.value;

			// 清空縣城下拉框中的資料
			var html = template('areaTpl', {area: []});
			area.innerHTML = html;

			// 根據省份id獲取城市資訊
			ajax({
				type: 'get',
				url: '/cities',
				data: {
					id: pid
				},
				success: function (data) {
					var html = template('cityTpl', {city: data});
					city.innerHTML = html;
				}
			})
		};

		// 當使用者選擇城市的時候
		city.onchange = function () {
			// 獲取城市id
			var cid = this.value;
			// 根據城市id獲取縣城資訊
			ajax({
				type: 'get',
				url: 'http://localhost:3000/areas',
				data: {
					id: cid
				},
				success: function(data) {
					var html = template('areaTpl', {area: data});
					area.innerHTML = html;
				}
			})
		}
	</script>
</body>
</html>

執行app.js,然後在瀏覽器中輸入http://localhost:3000/省市區聯動.html