From 64a0005a3b5a168a421efa1d8b253de234620229 Mon Sep 17 00:00:00 2001 From: timmywil Date: Sat, 2 Apr 2011 17:05:04 -0400 Subject: [PATCH 1/3] 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. --- src/attributes.js | 178 +++++++++++++++++++++++++++------------------- 1 file changed, 103 insertions(+), 75 deletions(-) diff --git a/src/attributes.js b/src/attributes.js index 59972105..4c59a675 100644 --- a/src/attributes.js +++ b/src/attributes.js @@ -154,82 +154,35 @@ jQuery.fn.extend({ }, val: function( value ) { + var hooks, val, + elem = this[0]; + if ( !arguments.length ) { - var elem = this[0]; - if ( elem ) { - if ( jQuery.nodeName( elem, "option" ) ) { - // 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; + hooks = jQuery.valHooks[ elem.nodeName.toLowerCase() ] || jQuery.valHooks[ elem.type ]; + + if ( hooks && "get" in hooks && (val = hooks.get( elem )) !== undefined ) { + return val; } - // 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 undefined; } - var isFunction = jQuery.isFunction(value); + var isFunction = jQuery.isFunction( value ); - return this.each(function(i) { - var self = jQuery(this), val = 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()); + val = value.call( this, i, self.val() ); } // Treat null/undefined as ""; convert numbers to string @@ -237,27 +190,16 @@ jQuery.fn.extend({ val = ""; } else if ( typeof val === "number" ) { val += ""; - } else if ( jQuery.isArray(val) ) { - val = jQuery.map(val, function (value) { + } 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; + hooks = jQuery.valHooks[ this.nodeName.toLowerCase() ] || jQuery.valHooks[ this.type ]; - } 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; - }); - - if ( !values.length ) { - this.selectedIndex = -1; - } - - } else { + // If set returns undefined, fall back to normal setting + if ( !hooks || ("set" in hooks && hooks.set( this, val ) === undefined) ) { this.value = val; } }); @@ -265,6 +207,71 @@ jQuery.fn.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: { val: 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 ); From 94fff6ff625be9fbbb54db53d9acec63f0919370 Mon Sep 17 00:00:00 2001 From: timmywil Date: Mon, 4 Apr 2011 14:22:58 -0400 Subject: [PATCH 2/3] Remove the unused radiocheck regex --- src/attributes.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/attributes.js b/src/attributes.js index 4c59a675..c8106e98 100644 --- a/src/attributes.js +++ b/src/attributes.js @@ -6,8 +6,7 @@ var rclass = /[\n\t\r]/g, rspecialurl = /^(?:href|src|style)$/, rtype = /^(?:button|input)$/i, rfocusable = /^(?:button|input|object|select|textarea)$/i, - rclickable = /^a(?:rea)?$/i, - rradiocheck = /^(?:radio|checkbox)$/i; + rclickable = /^a(?:rea)?$/i; jQuery.props = { "for": "htmlFor", From d47c0ae4228a59fcf731f626d650bb55c4e34609 Mon Sep 17 00:00:00 2001 From: timmywil Date: Tue, 5 Apr 2011 22:40:12 -0400 Subject: [PATCH 3/3] Performance testing: localize val to each block and only set val to value when not a function --- src/attributes.js | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/attributes.js b/src/attributes.js index c8106e98..9fdb1492 100644 --- a/src/attributes.js +++ b/src/attributes.js @@ -153,15 +153,15 @@ jQuery.fn.extend({ }, val: function( value ) { - var hooks, val, + var hooks, ret, elem = this[0]; if ( !arguments.length ) { if ( elem ) { hooks = jQuery.valHooks[ elem.nodeName.toLowerCase() ] || jQuery.valHooks[ elem.type ]; - if ( hooks && "get" in hooks && (val = hooks.get( elem )) !== undefined ) { - return val; + if ( hooks && "get" in hooks && (ret = hooks.get( elem )) !== undefined ) { + return ret; } return (elem.value || "").replace(rreturn, ""); @@ -173,15 +173,16 @@ jQuery.fn.extend({ var isFunction = jQuery.isFunction( value ); return this.each(function( i ) { - var self = jQuery(this); + var self = jQuery(this), val; if ( this.nodeType !== 1 ) { return; } - val = value; if ( isFunction ) { val = value.call( this, i, self.val() ); + } else { + val = value; } // Treat null/undefined as ""; convert numbers to string