1. 程式人生 > >jQuery屬性操作之.attr()

jQuery屬性操作之.attr()

目錄

轉載請註明出處

@

.attr()

1..attr()的四種用法
大致用法:

  • 呼叫形式:$("xxx").attr(attrName);
    獲取匹配到的所有元素中的第一個元素的指定屬性的屬性值.
  • 呼叫形式:$("xxx").attr(attrName,value):
    • 設定/新增匹配到的所有元素的指定屬性的屬性值
    • 如果value=null,指定屬性將被刪除
  • 呼叫形式:$("xxx").attr(attrObject):
    用"屬性-值"的鍵值對構成的物件來設定匹配到的所有元素的一個或多個屬性.
  • 呼叫形式:$("xxx").attr(attrName,attrFn(index,val)):
    用一個函式attrFn的返回值作為value來設定某一個匹配元素的指定屬性.(attrFn函式的引數有明確規定:index--該匹配元素在jQuery物件中的index值,也就是它的鍵值.attr--當前該元素的該屬性的值(舊值))

用法詳解:
(1)..attr()原始碼定義:

jQuery.fn.extend( {
    attr: function( name, value ) {
        return access( this, jQuery.attr, name, value, arguments.length > 1 );
    }
}

可見,.attr的核心是函式access(),而該函式不需要例項即可呼叫,屬於直接定義在jQuery上的'靜態函式'.

jQuery.access()原始碼定義:

var access = function( elems, fn, key, value, chainable, emptyGet, raw ) {
    var i = 0,
        length = elems.length,
        bulk = key == null; //bulk用於判斷key值是否為null(注:null==undefined的結果為true)

    // 設定多個值(當key引數為物件時,注:jQuery.type實現原理是[[class]])
    if ( jQuery.type( key ) === "object" ) {
        chainable = true;
        for ( i in key ) {
            access( elems, fn, i, key[ i ], true, emptyGet, raw );
        }

    // 設定一個值(當key為非物件型別時)
    } else if ( value !== undefined ) {
        chainable = true;

        if ( !jQuery.isFunction( value ) ) {
            raw = true; //判斷value值是否為函式型別.如果不是函式,設定raw值,為if(fn)以及if(bulk)埋下伏筆
        }

        if ( bulk ) { //當傳入key值為null時的處理情況(這段程式碼,我們在分析.attr()時不會用到)

            // 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 ) { //對elems中的每一個元素應用fn方法,第三個引數由raw決定
            for ( ; i < length; i++ ) {
                fn(
                    elems[ i ],
                    key,
                    raw ? value : value.call( elems[ i ], i, fn( elems[ i ], key ) )
                );
            }
        }
    }

    return chainable ? //返回值設定
        elems :

        // Gets
        bulk ?
            fn.call( elems ) :
            length ? fn( elems[ 0 ], key ) : emptyGet;
}

如上圖,由於access函式用途並不侷限於我們的.attr(),作為像筆者一樣的jQuery初學者,原始碼中有許多程式碼語句我們不能很好的理解(比如:if(bulk)部分).由於暫時接觸到的jQuery內容較少較淺,我們如果一味深究,可能既搞不清楚原理,還浪費時間.所以,我們可以用一種簡化的思想來分析問題: 只關注程式碼中跟我們當前問題有關的部分,忽略無關的,用不到的部分.

具體做法:我們將.attr()的四種用法的實際傳參情況帶入jQuery.access函式的定義中,刪除我們進不去的if語句程式碼塊,只保留會用到的程式碼語句.


呼叫形式:$("xxx").attr(name)

attr定義:

attr: function( name, value=undefined ) {
    return jQuery.access( this, jQuery.attr, name, value=undefined, arguments.length > 1 );
}

access呼叫簡化:

var access = function( this, jQuery.attr, name, value=undefined, chainable=false, emptyGet=undefined, raw=undefined ) {
    var i = 0,
        length = this.length, //jQuery物件的length屬性(jQuery物件總是有length屬性,它是類陣列物件)
        bulk = false;

    return jQuery.attr( this[0], name );
    };   

同樣,我們找到jQuery.attr()靜態方法的原始碼定義,用同樣的簡化方法分析,得到:

attr: function( elem, name, value=undefined ) {  // elem:DOM物件  name:屬性名/鍵名 value:屬性值
    var ret, hooks,
        nType = 1;

    ret = jQuery.find.attr( elem, name );

    return ret;
}   

其中,唯一不確定的是jQuery.find.attr,然而我們繼續找下去則是有關Sizzle選擇器引擎的問題,這對於我們初學者來說過於複雜.因此,我們再簡化一下,帶入實際情景,檢測這一函式的輸出:

var $p = $('#jQueryTest')[0];

console.log(jQuery.find.attr($p,'id')); //jQueryTest   

因此,大概知道該函式該種傳參情況下的作用是返回指定DOM元素的指定屬性的值.
由jQuery.access簡化程式碼中的return jQuery.attr( this[0], name );可知,只傳入一個name引數的情況下,確實只會返回jQuery物件中的索引為'0'的DOM物件的指定屬性的屬性值.


呼叫形式:$("xxx").attr(name,value);

attr定義:

attr: function( name, value) {  //此時的this是一個類陣列
        return jQuery.access( this, fn=jQuery.attr, name, value, chainable=true);
    }

access呼叫簡化:

var access = function( this, jQuery.attr, name, value, chainable=true, emptyGet=undefined, raw=undefined ) {
    var i = 0,
        length = this.length, //jQuery物件的length屬性,表示找到的匹配的DOM元素的個數
        bulk = false;


        chainable = true;

        if ( !jQuery.isFunction( value ) ) { //當value值不為函式時,設定raw為true,這是為了下一步if(jQuery.attr)中的raw判斷做鋪墊
            raw = true; 
        }

        
        if ( jQuery.attr ) { //jQuery.attr,為true

            for ( ; i < length; i++ ) {  //用for迴圈是因為此時的this是一個包含多個DOM元素的jQuery物件
                jQuery.attr(
                    this[ i ],
                    name,
                    raw ? value : value.call( elems[ i ], i, jQuery.attr( this[ i ], name ) )                       
                );//raw為true,也就是value不為函式時,用value作第三引數
            }     
        }
    }

    return this; //返回jQuery物件本身

};

