1. 程式人生 > >深入瀏覽器相容 細數jQuery Hooks 屬性篇

深入瀏覽器相容 細數jQuery Hooks 屬性篇

本章的目的很簡單,通過鉤子函式更細節的瞭解瀏覽器差異與處理方案,

版本是2.0.3所以不相容ie6.7.8,所以對應了鉤子會少很多。。

總的來說鉤子在.attr().prop().val() and .css() 四種操作中會涉及

屬性操作的鉤子

propFix

propHooks

attrHooks

valHooks

jQuery.propFix  中的物件

image

原始碼部分

1:保留值屬性名字修正

jQuery.propFix: {
    for   :  "htmlFor",
   class  :  "className"
},
  • 由於class屬於JavaScript保留值,因此當我們要操作元素的class屬性
    值時,直接使用obj.getAttribute('class')和obj.setAttribute('class', 'value')可能會遭遇瀏覽器相容性問題,W3C DOM標準為每個節點提供了一個可讀寫的className屬性,作為節點class屬性的對映,標準瀏覽器的都提供了這一屬性的支援,因此,可以使用e.className訪問元素的class屬性值,也可對該屬性進行重新斌值。而IE和Opera中也可使用e.getAttribute('className')和e.setAttribute('className', 'value')訪問及修改class屬性值。相比之下,e.className是W3C DOM標準,仍然是相容性最強的解決辦法。
  • 同理htmlFor用於讀取label標籤的for屬性

測試demo,通過class與className修改元素的屬性

2:與表單操作相關:

反轉下,讓鉤子適配用虛擬碼匹配,目測應該是為了相容開發者輸入大小寫格式不正確

比如輸入錯誤格式:cellpadding

如果jQuery.propFix 轉成cellPadding ,駝峰寫法了

複製程式碼

jQuery.each([
        "tabIndex",
        "readOnly",
        "maxLength",
        "cellSpacing",
        "cellPadding",
        "rowSpan",
        "colSpan",
        "useMap",
        "frameBorder",
        "contentEditable"
    ], function() {
        jQuery.propFix[ this.toLowerCase() ] = this;
    });

複製程式碼

tabIndex 屬性可設定或返回按鈕的 tab 鍵控制次序

readonly 屬性規定輸入欄位為只讀。

maxlength 屬性規定輸入欄位的最大長度,以字元個數計。

cellspacing 屬性規定單元格之間的空間

cellpadding 屬性規定單元邊沿與其內容之間的空白。

rowspan 屬性規定單元格可橫跨的行數。

colspan 屬性規定單元格可橫跨的列數。

HTML <img> 標籤的

usemap 屬性將影象定義為客戶端影象對映

frameBorder 屬性設定或返回是否顯示框架周圍的邊框。

contenteditable 屬性規定是否可編輯元素的內容。

值得一提的是這個方法用的比較巧妙了,收集所有的合集名,然後在每一個上下文回撥中把每一個名字傳遞到propFix方法,key轉成小寫,value儲存正確寫法

jQuery.propFix[ this.toLowerCase() ] = this;

jQuery.propHooks 屬性方法

關於tabIndex屬性

複製程式碼

propHooks: {
    tabIndex: {
        get: function( elem ) {
            return elem.hasAttribute( "tabindex" ) || rfocusable.test( elem.nodeName ) || elem.href ?
                elem.tabIndex :
                -1;
        }
    }
}

複製程式碼

複製程式碼

// Support: IE9+
// Selectedness for an option in an optgroup can be inaccurate
if ( !jQuery.support.optSelected ) {
    jQuery.propHooks.selected = {
        get: function( elem ) {
            var parent = elem.parentNode;
            if ( parent && parent.parentNode ) {
                parent.parentNode.selectedIndex;
            }
            return null;
        }
    };
}

複製程式碼

jQuery.attrHooks 方法

複製程式碼

attrHooks: {
    type: {
        set: function( elem, value ) {
            if ( !jQuery.support.radioValue && value === "radio" && jQuery.nodeName(elem, "input") ) {
                // Setting the type on a radio button after the value resets the value in IE6-9
                // Reset value to default in case type is set after value during creation
                var val = elem.value;
                elem.setAttribute( "type", value );
                if ( val ) {
                    elem.value = val;
                }
                return value;
            }
        }
    }
},

複製程式碼

jQuery.valHooks 方法

根據 JQuery api文件 的描述,.val() 函式有兩種用法,分別用來獲取或設定元素的值,這裡只介紹獲取值的方法。

文件裡面說 .val 主要是用於獲取元素的value,比如 inputselect 和 textarea等,

什麼是元素的value?”

用 select 標籤為例,如下的程式碼:

測試程式碼

複製程式碼

<select id="choise">
  <option value="1">One</option>
  <option value="2">Two</option>
  <option value="3">Three</option>
  <option value="4">Four</option>
</select>

複製程式碼

這裡的option有2個值,一個是value = 1 另一個則是 text = One

option 真正的 value 應該是其 value 屬性中的值,而不是 option 標籤中間所包含的內容

在 w3school 的文件中對 option 的 value 屬性的做了如下定義:

The value attribute specifies the value to be sent to a server when a form is submitted.
The content between the opening <option> and closing </option> tags is what the browsers will display in a drop-down list. However, the value of the value attribute is what will be sent to the server when a form is submitted.
Note: If the value attribute is not specified, the content will be passed as the value instead.

這一點對於所有可以擁有 value 屬性的標籤都是相同的,即在對一個元素呼叫 .val() 函式時,首先取其 value 屬性的值,如果沒有的話,再使用其 text值。

