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,82 +154,35 @@ jQuery.fn.extend({
}, },
val: function( value ) { val: function( value ) {
var hooks, val,
elem = this[0];
if ( !arguments.length ) { if ( !arguments.length ) {
var elem = this[0];
if ( elem ) { if ( elem ) {
if ( jQuery.nodeName( elem, "option" ) ) { hooks = jQuery.valHooks[ elem.nodeName.toLowerCase() ] || jQuery.valHooks[ elem.type ];
// attributes.value is undefined in Blackberry 4.7 but
// uses .value. See #6932 if ( hooks && "get" in hooks && (val = hooks.get( elem )) !== undefined ) {
var val = elem.attributes.value; return val;
return !val || val.specified ? elem.value : elem.text;
} }
// We need to handle select boxes special
if ( jQuery.nodeName( elem, "select" ) ) {
var index = elem.selectedIndex,
values = [],
options = elem.options,
one = elem.type === "select-one";
// Nothing was selected
if ( index < 0 ) {
return null;
}
// Loop through all the selected options
for ( var i = one ? index : 0, max = one ? index + 1 : options.length; i < max; i++ ) {
var option = options[ i ];
// Don't return options that are disabled or in a disabled optgroup
if ( option.selected && (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 );
}
}
// Fixes Bug #2551 -- select.val() broken in IE after form.reset()
if ( one && !values.length && options.length ) {
return jQuery( options[ index ] ).val();
}
return values;
}
// Handle the case where in Webkit "" is returned instead of "on" if a value isn't specified
if ( rradiocheck.test( elem.type ) && !jQuery.support.checkOn ) {
return elem.getAttribute("value") === null ? "on" : elem.value;
}
// Everything else, we just grab the value
return (elem.value || "").replace(rreturn, ""); return (elem.value || "").replace(rreturn, "");
} }
return undefined; return undefined;
} }
var isFunction = jQuery.isFunction(value); var isFunction = jQuery.isFunction( value );
return this.each(function(i) { return this.each(function( i ) {
var self = jQuery(this), val = value; var self = jQuery(this);
if ( this.nodeType !== 1 ) { if ( this.nodeType !== 1 ) {
return; return;
} }
val = value;
if ( isFunction ) { if ( isFunction ) {
val = value.call(this, i, self.val()); val = value.call( this, i, self.val() );
} }
// Treat null/undefined as ""; convert numbers to string // Treat null/undefined as ""; convert numbers to string
@ -237,27 +190,16 @@ jQuery.fn.extend({
val = ""; val = "";
} else if ( typeof val === "number" ) { } else if ( typeof val === "number" ) {
val += ""; val += "";
} else if ( jQuery.isArray(val) ) { } else if ( jQuery.isArray( val ) ) {
val = jQuery.map(val, function (value) { val = jQuery.map(val, function ( value ) {
return value == null ? "" : value + ""; return value == null ? "" : value + "";
}); });
} }
if ( jQuery.isArray(val) && rradiocheck.test( this.type ) ) { hooks = jQuery.valHooks[ this.nodeName.toLowerCase() ] || jQuery.valHooks[ this.type ];
this.checked = jQuery.inArray( self.val(), val ) >= 0;
} else if ( jQuery.nodeName( this, "select" ) ) { // If set returns undefined, fall back to normal setting
var values = jQuery.makeArray(val); if ( !hooks || ("set" in hooks && hooks.set( this, val ) === undefined) ) {
jQuery( "option", this ).each(function() {
this.selected = jQuery.inArray( jQuery(this).val(), values ) >= 0;
});
if ( !values.length ) {
this.selectedIndex = -1;
}
} else {
this.value = val; this.value = val;
} }
}); });
@ -265,6 +207,71 @@ jQuery.fn.extend({
}); });
jQuery.extend({ jQuery.extend({
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 index = elem.selectedIndex,
values = [],
options = elem.options,
one = elem.type === "select-one";
// Nothing was selected
if ( index < 0 ) {
return null;
}
// Loop through all the selected options
for ( var i = one ? index : 0, max = one ? index + 1 : options.length; i < max; i++ ) {
var option = options[ i ];
// Don't return options that are disabled or in a disabled optgroup
if ( option.selected && (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 );
}
}
// Fixes Bug #2551 -- select.val() broken in IE after form.reset()
if ( one && !values.length && options.length ) {
return jQuery( options[ index ] ).val();
}
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;
}
}
},
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 );