關於ajax學習筆記

1、什麼是AJAX,爲何要使用Ajax(請談一下你對Ajax的認識)

  • ajax全稱Asynchronous JavaScript and XML(異步的javascript和XML),爲何會有這麼一種技術的出現呢,由於前端時常會有這樣的需求,咱們只要局部刷新,不須要整一個刷新的時候,便催生了這樣的技術。
  • 在 Ajax應用中信息是經過XML數據或者字符串在瀏覽器和服務器之間傳遞的(json字符串居多)
  • 在瀏覽器端經過XMLHttpRequest對象的responseXMl屬性,獲得服務器端響應的XML數據。
  • AJAX優勢:javascript

    • 最大的一點是頁面無刷新,用戶的體驗很是好。
    • 使用異步方式與服務器通訊,具備更加迅速的響應能力。
    • 能夠把之前一些服務器負擔的工做轉嫁到客戶端,利用客戶端閒置的能力來處理,減輕服務器和帶寬的負擔,節約空間和寬帶租用成本。而且減輕服務器的負擔,ajax的原則是「按需取數據」,能夠最大程度的減小冗餘請求,和響應對服務器形成的負擔。
    • 基於標準化的並被普遍支持的技術,不須要下載插件或者小程序。
  • AJAX缺點:php

    • ajax不支持瀏覽器back按鈕。
    • 安全問題 AJAX暴露了與服務器交互的細節。
    • 對搜索引擎的支持比較弱。
    • 破壞了程序的異常機制。
    • 不容易調試。
  • AJAX應用和傳統Web應用有什麼不一樣?css

    • 傳統的web前端與後端的交互中,瀏覽器直接訪問Tomcat的Servlet來獲取數據。Servlet經過轉發把數據發送給瀏覽器。
    • 當咱們使用AJAX以後,瀏覽器是先把請求發送到XMLHttpRequest異步對象之中,異步對象對請求進行封裝,而後再與發送給服務器。服務器並非以轉發的方式響應,而是以流的方式把數據返回給瀏覽器
    • XMLHttpRequest異步對象會不停監聽服務器狀態的變化,獲得服務器返回的數據,就寫到瀏覽器上【由於不是轉發的方式,因此是無刷新就可以獲取服務器端的數據】
  • AJAX是異步執行的,如圖所示,異步執行不會阻塞.

2、ajax 的執行過程

  1. 建立XMLHttpRequest對象,也就是建立一個異步調用對象
  2. 建立一個新的HTTP請求,並指定該HTTP請求的方法、URL及驗證信息
  3. 設置響應HTTP請求狀態變化的函數
  4. 發送HTTP請求
  5. 獲取異步調用返回的數據
  6. 使用JavaScript和DOM實現局部刷新

基本示例: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");
        }
    }

2.1 open()方法

xhr.open("get","test.txt",true);java

調用open方法並不會真正發送請求,而只是啓動一個請求以備發送。jquery

它接受三個參數:web

  • 要發送的請求的類型
  • 請求的URL
  • 表示是否異步的布爾值。

2.2 send()方法

若是要發送請求,用send()方法。ajax

要發送特定的請求,須要調用send()方法。編程

  • 它接受一個參數:請求主體發送的數據。
  • 若是不須要經過請求主體發送數據,則必須傳入null,不能留空。
  • 請求主體:HTTP上行請求,有頭部、主體。

    • 通常來講,GET請求是隻有頭部,沒有主體
    • 而POST請求有請求主體。

一但調用send()方法,HTTP上行請求就將發出。

2.3 readyState屬性

表示「就緒狀態」

  • 0 (uninitialized) 未初始化
  • 1 (loading) XMLHttpRequest對象正在加載
  • 2 (loaded) XMLHttpRequest對象加載完畢
  • 3 (interactive) 正在傳輸數據
  • 4 (complete) 所有完成

通常來講,只須要使用4狀態就能夠了

只要這個屬性值發生了變化,就會觸發一個事件onreadystatechange事件,就可使用xhr.onreadystatechange = function(){}來捕獲readyState變化以後作的事情。

3、關於http的狀態

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服務器遇到一個錯誤,使其沒法對請求提供服務

