zoukankan      html  css  js  c++  java
  • jQuery1.9.1--attr,prop与val方法源码分析

    这里只介绍这几个方法的源码,这部分引用了一个技巧,钩子对象,用来做兼容fixed的对象,后面也有一些使用。钩子对象具体的兼容细节这里就不详解了。

      1 var nodeHook, boolHook,
      2         rclass = /[	
    ]/g,
      3         rreturn = /
    /g,
      4         rfocusable = /^(?:input|select|textarea|button|object)$/i,
      5         rclickable = /^(?:a|area)$/i,
      6         rboolean = /^(?:checked|selected|autofocus|autoplay|async|controls|defer|disabled|hidden|loop|multiple|open|readonly|required|scoped)$/i,
      7         ruseDefault = /^(?:checked|selected)$/i,
      8         getSetAttribute = jQuery.support.getSetAttribute,
      9         getSetInput = jQuery.support.input;
     10 
     11     jQuery.fn.extend({
     12         attr: function (name, value) {
     13             return jQuery.access(this, jQuery.attr, name, value, arguments.length > 1);
     14         },
     15         removeAttr: function (name) {
     16             return this.each(function () {
     17                 jQuery.removeAttr(this, name);
     18             });
     19         },
     20         prop: function (name, value) {
     21             return jQuery.access(this, jQuery.prop, name, value, arguments.length > 1);
     22         },
     23         removeProp: function (name) {
     24             name = jQuery.propFix[ name ] || name;
     25             return this.each(function () {
     26                 // try/catch handles cases where IE balks (such as removing a property on window)
     27                 try {
     28                     this[ name ] = undefined;
     29                     delete this[ name ];
     30                 } catch (e) {
     31                 }
     32             });
     33         },
     34         addClass: function (value) {
     35             var classes, elem, cur, clazz, j,
     36                 i = 0,
     37                 len = this.length,
     38                 proceed = typeof value === "string" && value;
     39 
     40             if (jQuery.isFunction(value)) {
     41                 return this.each(function (j) {
     42                     jQuery(this).addClass(value.call(this, j, this.className));
     43                 });
     44             }
     45 
     46             if (proceed) {
     47                 // The disjunction here is for better compressibility (see removeClass)
     48                 classes = ( value || "" ).match(core_rnotwhite) || [];
     49 
     50                 for (; i < len; i++) {
     51                     elem = this[ i ];
     52                     cur = elem.nodeType === 1 && ( elem.className ?
     53                         ( " " + elem.className + " " ).replace(rclass, " ") :
     54                         " "
     55                         );
     56 
     57                     if (cur) {
     58                         j = 0;
     59                         while ((clazz = classes[j++])) {
     60                             if (cur.indexOf(" " + clazz + " ") < 0) {
     61                                 cur += clazz + " ";
     62                             }
     63                         }
     64                         elem.className = jQuery.trim(cur);
     65 
     66                     }
     67                 }
     68             }
     69 
     70             return this;
     71         },
     72         removeClass: function (value) {
     73             var classes, elem, cur, clazz, j,
     74                 i = 0,
     75                 len = this.length,
     76                 proceed = arguments.length === 0 || typeof value === "string" && value;
     77 
     78             if (jQuery.isFunction(value)) {
     79                 return this.each(function (j) {
     80                     jQuery(this).removeClass(value.call(this, j, this.className));
     81                 });
     82             }
     83             if (proceed) {
     84                 classes = ( value || "" ).match(core_rnotwhite) || [];
     85 
     86                 for (; i < len; i++) {
     87                     elem = this[ i ];
     88                     // This expression is here for better compressibility (see addClass)
     89                     cur = elem.nodeType === 1 && ( elem.className ?
     90                         ( " " + elem.className + " " ).replace(rclass, " ") :
     91                         ""
     92                         );
     93 
     94                     if (cur) {
     95                         j = 0;
     96                         while ((clazz = classes[j++])) {
     97                             // Remove *all* instances
     98                             while (cur.indexOf(" " + clazz + " ") >= 0) {
     99                                 cur = cur.replace(" " + clazz + " ", " ");
    100                             }
    101                         }
    102                         elem.className = value ? jQuery.trim(cur) : "";
    103                     }
    104                 }
    105             }
    106 
    107             return this;
    108         },
    109         toggleClass: function (value, stateVal) {
    110             var type = typeof value,
    111                 isBool = typeof stateVal === "boolean";
    112 
    113             if (jQuery.isFunction(value)) {
    114                 return this.each(function (i) {
    115                     jQuery(this).toggleClass(value.call(this, i, this.className, stateVal), stateVal);
    116                 });
    117             }
    118 
    119             return this.each(function () {
    120                 if (type === "string") {
    121                     // toggle individual class names
    122                     var className,
    123                         i = 0,
    124                         self = jQuery(this),
    125                         state = stateVal,
    126                         classNames = value.match(core_rnotwhite) || [];
    127 
    128                     while ((className = classNames[ i++ ])) {
    129                         // check each className given, space separated list
    130                         state = isBool ? state : !self.hasClass(className);
    131                         self[ state ? "addClass" : "removeClass" ](className);
    132                     }
    133 
    134                     // Toggle whole class name
    135                 } else if (type === core_strundefined || type === "boolean") {
    136                     if (this.className) {
    137                         // store className if set
    138                         jQuery._data(this, "__className__", this.className);
    139                     }
    140 
    141                     // If the element has a class name or if we're passed "false",
    142                     // then remove the whole classname (if there was one, the above saved it).
    143                     // Otherwise bring back whatever was previously saved (if anything),
    144                     // falling back to the empty string if nothing was stored.
    145                     this.className = this.className || value === false ? "" : jQuery._data(this, "__className__") || "";
    146                 }
    147             });
    148         },
    149         hasClass: function (selector) {
    150             var className = " " + selector + " ",
    151                 i = 0,
    152                 l = this.length;
    153             for (; i < l; i++) {
    154                 if (this[i].nodeType === 1 && (" " + this[i].className + " ").replace(rclass, " ").indexOf(className) >= 0) {
    155                     return true;
    156                 }
    157             }
    158 
    159             return false;
    160         },
    161         val: function (value) {
    162             var ret, hooks, isFunction,
    163             // 获取伪数组中的第一个元素
    164                 elem = this[0];
    165 
    166             // 如果没有传参,说明是获取value值
    167             if (!arguments.length) {
    168                 if (elem) {
    169                     // 尝试获取valHooks钩子对象,
    170                     // 如果元素不具有type类型的钩子对象,
    171                     // 则尝试赋值元素标签键值的钩子对象
    172                     hooks = jQuery.valHooks[ elem.type ] || jQuery.valHooks[ elem.nodeName.toLowerCase() ];
    173 
    174                     // 如果存在钩子对象且有get方法且get返回的不是undefined
    175                     // 则返回get方法的返回值
    176                     if (hooks && "get" in hooks && (ret = hooks.get(elem, "value")) !== undefined) {
    177                         return ret;
    178                     }
    179 
    180                     // 否则没有相应的钩子对象,直接获取元素的value值
    181                     ret = elem.value;
    182 
    183                     // 如果ret是字符串,返回过滤掉制表符的字符串,
    184                     // 否则ret为空就返回空字符串,
    185                     // 否则返回ret
    186                     return typeof ret === "string" ?
    187                         // handle most common string cases
    188                         ret.replace(rreturn, "") :
    189                         ret == null ? "" : ret;
    190                 }
    191 
    192                 return;
    193             }
    194 
    195             // 下面是有参数的情况,说明是设置value值
    196 
    197             // 先判断value是否为函数
    198             isFunction = jQuery.isFunction(value);
    199 
    200             // 遍历元素集
    201             return this.each(function (i) {
    202                 var val,
    203                     self = jQuery(this);
    204 
    205                 if (this.nodeType !== 1) {
    206                     return;
    207                 }
    208 
    209                 // 如果value是函数就执行,然后给ret赋值返回的值
    210                 if (isFunction) {
    211                     val = value.call(this, i, self.val());
    212                 } else {
    213                     val = value;
    214                 }
    215 
    216                 // 如果value为null或undefined,转化为字符串
    217                 // 如果是数字类型也转换为字符串
    218                 // 如果是数组类型,使用map方法返回一个返回值数组
    219                 if (val == null) {
    220                     val = "";
    221                 } else if (typeof val === "number") {
    222                     val += "";
    223                 } else if (jQuery.isArray(val)) {
    224                     val = jQuery.map(val, function (value) {
    225                         return value == null ? "" : value + "";
    226                     });
    227                 }
    228 
    229                 // 尝试获取钩子对象
    230                 hooks = jQuery.valHooks[ this.type ] || jQuery.valHooks[ this.nodeName.toLowerCase() ];
    231 
    232                 // 如果没有钩子对象,或者钩子对象没有set方法,
    233                 // 又或者set方法返回的值是undefined,
    234                 // 就使用正常操作
    235                 if (!hooks || !("set" in hooks) || hooks.set(this, val, "value") === undefined) {
    236                     this.value = val;
    237                 }
    238             });
    239         }
    240     });
    241 
    242     jQuery.extend({
    243         valHooks: {
    244             option: {
    245                 /*
    246                  获取option的value值
    247                  */
    248                 get: function (elem) {
    249                     // Blackberry 4.7的attributes.value为undefined但可以使用.value获取
    250                     var val = elem.attributes.value;
    251                     return !val || val.specified ? elem.value : elem.text;
    252                 }
    253             },
    254             /* 获取select的value值,如果是多选则返回数组 */
    255             select: {
    256                 get: function (elem) {
    257                     var value, option,
    258                         options = elem.options,
    259                         index = elem.selectedIndex,
    260                         one = elem.type === 'select-one' || index < 0,
    261                         values = one ? null : [],
    262                         max = one ? index + 1 : options.length,
    263                         i = index < 0 ? max :
    264                             one ? index : 0;
    265 
    266                     // 遍历所有选中的项
    267                     for (; i < max; i++) {
    268                         option = options[i];
    269 
    270                         // 旧版本IE不会更新选中项当表单重置后
    271                         if ((option.selected || i === index) &&
    272                             // 不返回被禁用的选项或者在被禁用的optgroup中
    273                             (jQuery.support.optDisabled ? !option.disabled : option.getAttribute('disabled') === null) &&
    274                             (!option.parentNode.disabled || !jQuery.nodeName(option.parentNode, 'optgroup'))
    275                             ) {
    276                             // 为option设置指定值
    277                             value = jQuery(option).val();
    278 
    279                             // 单选的话我们就不需要用数组了
    280                             if (one) {
    281                                 return value;
    282                             }
    283 
    284                             // 多选就返回数组
    285                             values.push(value);
    286                         }
    287                     }
    288 
    289                     return values;
    290                 },
    291                 set: function (elem, value) {
    292                     var values = jQuery.makeArray(value);
    293 
    294                     jQuery(elem).find('option').each(function () {
    295                         this.selected = jQuery.inArray(jQuery(this).val(), values) >= 0;
    296                     });
    297 
    298                     if (!values.length) {
    299                         elem.selectedIndex = -1;
    300                     }
    301                     return values;
    302                 }
    303             }
    304         },
    305         attr: function (elem, name, value) {
    306             var hooks, notxml, ret,
    307                 nType = elem.nodeType;
    308 
    309             // 如果elem的类型是文本,注释或者属性直接退出
    310             if (!elem || nType === 3 || nType === 8 || nType === 2) {
    311                 return;
    312             }
    313 
    314             // 当不支持attributes时,回退用prop方法
    315             if (typeof elem.getAttribute === core_strundefined) {
    316                 return jQuery.prop(elem, name, value);
    317             }
    318 
    319             // 是否非XML文档
    320             notxml = nType !== 1 || !jQuery.isXMLDoc(elem);
    321 
    322             // 如果钩子被定义了则抓取
    323             if (notxml) {
    324                 name = name.toLowerCase();
    325                 // 如果不存在attrHooks钩子对象就尝试获取boolHook的钩子对象,
    326                 // 否则就用nodeHook这个钩子对象
    327                 hooks = jQuery.attrHooks[name] || (rboolean.test(name) ? boolHook : nodeHook);
    328             }
    329 
    330             if (value !== undefined) {
    331                 // value为null就删除attr属性
    332                 if (value === null) {
    333                     jQuery.removeAttr(elem, name);
    334                 } else if (hooks && notxml && 'set' in hooks && (ret = hooks.set(elem, value, name)) !== undefined) {
    335                     // 否则如果存在钩子方法,则返回set方法的返回值
    336                     return ret;
    337                 } else {
    338                     // 其他情况就直接用setAttribute设置value
    339                     elem.setAttribute(name, value + '');
    340                 }
    341             } else if (hooks && notxml && 'get' in hooks && (ret = hooks.get(elem, name)) !== null) {
    342                 // 如果value是undefined,且存在钩子方法,
    343                 // 返回get方法的返回值
    344                 return ret;
    345             } else {
    346                 // 其他情况(无钩子对象)就使用getAttribute获取value
    347                 // 在IE9+,Flash对象没有.getAttribute
    348                 if (typeof elem.getAttribute !== core_strundefined) {
    349                     ret = elem.getAttribute(name);
    350 
    351                     return ret == null ?
    352                         undefined :
    353                         ret;
    354                 }
    355             }
    356         },
    357         removeAttr: function (elem, value) {
    358             var name, propName,
    359                 i = 0,
    360                 // value值可以是空格连接的多个value,
    361                 // 这里通过正则匹配非空字符串,返回匹配的数组
    362                 attrNames = value && value.match(core_rnotwhite);
    363 
    364             // 如果attrNames存在且elem是元素节点
    365             if (attrNames && elem.nodeType === 1) {
    366                 // 遍历attrNames数组
    367                 while ((name = attrNames[i++])) {
    368                     // 如果没有propFix对象(将name转换为正确的字符串)就直接使用name作为属性值
    369                     propName = jQuery.propFix[name] || name;
    370 
    371                     // 布尔值的属性需要特殊处理
    372                     if (rboolean.test(name)) {
    373                         // 如果不支持获取和设置属性且有selected或checked属性,
    374                         // 则将defaultName和propName设置为false
    375                         if (!getSetAttribute && ruseDefault.test(name)) {
    376                             elem[jQuery.camelCase('default-' + name)] = elem[propName] = false;
    377                         } else {
    378                             // 其他情况直接把propName属性设置为false
    379                             elem[propName] = false;
    380                         }
    381                     } else {
    382                         // 非布尔值属性就调用jQuery.attr方法
    383                         jQuery.attr(elem, name, '');
    384                     }
    385 
    386                     // 删除元素上的该属性
    387                     elem.removeAttribute(getSetAttribute ? name : propName);
    388                 }
    389             }
    390         },
    391         attrHooks: {
    392             type: {
    393                 set: function (elem, value) {
    394                     if (!jQuery.support.radioValue && value === 'radio' && jQuery.nodeName(elem, 'input')) {
    395                         // Setting the type on a radio button after the value resets the value in IE6-9
    396                         // Reset value to default in case type is set after value during creation
    397                         var val = elem.value;
    398                         elem.setAttribute('type', value);
    399                         if (val) {
    400                             elem.value = val;
    401                         }
    402                         return value;
    403                     }
    404                 }
    405             }
    406         },
    407         propFix: {
    408             tabindex: 'tabIndex',
    409             readonly: 'readOnly',
    410             'for': 'htmlFor',
    411             'class': 'className',
    412             maxlength: 'maxLength',
    413             cellspacing: 'cellSpacing',
    414             cellpadding: 'cellPadding',
    415             rowspan: 'rowSpan',
    416             colspan: 'colSpan',
    417             usemap: 'useMap',
    418             frameborder: 'frameBorder',
    419             contenteditable: 'contentEditable'
    420         },
    421         prop: function (elem, name, value) {
    422             var ret, hooks, notxml,
    423                 nType = elem.nodeType;
    424 
    425             if (!elem || nType === 3 || nType === 8 || nType === 2) {
    426                 return;
    427             }
    428 
    429             notxml = nType !== 1 || !jQuery.isXMLDoc(elem);
    430 
    431             // 如果elem不是xml文档元素,获取被fixed的name和钩子对象
    432             if (notxml) {
    433                 name = jQuery.propFix[name] || name;
    434                 hooks = jQuery.propHooks[name];
    435             }
    436 
    437             // 如果value不是undefined,说明是设置prop
    438             if (value !== undefined) {
    439                 // 如果有钩子对象且存在set方法,
    440                 // 返回非undefined的方法返回值,
    441                 // 否则正常情况下直接用elem[name]设置prop
    442                 if (hooks && 'set' in hooks && (ret = hooks.set(elem, value, name)) !== undefined) {
    443                     return ret;
    444                 } else {
    445                     return (elem[name] = value);
    446                 }
    447 
    448                 // 如果value是undefined,说明是获取prop属性值
    449             } else {
    450                 // 有钩子对象用其get方法,没有就用原生的方法
    451                 if (hooks && 'get' in hooks && (ret = hooks.get(elem, name)) !== null) {
    452                     return ret;
    453                 } else {
    454                     return elem[name];
    455                 }
    456             }
    457         },
    458         propHooks: {
    459             tabIndex: {
    460                 get: function (elem) {
    461                     // 当elem的tabindex没有被明确设置时,不会总返回正确的值
    462                     var attributeNode = elem.getAttributeNode('tabindex');
    463 
    464                     return attributeNode && attributeNode.specified ?
    465                         parseInt(attributeNode.value, 10) :
    466                         rfocusable.test(elem.nodeName) || rclickable.test(elem.nodeName) && elem.href ?
    467                             0 :
    468                             undefined;
    469                 }
    470             }
    471         }
    472     });
    473 
    474     // Hook for boolean attributes
    475     boolHook = {
    476         get: function (elem, name) {
    477             var
    478             // Use .prop to determine if this attribute is understood as boolean
    479                 prop = jQuery.prop(elem, name),
    480 
    481             // Fetch it accordingly
    482                 attr = typeof prop === "boolean" && elem.getAttribute(name),
    483                 detail = typeof prop === "boolean" ?
    484 
    485                     getSetInput && getSetAttribute ?
    486                         attr != null :
    487                         // oldIE fabricates an empty string for missing boolean attributes
    488                         // and conflates checked/selected into attroperties
    489                         ruseDefault.test(name) ?
    490                             elem[ jQuery.camelCase("default-" + name) ] :
    491                             !!attr :
    492 
    493                     // fetch an attribute node for properties not recognized as boolean
    494                     elem.getAttributeNode(name);
    495 
    496             return detail && detail.value !== false ?
    497                 name.toLowerCase() :
    498                 undefined;
    499         },
    500         set: function (elem, value, name) {
    501             if (value === false) {
    502                 // Remove boolean attributes when set to false
    503                 jQuery.removeAttr(elem, name);
    504             } else if (getSetInput && getSetAttribute || !ruseDefault.test(name)) {
    505                 // IE<8 needs the *property* name
    506                 elem.setAttribute(!getSetAttribute && jQuery.propFix[ name ] || name, name);
    507 
    508                 // Use defaultChecked and defaultSelected for oldIE
    509             } else {
    510                 elem[ jQuery.camelCase("default-" + name) ] = elem[ name ] = true;
    511             }
    512 
    513             return name;
    514         }
    515     };
    View Code
  • 相关阅读:
    2018.09.08什么是ajax
    2018.09.03怎样让网页自适应所有屏幕宽度
    2018.08.25字符串和二维数组之间的转换
    2018.08.20MySQL常用命令总结(二)
    2018.08.15解决MySQL1290问题
    2018.08.13MySQL常用命令总结(一)
    2018.08.11MySQL无法启动错误码1067的解决方法
    2018.08.10 css中position定位问题
    2018.08.10jQuery导航栏置顶
    2018.08.07css实现图片放大
  • 原文地址:https://www.cnblogs.com/webFrontDev/p/3189845.html
Copyright © 2011-2022 走看看