JavaScript基础 Ajax

Ajax

Asynchronous Javascript And XML(异步 JavaScript 和 XML)是指一种创建交互式网页应用的网页开发技术

为什么用来学习Ajax?

因为学习ajax能让客户端跟服务端的交互更优雅。

学习Ajax有什么好处?

在没有ajax之前,客户端向服务端发起请求,比如需要提交表单数据,可以使用form表单,但是form表单在提交时,会刷新整个页面,用户的体验极其差!

so:

  1. ajax可以局部刷新页面,无需重载整个页面,节约资源。
  2. ajax异步加载,无需经历同步等待的无奈。
  3. 第三..... 没有第三,上面两点已经足够形容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;

?>
Animation.gif
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()  //
image.png

后面加这个时间戳地址每一次都会不一样,请求都一样的数据 所有每次都会返回数据

案例 快递单号查询

<!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>
Animation.gif

封装

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>
Animation2.gif

推荐阅读更多精彩内容