jQuery attributes(上)

iteye_3829發表於2013-05-21

jQuery attributes提供了文件節點的屬性操作方法。

為了更好的理解,我們先解決jQuery core中的access引數設定函式。

 

jQuery.access

jQuery.access是一個專用於引數設定以及讀取的方法。

jQuery鏈式操作中我們對BigInteger的值獲取是通過方法val來獲取的,很顯然此時val後面不能再鏈式操作了。所以:

如果要讀取引數值,那麼這個操作就不能鏈式操作。

/*************************
 *    elems: 接受操作的元素組
 *    fn: 設定或讀取函式
 *    key: 要設定的屬性
 *    value: 要設定的值,也可以是函式
 *    chainable: 是否可以鏈式操作,即是讀還是寫操作
 *    emptyGet: 讀取如果為空用什麼表示
 *    raw: value能否直接傳給fn,如果的確就是想將屬性設定成函式value,那麼要把這個值設成true
 */
jQuery.access = function( elems, fn, key, value, chainable, emptyGet, raw ) {
    var i = 0,
        length = elems.length,
        //key是否為空
        bulk = key == null;

    // 設定許多屬性
    if ( jQuery.type( key ) === "object" ) {
        chainable = true;
        for ( i in key ) {
            //迭代執行設定一個值
            jQuery.access( elems, fn, i, key[i], true, emptyGet, raw );
        }

    // 只設定一個值
    } else if ( value !== undefined ) {
        chainable = true;

        //如果value不是函式
        if ( !jQuery.isFunction( value ) ) {
            raw = true;
        }

        //如果key為空
        if ( bulk ) {
            // 如果raw為true,即value不是函式
            if ( raw ) {
                //執行fn,其this為elems,引數為value
                fn.call( elems, value );
                fn = null;

            // 如果value是函式
            } else {
                //用bulk儲存fn
                bulk = fn;
                //將fn變成特定形式
                fn = function( elem, key, value ) {
                    return bulk.call( jQuery( elem ), value );
                };
            }
        }
        
        //如果fn存在
        if ( fn ) {
            //遍歷所有elems
            for ( ; i < length; i++ ) {
                //value是函式則執行fn(elem, key, value.call(elem, i, fn(elem, key)))
                //value非函式則執行fn(elem, key, value)
                fn( elems[i], key, raw ? value : value.call( elems[i], i, fn( elems[i], key ) ) );
            }
        }
    }

    //如果是可鏈式操作的則返回elems
    return chainable ?
        elems :

        // 否則則是讀取操作
        bulk ?
            fn.call( elems ) :
            // 否則如果長度不為0,則返回fn( elems[0], key ),否則返回空
            length ? fn( elems[0], key ) : emptyGet;
}

jQuery官方文件沒有該函式,應該說這是jQuery的一個內部工具。主要是為了實現jQuery中設定與讀取複用性邏輯以及其內迴圈的,也就是為了節省寫程式碼而已。

 

jQuery.fn.attr()

fn也就是jQuery例項物件的方法集。

fn中的方法attr也就是我們常用的attr方法。

如獲取em標籤的title屬性:

var title = $("em").attr("title");

那麼這個函式式怎麼實現的呢?

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

當引數長度為0或只為1時,則是讀取操作,不能鏈式操作。

而這裡用的設定函式是jQuery.attr,即實際執行時會執行jQuery.attr(elem, name, value)。

那麼jQuery.attr是如何實現的呢?

/***************************
 *    elem: 要操作的元素
 *    name: 元素的屬性名
 *    value: 要改變的值
 */
jQuery.attr = function( elem, name, value ) {
    var ret, hooks, notxml,
        nType = elem.nodeType;

    // 不處理text,comment,attribute節點
    if ( !elem || nType === 3 || nType === 8 || nType === 2 ) {
        return;
    }

    // 如果屬性不支援則使用jQuery.prop
    if ( typeof elem.getAttribute === "undefined" ) {
        return jQuery.prop( elem, name, value );
    }

    //是否不是XML
    notxml = nType !== 1 || !jQuery.isXMLDoc( elem );

    // 如果不是XML
    if ( notxml ) {
        //那麼所有屬性名應當是小寫
        name = name.toLowerCase();
        //如果屬性定義了,那麼就抓住必要的鉤子,解決IE6-9的相關問題
        hooks = jQuery.attrHooks[ name ] || ( rboolean.test( name ) ? boolHook : nodeHook );
    }

    //如果value不是沒有定義,即寫入操作
    if ( value !== undefined ) {
        //如果value為空,則是刪除操作
        if ( value === null ) {
            jQuery.removeAttr( elem, name );
        //如果鉤子有set方法,則設定了之後,返回其值
        } else if ( hooks && notxml && "set" in hooks && (ret = hooks.set( elem, value, name )) !== undefined ) {
            return ret;
        //否則使用setAttribute方法
        } else {
            elem.setAttribute( name, value + "" );
            return value;
        }
    //如果是讀取操作,如果鉤子有get方法,則通過get得到返回值
    } else if ( hooks && notxml && "get" in hooks && (ret = hooks.get( elem, name )) !== null ) {
        return ret;
    //如果沒有get方法
    } else {

        // IE9+中Flash物件沒有.getAttrubute方法,判斷防止錯誤
        if ( typeof elem.getAttribute !== "undefined" ) {
            ret =  elem.getAttribute( name );
        }

        // 返回undefined或則值
        return ret == null ?
            undefined :
            ret;
    }
};

 