那麼接下來就要引入我們的valHooks,針對option,select的處理

        valHooks: {
            option: {
                get: function( elem ) {
                    // attributes.value is undefined in Blackberry 4.7 but
                    // uses .value. See #6932
                    var val = elem.attributes.value;
                    return !val || val.specified ? elem.value : elem.text;
                }
            },
            select: {
                get: function( elem ) {
                    var value, option,
                        options = elem.options,
                        index = elem.selectedIndex,
                        one = elem.type === "select-one" || index < 0,
                        values = one ? null : [],
                        max = one ? index + 1 : options.length,
                        i = index < 0 ?
                            max :
                            one ? index : 0;

                    // Loop through all the selected options
                    for ( ; i < max; i++ ) {
                        option = options[ i ];

                        // IE6-9 doesn't update selected after form reset (#2551)
                        if ( ( option.selected || i === index ) &&
                            // Don't return options that are disabled or in a disabled optgroup
                            ( jQuery.support.optDisabled ? !option.disabled : option.getAttribute("disabled") === null ) &&
                            ( !option.parentNode.disabled || !jQuery.nodeName( option.parentNode, "optgroup" ) ) ) {

                            // Get the specific value for the option
                            value = jQuery( option ).val();

                            // We don't need an array for one selects
                            if ( one ) {
                                return value;
                            }

                            // Multi-Selects return an array
                            values.push( value );
                        }
                    }

                    return values;
                },

                set: function( elem, value ) {
                    var optionSet, option,
                        options = elem.options,
                        values = jQuery.makeArray( value ),
                        i = options.length;

                    while ( i-- ) {
                        option = options[ i ];
                        if ( (option.selected = jQuery.inArray( jQuery(option).val(), values ) >= 0) ) {
                            optionSet = true;
                        }
                    }

                    // force browsers to behave consistently when non-matching value is set
                    if ( !optionSet ) {
                        elem.selectedIndex = -1;
                    }
                    return values;
                }
            }
        },

對於val方法的取值部分

複製程式碼

if ( elem ) {
    hooks = jQuery.valHooks[ elem.type ] || jQuery.valHooks[ elem.nodeName.toLowerCase() ];

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

    ret = elem.value;

    return typeof ret === "string" ?
        // handle most common string cases
        ret.replace(rreturn, "") :
        // handle cases where value is null/undef or number
        ret == null ? "" : ret;
}

複製程式碼

通過jQuery.valHooks匹配對應的鉤子處理方法

image

節點屬性的差異對比:

select : 建立單選或多選選單

  1. type:"select-one"
  2. tagName: "SELECT"
  3. value: "111"
  4. textContent: "↵ Single↵ Single2↵"

option : 元素定義下拉列表中的一個選項

  1. tagName: "OPTION"
  2. value: "111"
  3. text: "Single"
  4. textContent: "Single"

radio : 表單中的單選按鈕

  1. type: "radio"
  2. value: "11111"

checkbox : 選擇框

  1. type: "checkbox"
  2. value: "11111"

根據對比select的節點type是'select-one’與其餘幾個還不同,所以jQuery在適配的時候採用優先查詢type,否則就找nodeName的策略

hooks = jQuery.valHooks[ elem.type ] || jQuery.valHooks[ elem.nodeName.toLowerCase() ];

如果鉤子匹配到了,並且還存在get方法,那麼就要呼叫這個方法了,如果有返回值就返回當前的這個最終值

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

那麼具體的相容問題就會跑到對應的鉤子方法中處理了

複製程式碼

// Loop through all the selected options
                    for ( ; i < max; i++ ) {
                        option = options[ i ];

                        // IE6-9 doesn't update selected after form reset (#2551)
                        if ( ( option.selected || i === index ) &&
                            // Don't return options that are disabled or in a disabled optgroup
                            ( jQuery.support.optDisabled ? !option.disabled : option.getAttribute("disabled") === null ) &&
                            ( !option.parentNode.disabled || !jQuery.nodeName( option.parentNode, "optgroup" ) ) ) {

                            // Get the specific value for the option
                            value = jQuery( option ).val();

                            // We don't need an array for one selects
                            if ( one ) {
                                return value;
                            }

                            // Multi-Selects return an array
                            values.push( value );
                        }
                    }

複製程式碼

通過selectedIndex 屬性可設定或返回下拉列表中被選選項的索引號

通過遞迴select中的所有option

在包裝jQuery( option ).val()

從而呼叫option的鉤子方法get ,

複製程式碼

get: function( elem ) {
                    // attributes.value is undefined in Blackberry 4.7 but
                    // uses .value. See #6932
                    var val = elem.attributes.value;
                    return !val || val.specified ? elem.value : elem.text;
                }

複製程式碼

elem.attributes.value返回對應的option的val值

所以這裡就相容的預設返回val,否則就返回text的內容了

radio,checkbox

複製程式碼

// Radios and checkboxes getter/setter
    jQuery.each([ "radio", "checkbox" ], function() {
        jQuery.valHooks[ this ] = {
            set: function( elem, value ) {
                if ( jQuery.isArray( value ) ) {
                    return ( elem.checked = jQuery.inArray( jQuery(elem).val(), value ) >= 0 );
                }
            }
        };
        if ( !jQuery.support.checkOn ) {
            jQuery.valHooks[ this ].get = function( elem ) {
                // Support: Webkit
                // "" is returned instead of "on" if a value isn't specified
                return elem.getAttribute("value") === null ? "on" : elem.value;
            };
        }
    });

複製程式碼

如果您看完本篇文章感覺不錯,請點選一下右下角的推薦來支援一下博主,謝謝!

如果是原創文章,轉載請註明出處!!!