Ajax
Asynchronous Javascript And XML(异步 JavaScript 和 XML)是指一种创建交互式网页应用的网页开发技术
为什么用来学习Ajax?
因为学习ajax能让客户端跟服务端的交互更优雅。
学习Ajax有什么好处?
在没有ajax之前,客户端向服务端发起请求,比如需要提交表单数据,可以使用form表单,但是form表单在提交时,会刷新整个页面,用户的体验极其差!
so:
- ajax可以局部刷新页面,无需重载整个页面,节约资源。
- ajax异步加载,无需经历同步等待的无奈。
- 第三..... 没有第三,上面两点已经足够形容ajax的牛逼之处了。
需要注意的是:
ajax不能跨域,跨域需要服务端进行相应的设置,总之,不是你前端操心的事!
如果需要跨域,前端可以使用jsonp技术(下节课的装逼内容)
何为异步?
总所周知,javascript是同步的,就是同一时间只能做一件事,而异步则是多个需求可以同时进行,看起来很牛逼。但是异步不一定是好事,是好是坏看需求来,滥用异步,不能处理好事件顺序,会陷入恐怖的无限回调之中。
ok,扯了这么多,ajax到底难不难?
使用ajax技术:
//ajax的使用极其简单,只有4步:
1.创建ajax对象 xhr = new XMLHttpRequest();
2.建立请求 xhr.open(type,url,boolean) //type请求方式(Get or post) url(后台接口) bool(是否异步 true是异步,false则同步)
3.发送请求 xhr.send();
4.监听状态码 xhr.onreadystatechange=function(){}
//结束
状态码
xhr.readyState
* 状态码 0 1 2 3 4
*
* 0: 请求还没有建立
* 1:请求建立了,还没有进行发送
* 2:请求正式发送(xhr.send()执行了)
* 3:请求接收,同时又一部分数据可以使用了(数据并没有全部处理好)
* 4:请求已经完全受理
GET
//get方式不需要设置请求头,数据是跟在URL?后面
例如:
'url?name=123&age=18&sex=man'
实例:
<body>
<p>
用户名:<input type="text" placeholder='请输入用户名'>
</p>
<p>
年龄:<input type="text" placeholder='请输入年龄'>
</p>
<p>
性别:<input type="text" placeholder='请输入性别'>
</p>
<button>发送</button>
<script>
var aInp = document.getElementsByTagName( 'input' ),
btn = document.getElementsByTagName( 'button' )[0];
btn.onclick = function (){
var user = aInp[0].value,
age = aInp[1].value,
sex = aInp[2].value,
date = '?user='+user+'&age='+age+'&sex='+sex;
var xhr = new XMLHttpRequest();
xhr.open( 'get' , '1.php'+date );
xhr.send();
xhr.onreadystatechange = function(){
if( this.readyState ===4 ){ //先查询一下js中的状态码是否等于4,等于4说明请求已经完全受理
if( this.status>= 200 && this.status <= 301 ){ //查询一下服务器的状态码是否是成功的集合,如果是在集合里说明返回成功了
console.log( this.responseText )
}else{
alert( this.status )//这里就是不成功,把服务器的状态码弹给用户,提醒用户
}
}
}
}
</script>
</body>
php文件
<?php
header("content-type:text/html;charset= utf-8");
$user = $_GET['user'];
$age = $_GET['age'];
$sex = $_GET['sex'];
echo "您输入的用户名:".$user.'; 您输入的年龄是:'.$age.'; 您输入的性别是:'.$sex;
?>
POST请求
post需要设置请求头(数据格式)
发送的数据需要放到send里
xhr.open("post" , "url");
//设置请求头
xhr.setRequestHeader("Content-Type","application/x-www-form-urlencoded");
xhr.send(data);
post请求头的几种常用数据格式
1.application/x-www-form-urlencoded
#浏览器的原生 form 表单,如果不设置 enctype属性,那么最终就会默认以 application/x-www-form-urlencoded 方式提交数据。
在POST提交数据中Content-Type 被指定为 application/x-www-form-urlencoded;提交的数据按照 key1=val1&key2=val2 的方式进行编码,key 和 val 都进行了 URL 转码。大部分服务端语言都对这种方式有很好的支持。很多时候,我们用 Ajax 提交数据时,也是使用这种方式。
2.multipart/form-data
#这也是一个常见的 POST 数据提交的方式。我们使用表单上传文件时,必须让 form 的 enctype 等于这个值。
这种方式一般用来上传文件,各大服务端语言对它也有着良好的支持。上面提到的这两种 POST 数据的方式,都是浏览器原生支持的。
3.application/json
#用来告诉服务端消息主体是序列化后的 JSON 字符串。
由于 JSON 规范的流行,除了低版本 IE 之外的各大浏览器都原生支持 JSON.stringify,服务端语言也都有处理 JSON 的函数,使用 JSON 不会遇上什么麻烦。
4.text/xml
#它是一种使用 HTTP 作为传输协议,XML 作为编码方式的远程调用规范
它的使用也很广泛,能很好的支持已有的 XML-RPC 服务。不过,XML 结构还是过于臃肿,一般场景用 JSON 会更灵活方便。
<script>
var aInp = document.getElementsByTagName( 'input' ),
btn = document.getElementsByTagName( 'button' )[0];
btn.onclick = function (){
var user = aInp[0].value,
age = aInp[1].value,
sex = aInp[2].value,
date = 'user='+user+'&age='+age+'&sex='+sex;
var xhr = new XMLHttpRequest();
xhr.open( 'post','1.php');
xhr.setRequestHeader( "Content-Type","application/x-www-form-urlencoded")
xhr.send( date );
//这里用onload也可以
xhr.onload= function (){
if( this.readyState === 4 ){
if( this.status>=200 && this.status <=301 ){
console.log( this.responseText )
} else {
console.log( this.status )
}
}
}
}
</script>
GET和POST有哪些区别?
- get:常见的请求,通常用于向服务器查询信息,数据是拼接在接口的后面;
数据由于拼接在后面,所以可能 在浏览器的地址栏看到
域名与数据在加? - post 发送的数据放在send()里面,不需要加?
需要设置请求头
1.get方式为什么网上说,传输的数据有上限
(1).URL不存在参数上限的问题,HTTP协议规范没有对URL长度进行限制。这个限制是特定的浏览器及服务器对它的限制。IE对URL长度的限制是2083字节(2K+35)。对于其他浏览器,如Netscape、FireFox等,理论上没有长度限制,其限制取决于操作系统的支持。
(2).POST是没有大小限制的,HTTP协议规范也没有进行大小限制,起限制作用的是服务器的处理程序的处理能力。
2.获取数据和发送数据
GET和POST都可以进行数据的获取和发送。
3.安全性
其实都不安全
GET的数据可以在url里明文显示出来,
POST的虽然在url里看不到,但是使用一些工具一样能看到,所以都不安全
那么到底使用哪一种方式比较好呢?
这个以跟后端人员商量的结果为准!
通常,查询或者获取使用GET
方式,因为GET获取时,通常是有个幂等性(幂等通常可以理解为多次操作会得到等效结果的情况。或者不同时间的操作对结果不会有影响。)
而POST无法保证幂等,所以需要修改服务器状态可以使用POST
GET缓存问题
GET请求数据时会在本地缓存下来,当请求同一个url时,GET会直接调用本地缓存,当后台数据更新了,GET就不会得到
解决方法就是 在数据后面加上时间
url += '?' + data + '_=' + new Date().getTime() //
后面加这个时间戳地址每一次都会不一样,请求都一样的数据 所有每次都会返回数据
案例 快递单号查询
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="Author" content="jmens">
<title>Tz-40期Js</title>
<style>
/*清除默认样式*/
*{margin:0;padding:0;font-family: Microsoft YaHei,serif;}
li{list-style: none;}
/*----------------------------*/
#wrap{
width: 950px;
margin: 50px auto;
font-size: 14px;
}
#wrap input{
width: 200px;
height: 20px;
outline: none;
}
#wrap button{
width: 50px;
height: 24px;
}
#wrap ul{
margin-top: 10px;
border-bottom: 1px solid #ddd;
border-left: 1px solid #ddd;
}
#wrap ul li{
border-top: 1px solid #ddd;
}
#wrap ul li:after{
content: '';
display: block;
clear: both;
}
#wrap ul li p{
float: left;
height: 40px;
border-right: 1px solid #ddd;
box-sizing: border-box;
}
#wrap ul li p:nth-child(1){
width: 10%;
padding: 10px 0;
background: #eee;
text-align: center;
font-weight: bold;
}
#wrap ul li p:nth-child(2){
width: 90%;
line-height: 40px;
text-indent: 20px;
}
</style>
</head>
<body>
<div id="wrap">
<input type="text" placeholder="请输入单号">
<button>查询</button>
<ul>
<li>
<p>提示信息</p>
<p class="info" ></p>
</li>
<li>
<p>快递公司</p>
<p class="info" ></p>
</li>
<li>
<p>单号</p>
<p class="info" ></p>
</li>
<li>
<p>序号</p>
<p>物流情况(倒叙)</p>
</li>
<li>
<ol>
<!--<li>-->
<!--<p>1</p>-->
<!--<p>到福建</p>-->
<!--</li>-->
<!--<li>-->
<!--<p>2</p>-->
<!--<p>到厦门</p>-->
<!--</li>-->
<!--<li>-->
<!--<p>2</p>-->
<!--<p>到同安</p>-->
<!--</li>-->
</ol>
</li>
</ul>
</div>
<script>
var oInp = document.getElementsByTagName( 'input' )[0],
oBtn = document.getElementsByTagName( 'button' )[0],
aInfo = document.getElementsByClassName( 'info' ),
oOl = document.getElementsByTagName( 'ol' )[0];
oBtn.onclick = function (){
var val = oInp.value,
date = new Date(),
YY = date.getFullYear(),
MM = addZero( date.getMonth() + 1 ),
DD = addZero( date.getDate() ),
dd = addZero( date.getHours() ),
mm = addZero( date.getMinutes() ),
ss = addZero( date.getSeconds() ),
time = YY + MM + DD + dd + mm + ss,
date = 'com=auto&nu='+val+'&showapi_appid=59466&showapi_test_draft=false&showapi_timestamp='+time+'&showapi_sign=0244ad820cfb41a499ed7ec6dddba8e1',
xhr = new XMLHttpRequest();
xhr.open( 'post' , 'https://route.showapi.com/64-19' );
xhr.setRequestHeader( "Content-Type","application/x-www-form-urlencoded" )
xhr.send( date );
xhr.onreadystatechange = function (){
if( this.readyState === 4 ){
if( this.status >= 200 && this.status < 300 || this.status ===304 ){
addCont( JSON.parse(this.responseText ) )
console.log( JSON.parse(this.responseText ) )
}else {
console.log( this.status )
}
}
}
function addCont( obj ){ //处理后台的返回数据
var str = '';
aInfo[0].innerHTML = obj.showapi_res_body.msg;
aInfo[1].innerHTML = obj.showapi_res_body.expTextName;
aInfo[2].innerHTML = obj.showapi_res_body.mailNo;
var Contex = obj.showapi_res_body.data;
for( var key in Contex ){
str += '<li><p>'+( key/1+1 )+'</p><p>'+Contex[key].time+Contex[key].context+'</p></li>';
}
oOl.innerHTML = str;
}
}
function addZero( n ){
return n < 10 ? '0' + n : n + '' ;
}
</script>
</body>
</html>
封装
var aInp = document.getElementsByTagName( 'input' ),
btn = document.getElementsByTagName( 'button' )[0];
/*
封装:
传值:
type get post (必填)
url 1.php
aysn 异步 同步
data date = 'user='+ user + '&age=' + age + '&sex='+ sex;
success 成功时回调函数
error 失败时回调函数
*/
btn.onclick = function ( ) {
var json = {};
json[aInp[0].name] = aInp[0].value;
json[aInp[1].name] = aInp[1].value;
json[aInp[2].name] = aInp[2].value;
console.log( json )
ajax(
{
type : 'get',
url : '1.php',
aysn : true,
data : json,
success : function ( msg ){ //成功时回调函数
console.log( msg )
},
error : function ( tStatus ){ //失败时回调函数
console.log( tStatus )
}
}
);
}
function ajax( obj ){
//处理数据
var type = obj.type || 'get',
url = obj.url || '',
aysn = obj.aysn !== false, //obj.aysn 没有传值的时候 undefined
// undefined !== false ===>true
// false !== false ===>false
// true !== false ===>true
data = obj.data || ''; //处理一下data没有传值的时候
if( data ){ //判断一下data有没有值 有值就遍历出来
var str = '';
for (var key in data){
str += key + '=' + data[key] + '&';
}
data = str;
}
if( /get/i.test(type) ){ //正则判断一下是不是GET的时候
url += '?' + data + '_=' + new Date().getTime()
}
var xhr = new XMLHttpRequest();
xhr.open( type , url , aysn );
xhr.setRequestHeader("Content-Type","application/x-www-form-urlencoded");
xhr.send( data );
xhr.onreadystatechange = function (){
if( this.readyState === 4 ){
var tStatus = this.status;
if( tStatus >=200 && tStatus<300 || tStatus === 304 ){
obj.success && obj.success( this.responseText );
} else {
obj.error && obj.error( tStatus )
}
}
}
}
案例实现瀑布流
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="Author" content="jmens">
<title>Tz-40期Js</title>
<style>
/*清除默认样式*/
*{margin:0;padding:0;font-family: Microsoft YaHei,serif;}
li{list-style: none;}
/*----------------------------*/
#wrap{
position: relative;
width: 1000px;
margin: 50px auto;
}
#wrap:after{
content: '';
display: block;
clear: both;
}
#wrap ul {
float: left;
padding: 0 10px;
}
#wrap ul li{
position: relative;
width: 230px;
height: auto;
margin-bottom: 5px;
padding-bottom: 5px;
background: #eee;
}
#wrap ul li img{
width: 230px;
}
#wrap ul li p{
margin: 5px 0 ;
text-indent: 10px;
font-size: 14px;
font-weight: bold;
}
#wrap ul li p.name a{
text-decoration: none;
font-weight: normal;
color: red;
}
#wrap ul li p.name a:hover{
color: #ff08d7;
}
#wrap ul li p span{
font-weight: normal;
font-style: italic;
}
#wrap ul li p.rating span{
color: #884589;
}
.test{
border-top: 1px solid #ddd;
}
</style>
</head>
<body>
<div id="wrap">
<ul>
<!--<li>-->
<!--<img src="http://m.xxxiao.com/wp-content/uploads/sites/3/2016/08/m.xxxiao.com_866f25b69225b17f1858b1fd83d657fc-683x1024.jpg" alt="#" height="" width=""/>-->
<!--<p class="name">片名:<a href="">《肖申克的救赎》</a></p>-->
<!--<p class=" year ">年份:<span>1993</span></p>-->
<!--<p class="rating">评分:<span>9.5</span></p>-->
<!--</li>-->
</ul>
<ul></ul>
<ul></ul>
<ul></ul>
</div>
<div class="test"></div>
<script>
var oUl = document.getElementsByTagName( 'ul' );
var oTest = document.getElementsByClassName( 'test' )[0];
var timer;
eFn( 10 );
document.onscroll = function ( ) {
eFn( 200 )
};
function eFn( x ) {
var testH = oTest.offsetTop,
scrollH = ( document.documentElement.scrollTop || document.body.scrollTop ) +document.documentElement.clientHeight ; //得到浏览器下面的滚动高度
console.log( scrollH , testH )
if( testH - scrollH < 100 ){
//监听滚动高度,以便判断要不要加载
clearTimeout( timer ) //一上来就把上次的定时器清除,才不会出现定时器叠加
timer = setTimeout( function ( ) { //滚轮事件只要一滚动就会时时触发 用一个定时器可以达到 0.2s才触发
ajax( {
type : 'get',
url : 'https://route.showapi.com/197-1',
data : {
num : 10,
showapi_appid : 59636,
showapi_sign : '8ad614cf24534bd18be2769b856fe5a1'
},
success : function ( msg ) {
draw( JSON.parse( msg ) )
}
} )
},x)
}
}
//排版处理
function draw( data ) {
var newlist = data.showapi_res_body.newslist;
// 下面应用递归 一件事件加载完再调用自己 保证上一次的事件处理完
var i = 0;
!function m( ) {
if( i>= 10 ) return; // 当i=10时就停止
var aLi = document.createElement( 'li');
var oImg = new Image; // 在js里创建一下图片标签
oImg.src = newlist[i].picUrl; // 把src获取给oImg 只要有图片 就会开始加载
aLi.innerHTML = '<img src="'+newlist[i].picUrl+'" alt="#" height="" width=""/><p class="name">片名:<a href="'+newlist[i].url+'">'+newlist[i].title+'</a></p><p class=" year ">年份:<span>'+newlist[i].ctime+'</span></p><p class="rating">评分:<span>'+newlist[i].description+'</span></p>';
oImg.onload = function (){ //当图片加载完触发函数
i++; // i++ 序号加加
oUl[ indexNum() ].appendChild( aLi ); //等加载完再把它放进oUl里 达到每一次放图片的时候上一张加载完的
m(); //再调用下一次的m 开始执行下一次
var top = aLi.offsetTop + document.documentElement.scrollTop; //先获取append进去的定位高和宽度
var left = aLi.offsetLeft;
console.log( document.documentElement.scrollTop , left )
aLi.style.cssText = 'top:-'+top+'px; left: -'+left+'px;transition: top 0.2s , left 0.2s '; //通过相对定位把它们先放到0的位置(-top -left )
setTimeout( function ( ) {
aLi.style.top = 0; //0.05s后放到原来的位置
aLi.style.left = 0;
} ,100 )
}
}()
}
//判断一下哪个UL最短
function indexNum( ) {
var j = 0;
var h = oUl[0].clientHeight; //规定一下 h是oUl 的高度,来跟其他比
for( var i=1 , length = oUl.length ; i<length ; i++ ){
if( h > oUl[i].clientHeight ){ //h > oUl[i].clientHeight 说明其他ul有比它短
j = i; //i赋值给j 把短的给j
h = oUl[i].clientHeight //再跟短的高度给h以便下次比较
}
}
return j; //我们是得到短的序号
}
function ajax( obj ){
//处理数据
var type = obj.type || 'get',
url = obj.url || '',
aysn = obj.aysn !== false, //obj.aysn 没有传值的时候 undefined
// undefined !== false ===>true
// false !== false ===>false
// true !== false ===>true
data = obj.data || ''; //处理一下data没有传值的时候
if( data ){ //判断一下data有没有值 有值就遍历出来
var str = '';
for (var key in data){
str += key + '=' + data[key] + '&';
}
data = str;
}
if( /get/i.test(type) ){ //正则判断一下是不是GET的时候
url += '?' + data + '_=' + new Date().getTime()
}
var xhr = new XMLHttpRequest();
xhr.open( type , url , aysn );
xhr.setRequestHeader("Content-Type","application/x-www-form-urlencoded");
xhr.send( data );
xhr.onreadystatechange = function (){
if( this.readyState === 4 ){
var tStatus = this.status;
if( tStatus >=200 && tStatus<300 || tStatus === 304 ){
obj.success && obj.success( this.responseText );
} else {
obj.error && obj.error( tStatus )
}
}
}
}
</script>
</body>
</html>