A more modest valHooks proposal

- The main difference is that this does not allow arbitrarily adding hooks to any collection of elements.

- Modularizes val into a set of easily maintainable and conditional hooks.

- valHooks is placed at jQuery.valHooks

  + This could technically be extended, but I do not see it being used except in very rare cases since you can only apply valHooks to nodeNames and input types, and not a collection of elements as before. We could theoretically privatize valHooks taking it off of jQuery and only use it internally for our own convenience, but again, I do not believe this patch carries with it the dangers of the first proposal.

- Slightly improved performance of val on radios and checkboxes for browsers that support checkOn, given the conditional attachment of its hook.
This commit is contained in:
timmywil 2011-04-02 17:05:04 -04:00
parent ff06d411d2
commit 64a0005a3b

View File

@ -154,19 +154,70 @@ jQuery.fn.extend({
}, },
val: function( value ) { val: function( value ) {
if ( !arguments.length ) { var hooks, val,
var elem = this[0]; elem = this[0];
if ( !arguments.length ) {
if ( elem ) { if ( elem ) {
if ( jQuery.nodeName( elem, "option" ) ) { hooks = jQuery.valHooks[ elem.nodeName.toLowerCase() ] || jQuery.valHooks[ elem.type ];
if ( hooks && "get" in hooks && (val = hooks.get( elem )) !== undefined ) {
return val;
}
return (elem.value || "").replace(rreturn, "");
}
return undefined;
}
var isFunction = jQuery.isFunction( value );
return this.each(function( i ) {
var self = jQuery(this);
if ( this.nodeType !== 1 ) {
return;
}
val = value;
if ( isFunction ) {
val = value.call( this, i, self.val() );
}
// Treat null/undefined as ""; convert numbers to string
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.nodeName.toLowerCase() ] || jQuery.valHooks[ this.type ];
// If set returns undefined, fall back to normal setting
if ( !hooks || ("set" in hooks && hooks.set( this, val ) === undefined) ) {
this.value = val;
}
});
}
});
jQuery.extend({
valHooks: {
option: {
get: function( elem ) {
// attributes.value is undefined in Blackberry 4.7 but // attributes.value is undefined in Blackberry 4.7 but
// uses .value. See #6932 // uses .value. See #6932
var val = elem.attributes.value; var val = elem.attributes.value;
return !val || val.specified ? elem.value : elem.text; return !val || val.specified ? elem.value : elem.text;
} }
},
// We need to handle select boxes special select: {
if ( jQuery.nodeName( elem, "select" ) ) { get: function( elem ) {
var index = elem.selectedIndex, var index = elem.selectedIndex,
values = [], values = [],
options = elem.options, options = elem.options,
@ -186,7 +237,7 @@ jQuery.fn.extend({
(!option.parentNode.disabled || !jQuery.nodeName( option.parentNode, "optgroup" )) ) { (!option.parentNode.disabled || !jQuery.nodeName( option.parentNode, "optgroup" )) ) {
// Get the specific value for the option // Get the specific value for the option
value = jQuery(option).val(); value = jQuery( option ).val();
// We don't need an array for one selects // We don't need an array for one selects
if ( one ) { if ( one ) {
@ -204,67 +255,23 @@ jQuery.fn.extend({
} }
return values; return values;
} },
// Handle the case where in Webkit "" is returned instead of "on" if a value isn't specified set: function( elem, value ) {
if ( rradiocheck.test( elem.type ) && !jQuery.support.checkOn ) { var values = jQuery.makeArray( value );
return elem.getAttribute("value") === null ? "on" : elem.value;
}
// Everything else, we just grab the value jQuery(elem).find("option").each(function() {
return (elem.value || "").replace(rreturn, "");
}
return undefined;
}
var isFunction = jQuery.isFunction(value);
return this.each(function(i) {
var self = jQuery(this), val = value;
if ( this.nodeType !== 1 ) {
return;
}
if ( isFunction ) {
val = value.call(this, i, self.val());
}
// Treat null/undefined as ""; convert numbers to string
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 + "";
});
}
if ( jQuery.isArray(val) && rradiocheck.test( this.type ) ) {
this.checked = jQuery.inArray( self.val(), val ) >= 0;
} else if ( jQuery.nodeName( this, "select" ) ) {
var values = jQuery.makeArray(val);
jQuery( "option", this ).each(function() {
this.selected = jQuery.inArray( jQuery(this).val(), values ) >= 0; this.selected = jQuery.inArray( jQuery(this).val(), values ) >= 0;
}); });
if ( !values.length ) { if ( !values.length ) {
this.selectedIndex = -1; elem.selectedIndex = -1;
} }
return values;
}
}
},
} else {
this.value = val;
}
});
}
});
jQuery.extend({
attrFn: { attrFn: {
val: true, val: true,
css: true, css: true,
@ -386,4 +393,25 @@ jQuery.extend({
} }
}); });
// Radios and checkboxes getter/setter
if ( !jQuery.support.checkOn ) {
jQuery.each([ "radio", "checkbox" ], function() {
jQuery.valHooks[ this ] = {
get: function( elem ) {
// Handle the case where in Webkit "" is returned instead of "on" if a value isn't specified
return elem.getAttribute("value") === null ? "on" : elem.value;
}
};
});
}
jQuery.each([ "radio", "checkbox" ], function() {
jQuery.valHooks[ this ] = jQuery.extend( jQuery.valHooks[ this ], {
set: function( elem, value ) {
if ( jQuery.isArray( value ) ) {
return (elem.checked = jQuery.inArray( jQuery(elem).val(), value ) >= 0);
}
}
});
});
})( jQuery ); })( jQuery );