(註:本章講解涉及部分後端知識,將以php提供數據的方式進行相應的demo實現) 1:ajax的概念 全稱:Asynchronous Javascript And Xml AJAX不是一種新的編程語言,而是一種用於創建更快更好以及交互性更強的WEB應用程式技術,該技術在98年前後得到了應用。通過AJ ...
(註:本章講解涉及部分後端知識,將以php提供數據的方式進行相應的demo實現)
1:ajax的概念
全稱:Asynchronous Javascript And Xml
AJAX不是一種新的編程語言,而是一種用於創建更快更好以及交互性更強的WEB應用程式技術,該技術在98年前後得到了應用。通過AJAX,你的JS可以通過JS的XMLHttpRequest對象在頁面不重載的情況下與伺服器直接進行通信。這樣可以在伺服器請求到想要的數據,而不是整個頁面。AJAX的核心就是JS的XMLHttpRequest對象。xhr對象是在IE5中首次引入,它是一種支持非同步請求的對象。
2:ajax的優勢
- 無刷新更新數據。
- 非同步與伺服器通信。
- 基於標準被廣泛支持。
- 前端與後端分離。
- 節省帶寬。
3:編寫步驟
1.創建XMLHttpRequest對象。
所有現代瀏覽器(IE7+,chrome,firefox,opera,safari)均內建XMLHttpRequest對象。但是IE5、6使用ActiveXObject對象。
function getAjax() {
var xmlhttp = null;
if(window.ActiveXObject){
//針對IE
xmlhttp = new ActiveXObject(’Microsoft.XMLHTTP’);
} else if(window.XMLHttpRequest){
xmlhttp = new XMLHttpRequest();
}
return xmlhttp;
}
2.打開與Server的連接,指定發送方式、URL以及許可權等。
open方法:創建新的HTTP請求,並指定此請求的方法,URL以及驗證信息。
xhr.open(type, url, async, user, password);
type:HTTP請求方式,GET、POST等。大小寫不敏感。
url:請求地址。(get請求如果有傳值直接以url?param=value的方式傳遞。post的方式直接在發送指令的時候傳遞)
async:布爾型,請求是否為非同步方式。預設為true。如果為真,當狀態改變時會調用onreadystatechange屬性指定的回調函數。(可選)
註釋:當您使用 async=false 時,請不要編寫 onreadystatechange 函數 - 把代碼放到 send() 語句後面即可:
xmlhttp.open("GET","test.txt",false);
xmlhttp.send();
document.getElementById("myDiv").innerHTML=xmlhttp.responseText;
user:如果伺服器需要驗證,此處指定用戶名,如果未指定,當伺服器需要驗證時,會彈出驗證視窗。(少用僅瞭解)
password:驗證信息中的密碼部分,如果用戶名為空,則此值將會被忽略。(少用僅瞭解)
註:在AJAX中,其實我們就是來模擬正常的表單提交數據。正常的表單在POST數據時,會發送Content-Type欄位,所以我們在AJAX中就要指定該欄位值為application/x-www-form-urlencoded。並且對欄位名稱和值進行編碼處理在發送。使用setRequestHeader:單獨指定請求的某個HTTP頭。
註:一些特殊字元可能與代碼中的字元衝突(如URL中分隔符&等等),故數據應使用encocdeURIComponent()函數進行編碼。
3.發送指令。
send():發送請求到HTTP伺服器並接收回應。
此方法的同步或非同步方式取決於open方法中的async參數,如果async為true,此方法將立即返回,如果為false,此方法將會等待請求完成或者超時時才會返回。
xhr.send(body);
body:通過此請求發送的數據。GET請求設置為null即可。
post和get方式實現ajax的不同之處
xmlhttp.open("GET","demo_get2.asp?fname=Bill&lname=Gates",true); xmlhttp.send(); xmlhttp.open("POST","ajax_test.asp",true); xmlhttp.setRequestHeader("Content-type","application/x-www-form-urlencoded"); xmlhttp.send("fname=Bill&lname=Gates"); //傳參方式的不同以及編碼方式的不同post提交需要被別,則需要指定編碼方式與get的方式一樣
4.等待並接收伺服器返回的處理結果。
5.客戶端接收。
6.釋放XMLHttpRequest對象。
4:回調函數
通過onreadystatechange屬性指定readystate屬性改變時的事件處理回調函數。
xhr.onreadystatechange = function(){}
readyState屬性:返回請求的當前狀態。
- 0:對象已建立,尚未初始化(未調用open方法)。
- 1:對象已建立,尚未調用send方法。
- 2:send方法已調用。但是當前的狀態以及HTTP狀態未知。
- 3:開始接收數據,因為響應以及HTTP頭不全,這時通過responseBody和responseText獲取部分數據會出現錯誤。
- 4:數據接收完畢,此時可以通過responseBody和responseText獲取完整的響應數據。
status屬性:返回當前請求的狀態碼。
- 200 OK:請求文檔已經找到,並正確返回。
- 304 Not Modified:擁有一個本地的緩存副本,伺服器端內容與此相同。
- 403 Forbidden:請求者對所請求的文檔不具有相應的許可權。
- 404 Not Found:請求的文檔沒找到。
statusText屬性:返回當前請求的響應行信息。
responseXML屬性:將響應信息格式化為XML Document對象返回。
responseText屬性:將響應信息作為字元串返回。
5:JS解析JSON
JSON:Javascript Object Notation,一種輕量級的基於文本的數據交換格式,易於人閱讀和編寫,也能提高網路傳輸速率。
ES5新增的兩個方法:
JSON.parse:將JSON字元串數據轉換為JSON對象。
JSON.stringify:將JSON對象轉換為JSON字元串。
註:1、瀏覽器支持:IE8+。
2、JSON格式的字元串裡面的key或者字元串型的value都必須用雙引號包裹
6:局部數據刷新
demo講解上述提到的知識點:操作相應的DOM節點(例如評論列表的分頁效果)
html頁面
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>新聞列表</title>
<style type="text/css">
*{
margin:0;
padding:0;
}
.box{
width: 80%;
margin: 0 auto;
}
.newsTitle{
font-size: 24px;
text-align: center;
margin: 0 auto;
padding: 20px 10px;
font-weight: normal;
letter-spacing: 10px;
}
.newsList{
display: block;
margin: 0 auto;
line-height: 50px;
font-size: 16px;
list-style: none;
}
.newsList li{
border-top: 1px dashed #f4f4f4;
padding: 0 20px;
}
.newsList li:nth-child(odd){
background: #f3f3f3;
}
.newsPage{
display: block;
margin: 0 auto;
text-align: center;
}
.newsPage li{
margin:20px 5px;
display: inline-block;
padding: 5px 10px;
border:1px solid #f3f3f3;
font-size: 14px;
}
</style>
</head>
<body>
<div class="box">
<h3 class="newsTitle">新聞列表</h3>
<ul id="newsList" class="newsList">
<li>東航首位外籍女機長:來中國是最好的選擇</li>
<li>東航首位外籍女機長:來中國是最好的選擇</li>
<li>東航首位外籍女機長:來中國是最好的選擇</li>
<li>東航首位外籍女機長:來中國是最好的選擇</li>
<li>東航首位外籍女機長:來中國是最好的選擇</li>
</ul>
<ul id="newsPage" class="newsPage">
<li data-page="1">1</li>
<li data-page="2">2</li>
<li data-page="3">3</li>
<li data-page="4">4</li>
<li data-page="5">5</li>
<li data-page="6">6</li>
<li data-page="7">7</li>
<li data-page="8">8</li>
<li data-page="9">9</li>
<li data-page="10">10</li>
</ul>
</div>
</body>
<script type="text/javascript" src="ajax.js"></script>
</html>
js頁面
var oNewsPage=document.getElementById('newsPage');
var oNewsPage=Array.from(oNewsPage.children);
var oNewsList=document.getElementById('newsList');
oNewsPage.forEach(function (value) {
value.onclick=function () {
oNewsList.innerHTML = "";
//獲取頁碼值
var pageNum = value.getAttribute("data-page");
/*步驟一:創建XMLHttpRequest對象*/
var xhr = new XMLHttpRequest();
/*步驟二:請求配置*/
var pageNum = "pageNum="+ pageNum;
//xhr.open('get','ajax.php?'+pageNum,true);
xhr.open('post','ajax.php',true);
/*步驟四:接受返回結果*/
xhr.onreadystatechange=function () {
//根據狀態返回碼判定請求的狀態
if (xhr.readyState === 4 && xhr.status === 200) {
//獲取返回結果
var newsList = JSON.parse(xhr.responseText)//返回結果通過json將字元串進行轉換
//數據結果渲染到頁面
newsList.forEach( v => {
var oLi = document.createElement('li');
oLi.innerHTML = v.title;
oNewsList.appendChild(oLi);
});
}
}
/*步驟三:發送請求*/
//post請求需要設置編碼格式 否則無法解析數據
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
xhr.send(pageNum);//post方式將參數傳遞的方式
}
})
php後臺頁面
<?php
$pageNum=$_POST['pageNum']; //接收參數頁碼
//通過頁碼判斷載入的內容
if ($pageNum == 1) {
//通過二維數組存儲數據
$arr = [
[
'id' => 1,
'title' => '東航首位外籍女機長:來中國是最好的選擇'
],
[
'id' => 2,
'title' => '南航首位外籍女機長:來中國是最好的選擇'
],
[
'id' => 3,
'title' => '國際航空首位外籍女機長:來中國是最好的選擇'
]
];
}
else if ($pageNum == 2) {
$arr=[
[
'id' =>4,
'title'=>'世界那麼大不如出去走走'
],
[
'id'=>5,
'title'=>'今天陽光明媚適合出去走走'
]
];
}
else{
$arr=[
[
'id' =>6,
'title'=>'我是第三頁以後的內容'
],
[
'id'=>7,
'title'=>'我是第三頁以後的內容'
]
];
}
echo json_encode($arr);
?>
7:前後端分離(ajax函數的封裝)
後臺只管數據輸出和業務邏輯處理,前端負責交互邏輯和界面展示。簡單的說:前端靜態頁面中沒有有後臺程式代碼,後臺輸出不帶有HTML標簽的數據。前後端分離靠ajax來實現數據的交互。
demo通過封裝的ajax實現載入更多的功能以及通過事件源對象刪除單條數據(本來打算將源碼托管到GitHub,賬號丟失)
*文件夾目錄
1.html頁面
<!doctype html> <html lang="en"> <head> <meta charset="UTF-8"> <title>新聞列表</title> <link rel="stylesheet" href="styles/ajax.css"> </head> <body> <div class="box"> <h3 class="newsTitle">新聞列表</h3> <ul id="newsList" class="newsList"> <li> 冬天來了,春天還會遠麽 <a class="deleteBtn" href="#">×</a> </li> <li> 面朝大海,幸福花開 <a class="deleteBtn" href="#">×</a> </li> <li> Nothing is impossible! <a class="deleteBtn" href="#">×</a> </li> <li> 水能載舟亦能覆舟 <a class="deleteBtn" href="#">×</a> </li> <li> 中國是最好的選擇 <a class="deleteBtn" href="#">×</a> </li> </ul> <div id="loadMoreInfo" class="loadMoreInfo"> <img id="loadingImg" class="loading" src="images/timg.gif" /> 載入更多... </div> </div> </body> <script type="text/javascript" src="scripts/common.js"></script> <script type="text/javascript" src="scripts/ajax.js"></script> </html>ajax.html
2.css文件
@charset "utf-8"; /* CSS Document */ *{ margin:0; padding:0; } body{ background: #7CCD7C; } .box{ width: 80%; margin: 0 auto; } .newsTitle{ font-size: 24px; text-align: center; margin: 0 auto; padding: 20px 10px; font-weight: normal; letter-spacing: 10px; } .newsList{ display: block; margin: 0 auto; line-height: 50px; font-size: 16px; list-style: none; } .newsList li{ padding: 0 20px; box-shadow: 0 0 10px #eeeeee; } .box .newsList li:hover{ background: #B0E2FF; } .newsList li:nth-child(odd){ background: #f3f3f3; } .newsList li:nth-child(even){ background: #eeeeee; } .deleteBtn{ float: right; text-decoration: none; font-size: 16px; color: #555555; } .loadMoreInfo{ margin-top: 10px; text-align: center; line-height: 50px; font-size: 16px; background: #ffffff; cursor: pointer; vertical-align: middle; margin-bottom: 20px; } .loadMoreInfo img{ display: none; width: 20px; height: 20px; vertical-align: middle; }ajax.css
3.公共js(封裝的$函數以及ajax函數)
/* 封裝$函數 參數說明: id:標簽的id屬性值 */ function $(id) { return document.getElementById(id); } /* 封裝ajax函數 參數說明:對象作為參數不用考慮參數順序問題 type:請求類型; url:請求訪問地址; param:請求數據,支持對象也支持字元串; asyn:是否非同步載入,boolean類型,true非同步,false同步 beforesend:回調函數在發送請求之前執行 success:回調函數,請求成功時執行 complete:回調函數,請求完成後執行 */ function ajax({type,url,param,asyn=true,beforesend,success,complete}) { //創建XMLHttpRequest對象 var xhr = new XMLHttpRequest(); //param參數為對象時進行拼接字元串 if (param && typeof(param) === 'object') { var str; for(var child in param){ //encodeURIComponent編碼參數,避免特殊字元分割時丟失 str+=encodeURIComponent(child) + '=' + encodeURIComponent(param[child]) + '&' } param=str.slice(0,-1);//截取拼接完成後最後的&符 } //get請求對url傳參進行拼接 if (type.toUpperCase()==="GET" && param) { url+='?'+param; } //請求配置 xhr.open(type , url, asyn); //獲取返回的數據 xhr.onreadystatechange = function () { if (xhr.readyState === 4 && xhr.status === 200) { //獲取返回的數據並轉換 success(xhr.responseText); } //請求完成後的函數 complete && complete(); } //發送請求 beforesend && beforesend(); if (type.toUpperCase()==="POST") { //post請求設置編碼方式 xhr.setRequestHeader("Content-Type",'application/x-www-form-urlencoded') //post請求時通過send向body傳參(無論param參數是否為空,必須傳) xhr.send(param); } else{ //get請求發送請求 xhr.send(); } }common.js
4.頁面js
var oLoadMoreInfo = $("loadMoreInfo"); var oNewsList = $("newsList"); var oLoadingImg = $("loadingImg"); oLoadMoreInfo.onclick=function () { ajax({ type:'post', url:'php/ajax.php', beforesend:function () { //發送前顯示載入過程中的動態效果 oLoadingImg.style.display="inline-block" }, success:function (data) { //接受返回的數據進行轉換並渲染到頁面 var data=JSON.parse(data); data.forEach(function (value) { var li=document.createElement("li"); li.innerHTML=value+'<a class="deleteBtn" href="#">×</a>'; oNewsList.appendChild(li); }) }, //完成後顯示渲染出來的網頁效果,關閉等待效果 complete:function () { oLoadingImg.style.display="none" } }) } newsList.onclick=function (ev) { var e=ev||window.event; //獲取事件原對象 var tar=e.target||e.srcElement; //判斷事件原對象是否為a標簽nodenName的值都是大寫 if (tar.nodeName==="A") { oNewsList.removeChild(tar.parentNode); } }ajax.js
5.php後臺
<?php sleep(2);//本地測試資源載入速度過快無法看出載入的動態效果 $news=[ '東航首位外籍女機長:來中國是最好的選擇', '南航首位外籍女機長:來中國是最好的選擇', '國際航空首位外籍女機長:來中國是最好的選擇', '東航首位外籍女機長:來中國是最好的選擇', '南航首位外籍女機長:來中國是最好的選擇', '國際航空首位外籍女機長:來中國是最好的選擇', '東航首位外籍女機長:來中國是最好的選擇', '南航首位外籍女機長:來中國是最好的選擇', '國際航空首位外籍女機長:來中國是最好的選擇', '東航首位外籍女機長:來中國是最好的選擇', '南航首位外籍女機長:來中國是最好的選擇', '國際航空首位外籍女機長:來中國是最好的選擇' ]; echo json_encode($news, JSON_UNESCAPED_UNICODE); /* PHP的json_encode來處理中文的時候, 中文都會被編碼, 變成不可讀的, 類似”\u***”的格式,PHP5.4,JSON_UNESCAPED_UNICODE:Json不要編碼Unicode*/ajax.php
6.圖片資源
8:ajax的依賴調用
在使用ajax非同步調用的時候,可能碰到同時調用多個ajax的情況,而且多個ajax之間還存在依賴關係。
引用回調函數的嵌套說明ajax的以來調用:
<!doctype html> <html lang="en"> <head> <meta charset="UTF-8"> <title>回調函數的嵌套</title> </head> <body> </body> <script type="text/javascript"> function a(d, e) { d && d(e); } function b(f) { console.log('函數b!'); f && f(); } a(b, function() { console.log('實參e(回調函數)'); }); /*回調函數的簡單理解:函數在方法中以參數的方式進行傳遞*/ /*這裡提供兩種形式理解回調函數的嵌套*/ /* function a(d, e) { d && d(e); } a( function b(f){ console.log('函數b!'); f && f(); }, function() { console.log('實參e(回調函數)'); } );*/ </script> </html>回調函數的嵌套
demo:逐個非同步驗證驗證碼、用戶名和密碼,只有用戶通過,才能非同步驗證密碼。
此處只給出部分重要代碼逐個調用封裝的ajax方法(前後端分離的demo封裝的ajax方法)進行非同步驗證驗證碼、用戶名和密碼:
// 表單提交事件用純ajax實現表單驗證 // 驗證驗證碼 var sCaptcha = oCaptcha.value; ajax({ type: 'POST', url: 'login.php', data: {captcha: sCaptcha}, success: function (data) { if(data == 1) { // 驗證賬號 var sAccount = oAccount.value; ajax({ type: 'POST', url: 'login.php', data: {account: sAccount}, success: function (data) { if(data == 1) { // 驗證密碼 var sPassword = oPassword.value; ajax({ type: 'POST', url: 'login.php', data: {password: sPassword}, success: function (data) { if(data == 1) { oShow.innerHTML = '登錄成功!'; } else { oShow.innerHTML = '密碼錯誤!'; } } }); } else { oShow.innerHTML = '賬號錯誤!'; } } }); } else { oShow.innerHTML = '驗證碼錯誤!'; } } });ajax非同步調用demo
9:Promise介紹
Promise函數,是一個構造函數,它實際上是對回調函數的一種封裝 對非同步編程的一種改進。
1>Promise對象的特點:
1.Promise對象代表一個非同步操作,有三種狀態:Pending(進行中)、Resolved(已完成,又稱Fulfilled)和Rejected(已失敗)。只有非同步操作的結果,可以決定當前是哪一種狀態,任何其他操作都無法改變這個狀態。這也是Promise這個名字的由來,它的英語意思就是“承諾”,表示其他手段無法改變。
2.一旦狀態改變,就不會再變,任何時候都可以得到這個結果。Promise對象的狀態改變,只有兩種可能:從Pending變為Resolved和從Pending變為Rejected。只要這兩種情況發生,狀態就凝固了,不會再變了,會一直保持這個結果。就算改變已經發生了,你再對Promise對象添加回調函數,也會立即得到這個結果。這與