jQuery1.9.1--attr,prop与val方法源码分析
2013-07-14 20:38
841 查看
这里只介绍这几个方法的源码,这部分引用了一个技巧,钩子对象,用来做兼容fixed的对象,后面也有一些使用。钩子对象具体的兼容细节这里就不详解了。
View Code
var nodeHook, boolHook, rclass = /[\t\r\n]/g, rreturn = /\r/g, rfocusable = /^(?:input|select|textarea|button|object)$/i, rclickable = /^(?:a|area)$/i, rboolean = /^(?:checked|selected|autofocus|autoplay|async|controls|defer|disabled|hidden|loop|multiple|open|readonly|required|scoped)$/i, ruseDefault = /^(?:checked|selected)$/i, getSetAttribute = jQuery.support.getSetAttribute, getSetInput = jQuery.support.input; jQuery.fn.extend({ attr: function (name, value) { return jQuery.access(this, jQuery.attr, name, value, arguments.length > 1); }, removeAttr: function (name) { return this.each(function () { jQuery.removeAttr(this, name); }); }, prop: function (name, value) { return jQuery.access(this, jQuery.prop, name, value, arguments.length > 1); }, 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) { } }); }, addClass: function (value) { var classes, elem, cur, clazz, j, i = 0, len = this.length, proceed = typeof value === "string" && value; if (jQuery.isFunction(value)) { return this.each(function (j) { jQuery(this).addClass(value.call(this, j, this.className)); }); } if (proceed) { // The disjunction here is for better compressibility (see removeClass) classes = ( value || "" ).match(core_rnotwhite) || []; for (; i < len; i++) { elem = this[ i ]; cur = elem.nodeType === 1 && ( elem.className ? ( " " + elem.className + " " ).replace(rclass, " ") : " " ); if (cur) { j = 0; while ((clazz = classes[j++])) { if (cur.indexOf(" " + clazz + " ") < 0) { cur += clazz + " "; } } elem.className = jQuery.trim(cur); } } } return this; }, removeClass: function (value) { var classes, elem, cur, clazz, j, i = 0, len = this.length, proceed = arguments.length === 0 || typeof value === "string" && value; if (jQuery.isFunction(value)) { return this.each(function (j) { jQuery(this).removeClass(value.call(this, j, this.className)); }); } if (proceed) { classes = ( value || "" ).match(core_rnotwhite) || []; for (; i < len; i++) { elem = this[ i ]; // This expression is here for better compressibility (see addClass) cur = elem.nodeType === 1 && ( elem.className ? ( " " + elem.className + " " ).replace(rclass, " ") : "" ); if (cur) { j = 0; while ((clazz = classes[j++])) { // Remove *all* instances while (cur.indexOf(" " + clazz + " ") >= 0) { cur = cur.replace(" " + clazz + " ", " "); } } elem.className = value ? jQuery.trim(cur) : ""; } } } return this; }, toggleClass: function (value, stateVal) { var type = typeof value, isBool = typeof stateVal === "boolean"; if (jQuery.isFunction(value)) { return this.each(function (i) { jQuery(this).toggleClass(value.call(this, i, this.className, stateVal), stateVal); }); } return this.each(function () { if (type === "string") { // toggle individual class names var className, i = 0, self = jQuery(this), state = stateVal, classNames = value.match(core_rnotwhite) || []; while ((className = classNames[ i++ ])) { // check each className given, space separated list state = isBool ? state : !self.hasClass(className); self[ state ? "addClass" : "removeClass" ](className); } // Toggle whole class name } else if (type === core_strundefined || type === "boolean") { if (this.className) { // store className if set jQuery._data(this, "__className__", this.className); } // If the element has a class name or if we're passed "false", // then remove the whole classname (if there was one, the above saved it). // Otherwise bring back whatever was previously saved (if anything), // falling back to the empty string if nothing was stored. this.className = this.className || value === false ? "" : jQuery._data(this, "__className__") || ""; } }); }, hasClass: function (selector) { var className = " " + selector + " ", i = 0, l = this.length; for (; i < l; i++) { if (this[i].nodeType === 1 && (" " + this[i].className + " ").replace(rclass, " ").indexOf(className) >= 0) { return true; } } return false; }, val: function (value) { var ret, hooks, isFunction, // 获取伪数组中的第一个元素 elem = this[0]; // 如果没有传参,说明是获取value值 if (!arguments.length) { if (elem) { // 尝试获取valHooks钩子对象, // 如果元素不具有type类型的钩子对象, // 则尝试赋值元素标签键值的钩子对象 hooks = jQuery.valHooks[ elem.type ] || jQuery.valHooks[ elem.nodeName.toLowerCase() ]; // 如果存在钩子对象且有get方法且get返回的不是undefined // 则返回get方法的返回值 if (hooks && "get" in hooks && (ret = hooks.get(elem, "value")) !== undefined) { return ret; } // 否则没有相应的钩子对象,直接获取元素的value值 ret = elem.value; // 如果ret是字符串,返回过滤掉制表符的字符串, // 否则ret为空就返回空字符串, // 否则返回ret return typeof ret === "string" ? // handle most common string cases ret.replace(rreturn, "") : ret == null ? "" : ret; } return; } // 下面是有参数的情况,说明是设置value值 // 先判断value是否为函数 isFunction = jQuery.isFunction(value); // 遍历元素集 return this.each(function (i) { var val, self = jQuery(this); if (this.nodeType !== 1) { return; } // 如果value是函数就执行,然后给ret赋值返回的值 if (isFunction) { val = value.call(this, i, self.val()); } else { val = value; } // 如果value为null或undefined,转化为字符串 // 如果是数字类型也转换为字符串 // 如果是数组类型,使用map方法返回一个返回值数组 if (val == null) { val = ""; } else if (typeof val === "number") { val += ""; } else if (jQuery.isArray(val)) { val = jQuery.map(val, function (value) { return value == null ? "" : value + ""; }); } // 尝试获取钩子对象 hooks = jQuery.valHooks[ this.type ] || jQuery.valHooks[ this.nodeName.toLowerCase() ]; // 如果没有钩子对象,或者钩子对象没有set方法, // 又或者set方法返回的值是undefined, // 就使用正常操作 if (!hooks || !("set" in hooks) || hooks.set(this, val, "value") === undefined) { this.value = val; } }); } }); jQuery.extend({ valHooks: { option: { /* 获取option的value值 */ get: function (elem) { // Blackberry 4.7的attributes.value为undefined但可以使用.value获取 var val = elem.attributes.value; return !val || val.specified ? elem.value : elem.text; } }, /* 获取select的value值,如果是多选则返回数组 */ 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; // 遍历所有选中的项 for (; i < max; i++) { option = options[i]; // 旧版本IE不会更新选中项当表单重置后 if ((option.selected || i === index) && // 不返回被禁用的选项或者在被禁用的optgroup中 (jQuery.support.optDisabled ? !option.disabled : option.getAttribute('disabled') === null) && (!option.parentNode.disabled || !jQuery.nodeName(option.parentNode, 'optgroup')) ) { // 为option设置指定值 value = jQuery(option).val(); // 单选的话我们就不需要用数组了 if (one) { return value; } // 多选就返回数组 values.push(value); } } return values; }, set: function (elem, value) { var values = jQuery.makeArray(value); jQuery(elem).find('option').each(function () { this.selected = jQuery.inArray(jQuery(this).val(), values) >= 0; }); if (!values.length) { elem.selectedIndex = -1; } return values; } } }, attr: function (elem, name, value) { var hooks, notxml, ret, nType = elem.nodeType; // 如果elem的类型是文本,注释或者属性直接退出 if (!elem || nType === 3 || nType === 8 || nType === 2) { return; } // 当不支持attributes时,回退用prop方法 if (typeof elem.getAttribute === core_strundefined) { return jQuery.prop(elem, name, value); } // 是否非XML文档 notxml = nType !== 1 || !jQuery.isXMLDoc(elem); // 如果钩子被定义了则抓取 if (notxml) { name = name.toLowerCase(); // 如果不存在attrHooks钩子对象就尝试获取boolHook的钩子对象, // 否则就用nodeHook这个钩子对象 hooks = jQuery.attrHooks[name] || (rboolean.test(name) ? boolHook : nodeHook); } if (value !== undefined) { // value为null就删除attr属性 if (value === null) { jQuery.removeAttr(elem, name); } else if (hooks && notxml && 'set' in hooks && (ret = hooks.set(elem, value, name)) !== undefined) { // 否则如果存在钩子方法,则返回set方法的返回值 return ret; } else { // 其他情况就直接用setAttribute设置value elem.setAttribute(name, value + ''); } } else if (hooks && notxml && 'get' in hooks && (ret = hooks.get(elem, name)) !== null) { // 如果value是undefined,且存在钩子方法, // 返回get方法的返回值 return ret; } else { // 其他情况(无钩子对象)就使用getAttribute获取value // 在IE9+,Flash对象没有.getAttribute if (typeof elem.getAttribute !== core_strundefined) { ret = elem.getAttribute(name); return ret == null ? undefined : ret; } } }, removeAttr: function (elem, value) { var name, propName, i = 0, // value值可以是空格连接的多个value, // 这里通过正则匹配非空字符串,返回匹配的数组 attrNames = value && value.match(core_rnotwhite); // 如果attrNames存在且elem是元素节点 if (attrNames && elem.nodeType === 1) { // 遍历attrNames数组 while ((name = attrNames[i++])) { // 如果没有propFix对象(将name转换为正确的字符串)就直接使用name作为属性值 propName = jQuery.propFix[name] || name; // 布尔值的属性需要特殊处理 if (rboolean.test(name)) { // 如果不支持获取和设置属性且有selected或checked属性, // 则将defaultName和propName设置为false if (!getSetAttribute && ruseDefault.test(name)) { elem[jQuery.camelCase('default-' + name)] = elem[propName] = false; } else { // 其他情况直接把propName属性设置为false elem[propName] = false; } } else { // 非布尔值属性就调用jQuery.attr方法 jQuery.attr(elem, name, ''); } // 删除元素上的该属性 elem.removeAttribute(getSetAttribute ? name : propName); } } }, 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; } } } }, 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' }, 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); // 如果elem不是xml文档元素,获取被fixed的name和钩子对象 if (notxml) { name = jQuery.propFix[name] || name; hooks = jQuery.propHooks[name]; } // 如果value不是undefined,说明是设置prop if (value !== undefined) { // 如果有钩子对象且存在set方法, // 返回非undefined的方法返回值, // 否则正常情况下直接用elem[name]设置prop if (hooks && 'set' in hooks && (ret = hooks.set(elem, value, name)) !== undefined) { return ret; } else { return (elem[name] = value); } // 如果value是undefined,说明是获取prop属性值 } else { // 有钩子对象用其get方法,没有就用原生的方法 if (hooks && 'get' in hooks && (ret = hooks.get(elem, name)) !== null) { return ret; } else { return elem[name]; } } }, propHooks: { tabIndex: { get: function (elem) { // 当elem的tabindex没有被明确设置时,不会总返回正确的值 var attributeNode = elem.getAttributeNode('tabindex'); return attributeNode && attributeNode.specified ? parseInt(attributeNode.value, 10) : rfocusable.test(elem.nodeName) || rclickable.test(elem.nodeName) && elem.href ? 0 : undefined; } } } }); // Hook for boolean attributes boolHook = { get: function (elem, name) { var // Use .prop to determine if this attribute is understood as boolean prop = jQuery.prop(elem, name), // Fetch it accordingly attr = typeof prop === "boolean" && elem.getAttribute(name), detail = typeof prop === "boolean" ? getSetInput && getSetAttribute ? attr != null : // oldIE fabricates an empty string for missing boolean attributes // and conflates checked/selected into attroperties ruseDefault.test(name) ? elem[ jQuery.camelCase("default-" + name) ] : !!attr : // fetch an attribute node for properties not recognized as boolean elem.getAttributeNode(name); return detail && detail.value !== false ? name.toLowerCase() : undefined; }, set: function (elem, value, name) { if (value === false) { // Remove boolean attributes when set to false jQuery.removeAttr(elem, name); } else if (getSetInput && getSetAttribute || !ruseDefault.test(name)) { // IE<8 needs the *property* name elem.setAttribute(!getSetAttribute && jQuery.propFix[ name ] || name, name); // Use defaultChecked and defaultSelected for oldIE } else { elem[ jQuery.camelCase("default-" + name) ] = elem[ name ] = true; } return name; } };
View Code
相关文章推荐
- jQuery源码分析之prop和removeProp方法
- jQuery源码分析之removeAttr方法和attr方法
- jQuery源码解读之removeAttr()方法分析
- jQuery1.9.1--结构及$方法的工作原理源码分析
- jQuery源码解读之removeAttr()方法分析
- jquery之 css()方法。用法类似的有attr(),prop(),val()
- jQuery-1.9.1源码分析系列(二)jQuery选择器续1
- jQuery-1.9.1源码分析系列(十) 事件系统——事件绑定
- jQuery-1.9.1源码分析系列(十六)ajax——响应数据处理和api整理
- jQuery的prop和attr方法之间区别
- jquery中prop()方法和attr()方法的区别浅析
- jquery中attr和prop的区别分析
- jquery中prop()方法和attr()方法的区别浅析
- Jquery 1.6+ .prop()与.attr()方法比较
- jQuery 1.9.1源码分析系列(十四)之常用jQuery工具
- jquery $(this).attr $(this).val方法使用介绍
- Jquery中attr()与prop()方法的差异
- jQuery-1.9.1源码分析系列(十六)ajax——jsonp原理
- jquery中prop()方法和attr()方法的区别
- Jquery1.9.1源码分析系列(六)延时对象应用之jQuery.ready