jQuery.removeAttr

removeAttr = function( elem, value ) {
    var name, propName,
        i = 0,
        //分解value成為多個屬性的陣列
        attrNames = value && value.match( core_rnotwhite );
    
    //如果有要刪除的屬性名
    if ( attrNames && elem.nodeType === 1 ) {
        //遍歷所有屬性名
        while ( (name = attrNames[i++]) ) {
            //看看需不需要用propFix來修改名字,不用直接用name
            propName = jQuery.propFix[ name ] || name;

            // 如果屬性是布林量先改成false
            if ( rboolean.test( name ) ) {
                elem[ propName ] = false;
            }

            //刪除屬性
            elem.removeAttribute( name );
        }
    }
};

jQuery.propFix幹了什麼呢?

實際只是修改一下屬性名,比如很多人喜歡用class來表示css類名,但實際上是className。

jQuery.propFix = {
    tabindex: "tabIndex",
    readonly: "readOnly",
    "for": "htmlFor",
    "class": "className",
    maxlength: "maxLength",
    cellspacing: "cellSpacing",
    cellpadding: "cellPadding",
    rowspan: "rowSpan",
    colspan: "colSpan",
    usemap: "useMap",
    frameborder: "frameBorder",
    contenteditable: "contentEditable"
};

 

jQuery.fn.removeAttr

這個函式實現比較簡單,只是用each方法來呼叫jQuery.removeAttr而已。

jQuery.fn.removeAttr = function( name ) {
    return this.each(function() {
        jQuery.removeAttr( this, name );
    });
};

 

jQuery.fn.prop

jQuery.fn.prop = function( name, value ) {
    return jQuery.access( this, jQuery.prop, name, value, arguments.length > 1 );
};

可見jQuery.fn.prop和jQuery.fn.attr差不多。

 

jQuery.prop

jQuery.prop = function( elem, name, value ) {
    var ret, hooks, notxml,
        nType = elem.nodeType;

    if ( !elem || nType === 3 || nType === 8 || nType === 2 ) {
        return;
    }

    notxml = nType !== 1 || !jQuery.isXMLDoc( elem );

    if ( notxml ) {
        // 修復name和鉤子
        name = jQuery.propFix[ name ] || name;
        hooks = jQuery.propHooks[ name ];
    }

    if ( value !== undefined ) {
        if ( hooks && "set" in hooks && (ret = hooks.set( elem, value, name )) !== undefined ) {
            return ret;

        } else {
            return ( elem[ name ] = value );
        }

    } else {
        if ( hooks && "get" in hooks && (ret = hooks.get( elem, name )) !== null ) {
            return ret;

        } else {
            return elem[ name ];
        }
    }
};

由於和jQuery.attr差不多,就不備註了。

 

jQuery.fn.removeProp

removeProp = function( name ) {
    name = jQuery.propFix[ name ] || name;
    return this.each(function() {
        // try/catch handles cases where IE balks (such as removing a property on window)
        try {
            this[ name ] = undefined;
            delete this[ name ];
        } catch( e ) {}
    });
};

removeProp相對簡單些,只是通過each將所有元素的屬性設為undefined然後delete掉而已。

 

Attrubute和Property

從原始碼我們可以發現,jQuery.attr如果找不到相應的方法會使用jQuery.prop。

jQuery 1.6加入jQuery.prop方法後,對很多人來說可能根本沒啥用,因為用jQuery.attr方法肯定是對的。

但jQuery.attr和jQuery.prop到底差別在哪裡呢?

這是Attrubute和Property的差別。

jQuery.attr方法會處理Attrubute和Property,但jQuery.prop只處理Property。

雖然這兩個單詞都可以翻譯成“屬性”,但是這兩個實際上是不同的。

我們用一個例子來說明這個問題:

function Demo(){
    var attrs = {};
    this.name = "Bob";
    this.setAttr = function(name, value){
        attrs[name] = value;
        return value;
    }
    this.getAttr = function(name){
        return attrs[name];
    }
}

那麼對於一個例項:

var i = new Demo();
i.name    //Property
i.setAttr("name", "Tom");
i.getAttr("name")    //Attrubute

所以jQuery文件中對jQuery.prop的解釋是:獲取在匹配的元素集中的第一個元素的屬性值。

相關文章