這是比較齊全的狀態表:

4、關於函數封裝(ajax封裝)

  • 變量、函數的做用域,是定義這個變量、函數時,包裹它的最近父函數。
  • 沒有在任何function中定義的變量,稱爲全局變量。全局變量都是window對象的屬性。因此,若是想在函數內,向全局暴露頂層變量,只須要把頂層變量設置爲window對象的屬性。
  • 越是大的項目,越須要讓全局變量越少越好。這是爲了防止不一樣工程師之間的程序,命名衝突。因此,每個功能包,只能向全局暴露惟一的頂層變量,就是這個功能包本身的命名空間。
  • jQuery、YUI、underscore都是這樣的作法。
  • 向外暴露全局變量,設置window的變量(也是這個函數的命名空間),相似jquery的$其實也就是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));
        }
    }
})();

5、關於ajax緩存問題

當Ajax第一次發送請求後,會把請求的URL和返回的響應結果保存在緩存內,當下一次調用Ajax發送相同的請求時,注意,這裏相同的請求指的是URL徹底相同,包括參數,瀏覽器就不會與服務器交互,而是直接從緩存中把數據取出來,這是爲了提升頁面的響應速度和用戶體驗。(服務端也會收到請求響應304)

瀏覽器會自做主張的把全部異步請求來的文件緩存,當下一次請求的URL和以前的同樣,那麼瀏覽器將不會發送這個請求,而是直接把緩存的內容當作xhr.responseText

須要注意的是,post 請求方式不會被緩存,只有 get 請求方式會被緩存。

5.1 如何避免 ajax 緩存問題

方法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 作成每次都不同,這樣就不會被瀏覽器緩存了。

6、json檢測

判斷返回的 json 數據是否可用,這個只是屬性一些平常使用 ajax 的點而已。

6.1 使用 JSON.parse

經過JSON.parse轉換爲json格式,若是沒法轉換,會報錯。

var jsonObj = JSON.parse(str);

6.2 用hasOwnProperty進行判斷

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";
        }

7、關於跨域問題

已經在另一篇文章裏面說過了,jsonp 是其中一種解決辦法。

微信:地址
blog:地址

7.1 使用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);
        }

8、關於ajax的示例:瀑布流

要實現2個地方:

  1. 滾動到底部判斷(包含視口的底部和總的底部)
  2. 瀑布流裏面的內容須要錯位顯示

8.1 滾動到底部判斷

咱們須要知道:

  • 總文檔高度
  • 已經滾動的高度
  • 視口高度,經過$(document).height(); 獲取,視口底部來觸發ajax 獲取下一頁的數據
  • 總文檔高度-已經卷動高度-視口高度 < 200 基本上就是滾動到底了,滾動到文檔底部就中止 ajax 請求。

  • scroll事件,必定是要截流的。由於用戶滾一個鼠標滾輪的「小咯噔」就觸發一次scroll事件;滑動滾動條的時候,是每一像素觸發一次這個事件。還有pageDown、下箭頭按鈕,都能觸發scroll事件。
  • 如何判斷文章是否到頭,說白了前端開發工程師不知道一共有多少頁。好比今天又53頁,明天就有55頁了,因此你的JS裏面沒法寫死一個文章總頁數。因此辦法就是,請求下去,請求到page.php?pagenum=54的時候,發現終止標記,或者這個頁面返回的json是空,就表示到頭了。

8.2 瀑布流裏面的內容須要錯位顯示

  • 這裏分紅三列瀑布流,組成一個數組管理

    • 這個數組會不斷計算三列之中的最小值
    • 而後按照每次的最小值進行高度插入
  • 圖片判斷是否加載完成須要用load方法,而且圖片須要先new image才能加載方法
  • 圖片的插入次序不是固定的(ajax異步),因此用以前的數組進行管理,每次都對最小值的高度插入值,這樣就能保證每次都往最靠裏面的圖片位置進行放置

    • 而且須要使用絕對位置值,由於css裏面,須要使用絕對值撐開位置(left 和top)

瀑布流的數組樣例以下:

// 第一行
// [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]

8.3 整個代碼

<!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;
                });
            }
        });
    }
相關文章
相關標籤/搜索