1. 程式人生 > >jQuery原始碼學習(四)

jQuery原始碼學習(四)

佇列queue()

佇列(先進先出)方法,執行順序的管理。

<script type="text/javascript">
    //大體框架
    //佇列其實就是一個數組
    jQuery.extend([//工具方法
        queue//相當於陣列的push操作-往陣列的後面新增資料   入隊操作
        dequeue//相當於陣列的shift操作-從陣列的前面取資料    出隊操作
        _queueHooks
        ]);
    jQuery.fn.extend([//例項方法
        queue //入隊
        dequeue //出隊
delay //延遲佇列 clearQueue //清除佇列 promise //佇列全部執行完後再執行 ]);
</script>

佇列的基本使用

佇列中存的都是函式
佇列操作儲存的必須是函式,因為在出隊的時候會對函式進行操作
工具方法操作:

<script type="text/javascript" src="../../jquery-2.1.4.js"></script>
    <script type="text/javascript">
    $(function
(){
function fn1(){ alert(111); } function fn2(){ alert(222); } //工具方法操作 $.queue(document,'q1',fn1); $.queue(document,'q1',fn2);//三個引數分別表示新增佇列的物件、佇列名稱和佇列中的函式資料 //也可以整體新增 //$.queue(document,'q1',[fn1,fn2]); console.log($.queue(document,'q1'
));//[function, function] $.dequeue(document,'q1');//111 出隊操作-從佇列中取出第一個函式並執行,而不是僅僅取出來 //進行了一次出隊操作後,陣列就減少一項。只剩下[fn2] $.dequeue(document,'q1');//222 出隊操作-從佇列中取出第一個函式並執行 });
</script>

例項操作:

<script type="text/javascript" src="../../jquery-2.1.4.js"></script>    
    <script type="text/javascript">
    $(function(){
        function fn1(){
            alert(111);
        }
        function fn2(){
            alert(222);
        }
        //例項操作
        $(document).queue('q1',fn1);//入隊操作
        $(document).queue('q1',fn2);
        console.log($.queue(document,'q1'));//[function, function]
        $(document).dequeue('q1');//111
        $(document).dequeue('q1');//222
    });
    </script>

在jQuery中queue主要是針對動畫操作的,與運動有關
Demo:

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <title>queue</title>
    <style type="text/css">
        div{
            width: 100px;
            height: 100px;
            background-color: red;
            position: absolute;
        }
    </style>
</head>
<body>
    <div id="#div1"></div>
    <script type="text/javascript" src="../../jquery-2.1.4.js"></script>
    <script type="text/javascript">
    $(function(){
        $('div').click(function(){
            $(this).animate({ //通過開啟一個定時器setInterval來不斷改變寬度
                width:300
            },2000);
            $(this).animate({//通過開啟一個定時器setInterval來不斷改變高度
                height:300
            },2000);
            $(this).animate({//通過開啟一個定時器setInterval來不斷改變left值
                left:300
            },2000);
        });
    });
    </script>
</body>
</html>

當動畫開始之前將三個定時器放到queue的佇列陣列中[setInterval1,setInterval2,setInterval3],然後呼叫dequeue出隊,從前往後依次執行。
queue也可以對非同步進行管理(定時器就是非同步的),而且可以對多個非同步進行管理
deferred是針對單個非同步操作的
注意:在佇列操作中,如果不進行出隊操作,則佇列後面的操作永遠不會執行。
Demo:

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <title>queue</title>
    <style type="text/css">
        div{
            width: 100px;
            height: 100px;
            background-color: red;
            position: absolute;
        }
    </style>
</head>
<body>
    <div id="#div1"></div>
    <script type="text/javascript" src="../../jquery-2.1.4.js"></script>
    <script type="text/javascript">
    $(function(){
        $('div').click(function(){
        $(this).animate(
        {width:300},2000).queue('fx',function(){
//這裡沒有出隊操作,後面的操作都不會執行,只會改變寬度值,不會改變高度值
            }).animate({height:300},2000);
        });
    });
    </script>
    <script type="text/javascript">
    $(function(){
        $('div').click(function(){
//fx為預設的佇列名稱,可以省略
$(this).animate({width:300},2000).queue('fx',function(next){
        //$(this).dequeue();//出隊操作 可以進行後續操作
            next();//和上面的程式碼功能一樣,都是出隊操作
            }).animate({height:300},2000);
        });
    });
    </script>
</body>
</html>

queue與回撥的區別

回撥是不可控制的
queue是可以控制的,想什麼出隊就什麼時候出隊

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <title>queue</title>
    <style type="text/css">
        div{
            width: 100px;
            height: 100px;
            background-color: red;
            position: absolute;
        }
    </style>
</head>
<body>
    <div id="#div1"></div>
    <script type="text/javascript" src="../../jquery-2.1.4.js"></script>
    <script type="text/javascript">
    $(function(){
        $('div').click(function(){
            $(this).animate({width:300},2000,function(){
                $(this).css('left',300);
            }).animate({height:300},2000);
        });
    });
    </script>
    <script type="text/javascript">
    $(function(){
        $('div').click(function(){
            $(this).animate({width:300},2000,function(){
                var that = this;
                //因為定時器是非同步的
                //這裡使用回撥函式不會影響後續動畫的執行
                //所以回撥裡面的運動會和後面的運動一起發生
                var timer = setInterval(function(){
                    that.style.left = that.offsetLeft + 1 + 'px';
                    if(that.offsetLeft == 200){
                        clearInterval(timer);
                    }
                },30);
            }).animate({height:300},2000);
        });
    });
    </script>
    <script type="text/javascript">
    $(function(){
        $('div').click(function(){
            $(this).animate({width:300},2000).queue(function(next){
                //這裡使用queue,保證left改變完成後再出隊
                //如果永遠不出隊,後面的操作永遠不會執行
                var that = this;
                var timer = setInterval(function(){
                    that.style.left = that.offsetLeft + 1 + 'px';
                    if(that.offsetLeft == 200){
                        next();//這裡當left改變完成後再出隊
                        //可以控制非同步的執行順序
                        clearInterval(timer);
                    }
                },30);
            }).animate({height:300},2000);
        });
    });
    </script>
</body>
</html>

delay是延遲佇列執行

<body>
    <div id="#div1"></div>
    <script type="text/javascript" src="../../jquery-2.1.4.js"></script>
    <script type="text/javascript">
    $(function(){
        $('div').click(function(){
            //delay是延遲佇列執行  
                    $(this).animate({width:300},2000).delay(2000).animate({height:300},2000);
        });
    });
    </script>
</body>

promise()佇列全部執行完後再執行

<body>
    <div id="#div1"></div>
    <script type="text/javascript" src="../../jquery-2.1.4.js"></script>
    <script type="text/javascript">
    $(function(){
        $('div').click(function(){
            //佇列全部執行完後再執行
                    $(this).animate({width:300},2000).animate({height:300},2000);
$(this).promise().done(function(){//佇列執行完畢後彈123
                alert(123);
            });
        });
    });
    </script>
</body>

原始碼部分

//佇列(先進先出)方法
//執行順序的管理
jQuery.extend({
    //接收三個引數:要新增對列的物件、佇列名稱和函式資料
    //入隊
    queue: function( elem, type, data ) {
        var queue;
        if ( elem ) //當元素存在時進行後續操作
            //type || "fx"  取自定義的佇列名或者預設的fx
            type = ( type || "fx" ) + "queue";
            //data_priv資料快取物件
            queue = data_priv.get( elem, type );

            // Speed up dequeue by getting out quickly if this is just a lookup
            if ( data ) {
                if ( !queue || jQuery.isArray( data ) ) {
                    //jQuery.isArray( data )當data是陣列時,重新進行設定佇列
                    queue = data_priv.access( elem, type, jQuery.makeArray(data) );
                } else {
                    queue.push( data );
                }
            }
            return queue || [];
        }
    },
    //出隊
    dequeue: function( elem, type ) {
        type = type || "fx";
        var queue = jQuery.queue( elem, type ),//獲取佇列陣列
            startLength = queue.length,//佇列長度
            fn = queue.shift(),//獲取陣列第一項
            hooks = jQuery._queueHooks( elem, type ),
            next = function() {//next就是執行出隊操作
                jQuery.dequeue( elem, type );//出隊操作
            };
        // If the fx queue is dequeued, always remove the progress sentinel
        if ( fn === "inprogress" ) {
            fn = queue.shift();
            startLength--;
        }
        if ( fn ) {

            // Add a progress sentinel to prevent the fx queue from being
            // automatically dequeued
            if ( type === "fx" ) {
                queue.unshift( "inprogress" );
            }

            // clear up the last queue stop function
            delete hooks.stop;
            fn.call( elem, next, hooks );
        }

        if ( !startLength && hooks ) {
            hooks.empty.fire();
        }
    },

    // not intended for public consumption - generates a queueHooks object, or returns the current one
    _queueHooks: function( elem, type ) {
        var key = type + "queueHooks";
        return data_priv.get( elem, key ) || data_priv.access( elem, key, {
            empty: jQuery.Callbacks("once memory").add(function() {
                data_priv.remove( elem, [ type + "queue", key ] );
            })
        });
    }
});
//擴充套件例項方法
jQuery.fn.extend({
    //入隊
    queue: function( type, data ) {
        var setter = 2;

        if ( typeof type !== "string" ) {
            data = type;
            type = "fx";
            setter--;
        }
        //根據引數長度來判斷是應該獲取還是設定
        if ( arguments.length < setter ) {
            return jQuery.queue( this[0], type );
        }

        return data === undefined ?
            this :
            this.each(function() {
                var queue = jQuery.queue( this, type, data );

                // ensure a hooks for this queue
                jQuery._queueHooks( this, type );
                //inprogress是針對動畫的第一次出隊操作
                if ( type === "fx" && queue[0] !== "inprogress" ) {
                    jQuery.dequeue( this, type );
                }
            });
    },
    //出隊
    dequeue: function( type ) {
        return this.each(function() {
            jQuery.dequeue( this, type );
        });
    },
    // Based off of the plugin by Clint Helfers, with permission.
    // http://blindsignals.com/index.php/2009/07/jquery-delay/
    // 延遲佇列
    delay: function( time, type ) {
        time = jQuery.fx ? jQuery.fx.speeds[ time ] || time : time;
        type = type || "fx";

        return this.queue( type, function( next, hooks ) {
            //next就是出隊
            var timeout = setTimeout( next, time );
            hooks.stop = function() {
                clearTimeout( timeout );
            };
        });
    },
    //清除佇列
    clearQueue: function( type ) {
        return this.queue( type || "fx", [] );
    },
    // Get a promise resolved when queues of a certain type
    // are emptied (fx is the type by default)
    // 佇列全部執行完後再執行
    promise: function( type, obj ) {
        var tmp,
            count = 1,
            defer = jQuery.Deferred(),
            elements = this,
            i = this.length,
            resolve = function() {
                if ( !( --count ) ) {
                    defer.resolveWith( elements, [ elements ] );
                }
            };

        if ( typeof type !== "string" ) {
            obj = type;
            type = undefined;
        }
        type = type || "fx";
        while( i-- ) {
            tmp = data_priv.get( elements[ i ], type + "queueHooks" );
            if ( tmp && tmp.empty ) {
                count++;
                tmp.empty.add( resolve );
            }
        }
        resolve();
        return defer.promise( obj );
    }
});

元素屬性的操作

  • attr() 獲取匹配的元素集合中的第一個元素相應屬性的值或設定每一個匹配元素的一個或多個屬性值。(接收兩個引數是設定操作,接收一個引數是獲取操作)
    Demo:
<body>
    <div id="box" title="盒子1">111111</div>
    <div id="box" title="盒子2">222222</div>
    <div id="box" title="盒子3">333333</div>
    <script type="text/javascript" src="../../jquery-2.1.4.js"></script>
    <script type="text/javascript">
    $(function(){
        $("div").attr('title','盒子');//這個是設定所有div的title屬性值為盒子
        alert($("div").attr('title'));//這個只會獲取到第一個div的title屬性值
        });
    </script>
</body>
  • prop() 獲取匹配的元素集合中第一個元素的屬性(property)值或設定每一個匹配元素的一個或多個屬性。
  • removeAttr() 為匹配的元素集合中的每個元素中移除一個屬性(attribute)。
  • removeProp() 為集合中匹配的元素刪除一個屬性(property)。
  • val() 獲取匹配的元素集合中第一個元素的當前值或設定匹配的元素集合中每個元素的值

attr和prop的基本用法

<body>
    <div id="box">111111</div>
    <script type="text/javascript" src="../../jquery-2.1.4.js"></script>
    <script type="text/javascript">
    $(function(){
        $("#box").attr('title','hello');//兩個引數是設定
        alert($("#box").attr('id'));//box  1個引數是獲取
        //原生實現
        var oDiv = document.getElementById("box");
        oDiv.setAttribute('title','hello');
        oDiv.getAttribute('title');

// attr在原生js是基於setAttribute和getAttribute來實現的
// prop是基於dot(.)和方括號語法[]來實現的
        //原生實現
        var oDiv = document.getElementById("box");
        oDiv.title = 'hello';
        oDiv['title'] = 'hello';
        $("#box").prop('title','hello');//兩個引數是設定
        alert($("#box").prop('id'));//box  1個引數是獲取
    });
    </script>
</body>
<body>
    <div id="box" title="盒子1">111111</div>
    <div id="box" title="盒子2">222222</div>
    <div id="box" title="盒子3">333333</div>
    <script type="text/javascript" src="../../jquery-2.1.4.js"></script>
    <script type="text/javascript">
    $(function(){
        $("div").attr('title','盒子');//這個是設定所有div的title屬性值為盒子
        alert($("div").attr('title'));//這個只會獲取到第一個div的title屬性值
        });
    </script>
</body>

attr和prop的區別

<body>
    <div id="box">111111</div>
    <a href="hello.html" id="link"></a>
    <input type="checkbox" name="" value="" checked="checked">
    <script type="text/javascript" src="../../jquery-2.1.4.js"></script>
    <script type="text/javascript">
    $(function(){
        //獲取連結的href屬性值也不一樣
        console.log($("#link").attr('href'));//hello.html
        console.log($("#link").prop('href'));//file:///C:/Users/lj/Desktop/%E6%88%91%E7%9A%84%E5%89%8D%E7%AB%AF%E5%AD%A6%E…%E5%85%83%E7%B4%A0%E5%B1%9E%E6%80%A7%E7%9A%84%E6%93%8D%E4%BD%9C/hello.html

        $("#link").removeAttr('href');
        alert($("#link").attr('href'));//undefined
        $("#link").removeAttr('id');
        alert($("#link").attr('id'));//undefined
        $("#link").removeProp('id');//不會刪除掉
        alert($("#link").prop('id'));//link

        alert($('input').attr('checked'));//checked
        alert($('input').prop('checked'));//true
    });
    </script>
</body>

對於自定義屬性,attr起作用,prop不起作用。

<body>
<div id="box" muke="學前端">111111</div>
<script type="text/javascript" src="../../jquery-2.1.4.js"></script>
<script type="text/javascript">
//設定自定義屬性,attr起作用,prop不起作用
$("#box").attr('muke','學前端');
$("#box").prop('muke','學前端');//不起作用

//獲取自定義屬性
alert($("#box").attr('muke'));//可以獲取到
alert($("#box").prop('muke'));//undefined 獲取不到
</script>
</body>

attr()和prop()方法分別取的是節點的attribute值、property值

例項方法

  • jQuery.prototype.attr
  • jQuery.prototype.prop
  • jQuery.prototype.removeAttr
  • jQuery.prototype.removeProp
  • jQuery.prototype.val
    原始碼部分:
attr: function( name, value ) {
        //this當前的元素
        //jQuery.attr對應回撥函式
        //name和value就是屬性和值
        //arguments.length > 1判斷是讀取還是設定,1個引數是讀取,兩個引數是設定
        //例項attr實際操作的是工具方法jQuery.attr
        //下面方法也都是呼叫的工具方法
        return jQuery.access( this, jQuery.attr, name, value, arguments.length > 1 );
    },
    prop: function( name, value ) {
        return jQuery.access( this, jQuery.prop, name, value, arguments.length > 1 );
    }

attr()和prop()實際上是呼叫jQuery.access()方法。jQuery.access主要作用是修正引數。access函式裡的第二個引數jQuery.attr. 這個引數的作用是告訴access方法, 修正完引數後再去呼叫 靜態方法jQuery.attr()。
例項方法都是呼叫最終的靜態方法來實現的
access方法最後把引數又傳遞給了jQuery.attr, 在jQuery.attr裡才真正進行setAttribute和getAttribute操作.
access原始碼部分:

//多功能值操作(內部)
    //chainable控制設定或者獲取
    access: function( elems, fn, key, value, chainable, emptyGet, raw ) {
        var i = 0,
            length = elems.length,
            bulk = key == null;

        // Sets many values
        // 設定多組值
        if ( jQuery.type( key ) === "object" ) {
            chainable = true;
            for ( i in key ) {
                jQuery.access( elems, fn, i, key[i], true, emptyGet, raw );
            }

        // Sets one value
        } else if ( value !== undefined ) {
            chainable = true;

            if ( !jQuery.isFunction( value ) ) {
                raw = true;
            }

            if ( bulk ) {
                // Bulk operations run against the entire set
                //是字串
                if ( raw ) {
                    fn.call( elems, value );
                    fn = null;

                // ...except when executing function values
                //是函式
                } else {
                    bulk = fn;
                    fn = function( elem, key, value ) {
                        return bulk.call( jQuery( elem ), value );
                    };
                }
            }

            if ( fn ) {
                for ( ; i < length; i++ ) {
                    fn( elems[i], key, raw ? value : value.call( elems[i], i, fn( elems[i], key ) ) );
                }
            }
        }
        //chainable為真是設定
        //為假就是獲取  
        return chainable ?
            elems :

            // Gets
            bulk ?
                fn.call( elems ) :
                length ? fn( elems[0], key ) : emptyGet;
    }
<body>
    <div id="box" muke="學前端" class="div">111111</div>
    <script type="text/javascript" src="../../jquery-2.1.4.js"></script>
    <script type="text/javascript">
    $(function(){
        //可以同時刪除多個屬性和屬性值
        //attrNames = value && value.match( core_rnotwhite );
        //value.match( core_rnotwhite )返回一個數組
        //["id","muke","class"]
        $("div").removeAttr("id muke class");
    });
    </script>
</body>

addClass、removeClass、toggleClass和hasClass

基本用法:

<body>
    <div id="box" class="box1 box2">111111</div>
    <script type="text/javascript" src="../../jquery-2.1.4.js"></script>
    <script type="text/javascript">
    $(function(){
        //新增類名
        $("#box").addClass("box3");
        //新增多個類名
        $("#box").addClass("box3 box4");
        // 特別注意空字串和空格字串不一樣
        alert(Boolean(""));//false
        // 空格字串會返回真
        alert(Boolean(" "));//true
    });
    </script>
</body>
<body>
    <div id="box">111111</div>
    <div id="box">222222</div>
    <div id="box">333333</div>
    <script type="text/javascript" src="../../jquery-2.1.4.js"></script>
    <script type="text/javascript">
    $(function(){
        // addClass()支援回撥函式的形式,這種形式用的少
        $("div").addClass(function(index){
            // alert(index);//0
            return 'box'+index;//這裡根據index的不同,新增不同的class
        });
    });
    </script>
</body>
<body>
    <div id="box" class="box1 box2">111111</div>
    <script type="text/javascript" src="../../jquery-2.1.4.js"></script>
    <