Asynchronous JavaScript and XML
(异步的javascript和XML),为何会有这么一种技术的出现呢,由于前端时常会有这样的需求,咱们只要局部刷新,不须要整一个刷新的时候,便催生了这样的技术。AJAX优势:javascript
AJAX缺点:php
AJAX应用和传统Web应用有什么不一样?css
基本示例:html
//建立 XMLHttpRequest 对象 var ajax = new XMLHttpRequest(); // 规定请求的类型、URL 以及是否异步处理请求。 ajax.open('GET',url,true); //发送信息至服务器时内容编码类型 ajax.setRequestHeader("Content-type", "application/x-www-form-urlencoded"); //发送请求 ajax.send(null); //接受服务器响应数据 ajax.onreadystatechange = function () { if (obj.readyState == 4 && (obj.status == 200 || obj.status == 304)) { } };
简单应用示例:前端
oBtn.onclick = function () { //建立对象 var xhr = getXMLHttpRequest(); //当xhr对象的readyState属性发生改变的时候触发 xhr.onreadystatechange = function () { if (xhr.readyState == 4) { //ajax的状态4表示加载完成 if (xhr.status >= 200 && xhr.status < 300 || xhr.status == 304) {// http的状态是以上才算正常 pp.innerHTML = xhr.responseText; } else { throw new Error("文件读取错误"); } } } //open方法表示配置此次请求 xhr.open("get", "test.txt", true); //发送请求 //get请求中,没有任何的上行主体的,因此写null xhr.send(null); } //工厂函数(兼容浏览器) function getXMLHttpRequest() { if (window.XMLHttpRequest) { //高级浏览器,IE7,IE7+ return new XMLHttpRequest(); } else { //老版本浏览器,IE6 return new ActiveXObject("Microsoft.XMLHTTP"); } }
open()
方法xhr.open("get","test.txt",true);
java
调用open方法并不会真正发送请求,而只是启动一个请求以备发送。jquery
它接受三个参数:web
send()
方法若是要发送请求,用send()
方法。ajax
要发送特定的请求,须要调用send()方法。编程
请求主体:HTTP上行请求,有头部、主体。
一但调用send()方法,HTTP上行请求就将发出。
readyState
属性表示“就绪状态”
通常来讲,只须要使用4状态就能够了
只要这个属性值发生了变化,就会触发一个事件onreadystatechange
事件,就可使用xhr.onreadystatechange = function(){}
来捕获readyState
变化以后作的事情。
ajax 也是使用 http 协议的,因此也须要了解 http协议的状态。
1XX 100-101 信息提示 2XX 200-206 成功 3XX 300-305 重定向 4XX 400-415 客户端错误 5XX 500-505 服务器错误 200 OK 服务器成功处理了请求(这个是咱们见到最多的) 301/302 Moved Permanently(重定向)请求的URL已移走。Response中应该包含一个Location URL, 说明资源如今所处的位置 304 Not Modified(未修改)客户的缓存资源是最新的, 要客户端使用缓存 404 Not Found 未找到资源 501 Internal Server Error服务器遇到一个错误,使其没法对请求提供服务
这是比较齐全的状态表:
$
其实也就是window.$
良好的代码风格
//=======================属性=======================
//=======================方法=====================
//=======================内部方法=====================
_
表明内部方法或者属性,主要是给编程人员看的arguments.length
来实现函数重载(function () { var myAjax = {}; //空对象 //向外暴露这么一个全局变量 //就是这个函数的命名空间 window.myAjax = myAjax; //=======================属性======================= myAjax.version = "0.2.0"; //=======================方法======================= myAjax.get = function () { //参数个数 var argLength = arguments.length; var URL, json, callback; if (argLength == 2 && typeof arguments[0] == "string" && typeof arguments[1] == "function") { //两个参数 URL = arguments[0]; callback = arguments[1]; //传给咱们的核心函数来发出Ajax请求 myAjax._doAjax("get", URL, null, callback); } else if (argLength == 3 && typeof arguments[0] == "string" && typeof arguments[1] == "object" && typeof arguments[2] == "function") { //3个参数 URL = arguments[0]; json = arguments[1]; callback = arguments[2]; //传给咱们的核心函数来发出Ajax请求 myAjax._doAjax("get", URL, json, callback); } else { throw new Error("get方法参数错误!"); } } myAjax.post = function () { //参数个数 var argLength = arguments.length; if (argLength == 3 && typeof arguments[0] == "string" && typeof arguments[1] == "object" && typeof arguments[2] == "function") { //3个参数 var URL = arguments[0]; var json = arguments[1]; var callback = arguments[2]; //传给咱们的核心函数来发出Ajax请求 myAjax._doAjax("post", URL, json, callback); } else { throw new Error("post方法参数错误!"); } } //post方式提交全部表单 myAjax.postAllForm = function (URL, formId, callback) { //将表单数据转为json var json = myAjax._formSerialize(formId); myAjax._doAjax("post", URL, json, callback); } //=======================内部方法===================== //将JSON转换为URL查询参数写法 //传入{"id":12,"name":"考拉"} //返回id=12&name=%45%45%ED myAjax._JSONtoURLparams = function (json) { var arrParts = []; //每一个小部分的数组 for (k in json) { //组成参数数组,而后用& 链接 arrParts.push(k + "=" + encodeURIComponent(json[k]));//须要uri编码特殊字符串,例如中文或者符号 } return arrParts.join("&"); } //最核心的发出Ajax请求的方法 myAjax._doAjax = function (method, URL, json, callback) { //Ajax的几个公式 if (XMLHttpRequest) { var xhr = new XMLHttpRequest(); } else { var xhr = ActiveXObject("Microsoft.XMLHTTP"); } xhr.onreadystatechange = function () { if (xhr.readyState == 4) { if (xhr.status >= 200 && xhr.status < 300 || xhr.status == 304) { callback(null, xhr.responseText); } else { callback("文件没有找到" + xhr.status, null); } } } //如今要根据请求类型进行判断 if (method == "get") { //请求类型是get //若是用户传输了json,此时要连字 if (json) { //判断URL自己是否有?,没有就须要&链接 var combineChar = URL.indexOf("?") == -1 ? "?" : "&"; //将json转为url参数后拼接 URL += combineChar + myAjax._JSONtoURLparams(json); } //增长一个随机数参数,防止缓存 var combineChar = URL.indexOf("?") == -1 ? "?" : "&"; URL += combineChar + Math.random().toString().substr(2); xhr.open("get", URL, true); xhr.send(null); } else if (method == "post") { //增长一个随机数参数,防止缓存 var combineChar = URL.indexOf("?") == -1 ? "?" : "&"; URL += combineChar + Math.random().toString().substr(2); xhr.open("post", URL, true); //post须要有header xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded"); xhr.send(myAjax._JSONtoURLparams(json)); } } })();
当Ajax第一次发送请求后,会把请求的URL和返回的响应结果保存在缓存内,当下一次调用Ajax发送相同的请求时,注意,这里相同的请求指的是URL彻底相同,包括参数,浏览器就不会与服务器交互,而是直接从缓存中把数据取出来,这是为了提升页面的响应速度和用户体验。(服务端也会收到请求响应304)
浏览器会自做主张的把全部异步请求来的文件缓存,当下一次请求的URL和以前的同样,那么浏览器将不会发送这个请求,而是直接把缓存的内容当作xhr.responseText
。
须要注意的是,post 请求方式不会被缓存,只有 get 请求方式会被缓存。
方法1:随机数
//随机数,咱们不要0. 只要小数点后面的数字: var random = Math.random().toString().substring(2); //URL上面就拼接一个随机字符串,保证每次URL不同 myAjax.get("text.txt?" + random,function(err,data){ alert(data); });
方法2:时间戳
从1970年1月1日0:00到这一刻的毫秒数。就叫作时间戳。英语属于timestamp。
JS里面时间戳就是
//时间戳: var timestamp = Date.parse(new Date()); //URL上面就拼接一个随机字符串,保证每次URL不同 myAjax.get("text.txt?" + timestamp,function(err,data){ alert(data); });
总的来讲,原理就是经过将 get 请求的 url 作成每次都不同,这样就不会被浏览器缓存了。
判断返回的 json 数据是否可用,这个只是属性一些平常使用 ajax 的点而已。
经过JSON.parse
转换为json格式,若是没法转换,会报错。
var jsonObj = JSON.parse(str);
hasOwnProperty 这个方法可以判断对象里面是否有某个键属性。
var obj = {"a":1,"b":2}; console.log(obj.hasOwnProperty("aaa"));
这个示例比较详细,而且加入了错误以后的处理:
//获得页面上的用户名的文本框、下拉列表 var oUsername = document.getElementById("username"); var oDomain = document.getElementById("domain"); //获得good、bad、tuijian var oTuijian = document.getElementById("tuijian"); var oBadTip = document.getElementById("badTip"); var oGoodTip = document.getElementById("goodTip"); //获得4个li(事先给定或者从其余接口获取的) var tuijianLis = oTuijian.getElementsByTagName("li"); //失去焦点和改变下拉列表,都是作同一个事情 oUsername.onblur = check; oDomain.onchange = check; function check(){ clearAllTip(); //清除全部提示框 //获得值 var username = oUsername.value; //文本框 //获取全部用户选中的邮箱选项,并放入到domain数组 var domain = (function(){ //获得全部option var options = oDomain.getElementsByTagName("option"); //遍历,看看哪一个被selected了 for(var i = 0 ; i < options.length ; i++){ if(options[i].selected){ return options[i].value; } } })(); //若是这个值是空,那么什么也不作。 if(!username) { return; } //正则验证合法性 //6~18个字符,可以使用字母、数字、下划线,需以字母开头 var reg = /^[A-Za-z][\w]{5,17}$/; if (!reg.test(username)) { showWrong("6~18个字符,可以使用字母、数字、下划线,需以字母开头"); return; //不合法的时候,就返回,不执行下面的语句了 } //这里请求一个静态json,实际上要请求后台php页面。 myAjax.post("check.json",{"username" : username},function(err,data){ if(err){ showWrong("服务器错误,稍后再试"); return; } //转为json格式: var dataJSON = JSON.parse(data); //得到result对象(即获取服务器返回的验证结果) var result = dataJSON.result; //若是没有result对象。就创造一个result对象 if(!result){ var result = {}; //由于须要给后续的hasOwnProperty校验 } //检测是否可用 if(result.hasOwnProperty(domain)){//服务器验证结果跟用户选项一致的时候 showRight("恭喜,可用!"); }else{ //服务器验证结果跟用户选项不一致的时候 //就要给用户显示推荐的邮箱 oTuijian.style.display = "block"; //显示推荐框 //咱们要依次查找这些域名是否可用(事先给定或者从其余接口获取的) var domainArray = ["163.com","126.com","yeah.net"]; //咱们再写一个结果数组 var usableArray = []; //遍历domainArray,把domainArray中的每个项,进行检测 //检测result对象中是否是有这个属性 //直接获取了判断的结果的数组 for(var i = 0 ; i < domainArray.length ; i++){ var tOrf = result.hasOwnProperty(domainArray[i]) ? true : false; usableArray.push(tOrf); } console.log(usableArray); //遍历4个li标签,根据咱们的结果数组来决定他们 //是否有disable类、里面的span的内容、b的内容 for(var i = 0 ; i < tuijianLis.length ; i++){ var thisli = tuijianLis[i]; //经过判断的结果的数组的值来控制是否设置class //决定这个li是否有disable类 thisli.className = usableArray[i] ? "" : "disable"; //往span里面写内容 //获得这惟一一个span var thisspan = thisli.getElementsByTagName("span")[0]; //有时候须要从新解析一些值的格式 if(domainArray[i] == "vip163"){ domainArray[i] = "vip.163.com"; } thisspan.innerHTML = username + "@" + domainArray[i]; //往b里面写内容 var thisb = thisli.getElementsByTagName("b")[0]; //经过判断的结果的数组的值来控制显示内容 thisb.innerHTML = usableArray[i] ? "可使用" : "已经被占用"; } } }); } //获得焦点 oUsername.onfocus = clearAllTip; function clearAllTip(){ //让全部的提示框消失 oTuijian.style.display = "none"; oBadTip.style.display = "none"; oGoodTip.style.display = "none"; } //显示错误提示框 function showWrong(info){ oBadTip.innerHTML = info; oBadTip.style.display = "block"; } //显示正确提示框 function showRight(info){ oGoodTip.innerHTML = info; oGoodTip.style.display = "block"; }
已经在另一篇文章里面说过了,jsonp 是其中一种解决办法。
//给按钮添加监听 oBtn.onclick = function(){ //获得用户填写的手机号 var danhao = odanhao.value; var kuaidigongsi = okuaidigongsi.value; //建立script var script = document.createElement("script"); script.src = "https://sp0.baidu.com/9_Q4sjW91Qh3otqbppnN2DJv/pae/channel/data/asyncqury?cb=xixi&appid=4001&com=" + kuaidigongsi +"&nu=" + danhao +"&vcode=&token=&_=1438916675664" //追加而后删除 document.body.appendChild(script); document.body.removeChild(script); } function xixi(data){ console.log(data); }
要实现2个地方:
咱们须要知道:
$(document).height();
获取,视口底部来触发ajax 获取下一页的数据这里分红三列瀑布流,组成一个数组管理
图片的插入次序不是固定的(ajax异步),因此用以前的数组进行管理,每次都对最小值的高度插入值,这样就能保证每次都往最靠里面的图片位置进行放置
瀑布流的数组样例以下:
// 第一行 // [0,0,0] minIndex: 0 left 0 top 20 [558,0,0 ] // [558,0,0] minIndex: 1 left 300 top 20 [558,386,0] // [558,386,0] minIndex:2 left 600 top 20 [558,386,722] //第二行 //[558,386,722] minIndex:1 left 300 top 406 [558, 943, 722] //[558, 943, 722] minIndex:0 left 0 top 578 [1193, 943, 722] //[1193, 943, 722] minIndex:2 left 600 top 742 [1193, 943, 1128]
<!DOCTYPE html> <html lang="en"> <head> <meta http-equiv="Content-Type" content="text/html;charset=UTF-8"> <title>Document</title> <style type="text/css"> <!--省略样式--> </style> </head> <body> <!--加载logo,默认隐藏--> <div class="waterfall"> </div> <div class="end"> 到最后了亲! </div> <script type="text/javascript" src="js/jquery-1.11.3.min.js"></script> <script type="text/javascript" src="js/underscore.js"></script> <!-- 模板,用underscore解析 --> <script type="type/template" id="feed-template"> <div class="feed-item"> <p> <img src="<%= imgurl %>" alt=""/> </p> <p class="biaoti"> <%= title %> </p> <p class="neirong"> <%= content %> </p> <p class="zuozhe"> <%= author %> </p> </div> </script>
var $waterfall = $(".waterfall"); //获得模板 var templateString = $("#feed-template").html(); //准备模板函数,经过underscore将模板函数转为html模板,全局使用,因此单独拿出来 var compile = _.template(templateString); //准备总高度数组 var colAllHeight = [0, 0, 0]; //三个表示页面的瀑布的三列的每个块的高度 var pagenum = 1; //页码 getAndRender(1); //先渲染第一页的内容 var lock = true; //函数截流 //窗口卷动监听 //每滚动一次都会触发 $(window).scroll(function () { //jquery帮咱们作了关于滚动的三个兼容处理:总文档高度,已经卷动高度,视口高度 var scrollTop = $(window).scrollTop(); var windowHeight = $(window).height(); var documentHeight = $(document).height(); //已经滚动到底部而且已经被lock if (documentHeight - windowHeight - scrollTop < 200 && lock) { lock = false; //解除锁定 pagenum++; //滚动一次加一次页数 getAndRender(pagenum); //根据页数渲染数据,而且里面会从新锁定 } }); function getAndRender(pagenum) { //让加载logo显示 $waterfall.addClass("loading"); //发出Ajax请求 //这里的页数是用简单的文件的数字编号来代替 $.get("json/json" + pagenum + ".txt", function (data, statusText) { //jq的ajax的get方法 //把字符串转为对象 var dataJSON = JSON.parse(data); //news这个数组,仔细想一想,news这个数组里面装的是什么? var dictionaryArray = dataJSON.news; //若是数组为空,就表示到最后了 if (dictionaryArray.length == 0) { $(".end").show(); $waterfall.removeClass('loading'); return; } //遍历从接口获取的数据 for (var i = 0; i < dictionaryArray.length; i++) { var thisDictionary = dictionaryArray[i]; //立刻发出请求这个字典里面图片的请求 var image = new Image(); //一旦设置src,上行HTTP请求将发出 image.src = thisDictionary.imgurl; image.index = i; //设置这个image的索引值 //监听这个图片是否是加载完毕 $(image).load(function () { //这张图片加载完毕了 //console.log(this.index + "号图片加载完毕"); //填充字典 //哪一个图片已经填充完了,就注入几号字典 //例如第一个图片,传入转为html模板的函数 var compiledString = compile(dictionaryArray[this.index]); //获得这个盒子,变为jQuery对象 var $box = $(compiledString); //上DOM $waterfall.append($box); //寻找最小列 var min = _.min(colAllHeight); //寻找最小列的索引 var minIndex = _.indexOf(colAllHeight, min); //绝对定位: $box.css("left", 300 * minIndex); $box.css("top", colAllHeight[minIndex] + 20); //将本身的高度,也加到数组的指定列中: colAllHeight[minIndex] += $box.outerHeight() + 20; //淡入 $box.fadeIn(); //让加载滚动的logo有高度,跟随移动位置 $waterfall.css("height", _.max(colAllHeight)); $waterfall.removeClass("loading"); lock = true; }); } }); }