而,我們用簡化的方法分析此種情況下的jQuery.attr(this,name,value):

attr: function( this[i], name, value ) {
    var ret, hooks,
        nType = this[i].nodeType;

    if ( value !== undefined ) {  //判斷為true,進入if語句
        if ( value === null ) {
            jQuery.removeAttr( this[i], name );//如果value為null,刪除該jQuery物件的所有匹配元素的指定屬性
            return;
        }

        this[i].setAttribute( name, value + "" );//設定當前DOM元素的指定屬性的屬性值
        return value;
    }

    ret = jQuery.find.attr( this[i], name ); //刪除了屬性,返回null;否則,返回指定屬性的屬性值

    return ret == null ? undefined : ret; //如果刪除了指定屬性,返回undefined;如果修改了屬性,返回指定屬性值
}

呼叫形式:$("xxx").attr(attrObject);

attr定義:

attr: function( name=attrObject ) {
        return jQuery.access( this, jQuery.attr, name=attrObject, value=undefined, false);
    }

access呼叫簡化:

var access = function( this, fn, name, value=undefined, chainable=false, emptyGet=undefined, raw=undefined ) {
    var i = 0,
        length = this.length, //元素的length屬性
        bulk = false;

    // 設定多個value值
    if ( jQuery.type( name ) === "object" ) { //如果傳入的name形參為物件型別
        chainable = true;
        for ( i in name ) { //對每一個物件中的屬性名及屬性值再次呼叫本身(遞迴)
            access( this, fn, i, name[ i ], true, emptyGet, raw );
        }

    }
    return elems;  //返回jQuery物件本身

};   

可見,對於一個由"屬性-屬性值"鍵值對構成的物件,會對其中的每一個屬性都呼叫access設定一次.由於程式碼中使用的for-in迴圈,所以enumerable為false的鍵值對是無效的.


呼叫形式:$("xxx").attr(name,attrFn);

attr定義:

attr: function( name, value=attrFn ) {
        return jQuery.access( this, jQuery.attr, name, value=attrFn, chainable=true );
    }

access呼叫簡化:

var access = function( this, jQuery.attr, name, value=attrFn, chainable=true, emptyGet=undefined, raw=undefined ) {
    var i = 0,
        length = this.length, //jQuery物件的length屬性
        bulk = false;

        chainable = true;

        if ( jQuery.attr ) {  //true,進入if語句
            for ( ; i < length; i++ ) {
                jQuery.attr(
                    this[ i ],
                    name,
                    attrFn.call( this[ i ], i, jQuery.attr( this[ i ], name ) )  // 呼叫attrFn,其返回值作為第三個引數                                             
                );
            }
        }

    return elems; // 返回jQuery物件本身
};

attrFn.call( this[ i ], i, jQuery.attr( this[ i ], name ) )可知,attrFn的引數限制就是源自這一行程式碼:(this[i]是呼叫attrFn的元素,後面兩個是引數,一個是jQuery物件中的索引值,一個是當前元素的指定屬性name的值的查詢返回)

[特別注意:attrFn的兩個引數雖然有規定,但是不需要我們真的傳參,而是函式體內部使用索引值或者當前屬性值的一個介面]