http://www.timmywillison.com/2011/A-New-jQuery.attr-and-the-New-jQuer...
From the comments:
"Thanks for this Timmy. Over the years I've read that jQuery's attr
implementation was broken and needed a rewrite, but never knew why or
how it was broken. Looking forward to 1.6!"
They still don't know why or how (and I don't think Timmy does
either). They don't know what they are looking forward too. These
are simply zombies.
Sorry for the line numbers and screwed up indentation. I'll post a
better copy when I review it.
But this doesn't really need review, does it? It's the same old BS,
but slightly rearranged (with little regard for the ramifications).
Certainly nothing close to competence, which is disappointing after
years of examples. And, as mentioned, most of the issues they attempt
to address here are related to IE6/7. Too little, too late and too
bad.
The line numbers do serve to illustrate the futility though as it
appears the entire boondoggle fits in roughly 300 lines of code
(including comments). This is the "wheel" they so feverishly
protected all those years? What was the line? Don't reinvent the
wheel? No, wait three plus years and some clod will come along and
reinvent it for you (breaking your code to boot).
Lazy, defensive and ignorant is not a good combination for
programming.
jQuery.extend({
2066 valHooks: {
2067 option: {
2068 get: function( elem ) {
2069 // attributes.value is undefined in Blackberry 4.7 but
2070 // uses .value. See #6932
2071 var val = elem.attributes.value;
2072 return !val || val.specified ? elem.value : elem.text;
2073 }
2074 },
2075 select: {
2076 get: function( elem ) {
2077 var index = elem.selectedIndex,
2078 values = [],
2079 options = elem.options,
2080 one = elem.type === "select-one";
2081
2082 // Nothing was selected
2083 if ( index < 0 ) {
2084 return null;
2085 }
2086
2087 // Loop through all the selected options
2088 for ( var i = one ? index : 0, max = one ? index + 1 :
options.length; i < max; i++ ) {
2089 var option = options[ i ];
2090
2091 // Don't return options that are disabled or in a disabled
optgroup
2092 if ( option.selected && (jQuery.support.optDisabled ? !
option.disabled : option.getAttribute("disabled") === null) &&
2093 (!option.parentNode.disabled || !
jQuery.nodeName( option.parentNode, "optgroup" )) ) {
2094
2095 // Get the specific value for the option
2096 value = jQuery( option ).val();
2097
2098 // We don't need an array for one selects
2099 if ( one ) {
2100 return value;
2101 }
2102
2103 // Multi-Selects return an array
2104 values.push( value );
2105 }
2106 }
2107
2108 // Fixes Bug #2551 -- select.val() broken in IE after
form.reset()
2109 if ( one && !values.length && options.length ) {
2110 return jQuery( options[ index ] ).val();
2111 }
2112
2113 return values;
2114 },
2115
2116 set: function( elem, value ) {
2117 var values = jQuery.makeArray( value );
2118
2119 jQuery(elem).find("option").each(function() {
2120 this.selected = jQuery.inArray( jQuery(this).val(), values ) >=
0;
2121 });
2122
2123 if ( !values.length ) {
2124 elem.selectedIndex = -1;
2125 }
2126 return values;
2127 }
2128 }
2129 },
2130
2131 attrFn: {
2132 val: true,
2133 css: true,
2134 html: true,
2135 text: true,
2136 data: true,
2137 width: true,
2138 height: true,
2139 offset: true
2140 },
2141
2142 attrFix: {
2143 // Always normalize to ensure hook usage
2144 tabindex: "tabIndex",
2145 readonly: "readOnly"
2146 },
2147
2148 attr: function( elem, name, value, pass ) {
2149 var nType = elem.nodeType;
2150
2151 // don't get/set attributes on text, comment and attribute nodes
2152 if ( !elem || nType === 3 || nType === 8 || nType === 2 ) {
2153 return undefined;
2154 }
2155
2156 if ( pass && name in jQuery.attrFn ) {
2157 return jQuery( elem )[ name ]( value );
2158 }
2159
2160 var ret, hooks,
2161 notxml = nType !== 1 || !jQuery.isXMLDoc( elem );
2162
2163 // Normalize the name if needed
2164 name = notxml && jQuery.attrFix[ name ] || name;
2165
2166 // Get the appropriate hook, or the formHook
2167 // if getSetAttribute is not supported and we have form objects
in IE6/7
2168 hooks = jQuery.attrHooks[ name ] || ( elem.nodeName === "FORM"&&
formHook );
2169
2170 if ( value !== undefined ) {
2171
2172 if ( value === null ) {
2173 jQuery.removeAttr( elem, name );
2174 return undefined;
2175
2176 } else if ( hooks && "set" in hooks && notxml && (ret =
hooks.set( elem, value, name )) !== undefined ) {
2177 return ret;
2178
2179 } else {
2180 elem.setAttribute( name, "" + value );
2181 return value;
2182 }
2183
2184 } else {
2185
2186 if ( hooks && "get" in hooks && notxml ) {
2187 return hooks.get( elem, name );
2188
2189 } else {
2190
2191 ret = elem.getAttribute( name );
2192
2193 // Non-existent attributes return null, we normalize to undefined
2194 return ret === null ?
2195 undefined :
2196 ret;
2197 }
2198 }
2199 },
2200
2201 removeAttr: function( elem, name ) {
2202 if ( elem.nodeType === 1 ) {
2203 name = jQuery.attrFix[ name ] || name;
2204
2205 if ( jQuery.support.getSetAttribute ) {
2206 // Use removeAttribute in browsers that support it
2207 elem.removeAttribute( name );
2208 } else {
2209 jQuery.attr( elem, name, "" );
2210 elem.removeAttributeNode( elem.getAttributeNode( name ) );
2211 }
2212 }
2213 },
2214
2215 attrHooks: {
2216 type: {
2217 set: function( elem, value ) {
2218 // We can't allow the type property to be changed (since it
causes problems in IE)
2219 if ( rtype.test( elem.nodeName ) && elem.parentNode ) {
2220 jQuery.error( "type property can't be changed" );
2221 }
2222 }
2223 },
2224 tabIndex: {
2225 get: function( elem ) {
2226 // elem.tabIndex doesn't always return the correct value when it
hasn't been explicitly set
2227 //
http://fluidproject.org/blog/2008/0...and-removing-tabindex-values-with-javascript/
2228 var attributeNode = elem.getAttributeNode("tabIndex");
2229
2230 return attributeNode && attributeNode.specified ?
2231 parseInt( attributeNode.value, 10 ) :
2232 rfocusable.test( elem.nodeName ) ||
rclickable.test( elem.nodeName ) && elem.href ?
2233 0 :
2234 undefined;
2235 }
2236 }
2237 },
2238
2239 propFix: {},
2240
2241 prop: function( elem, name, value ) {
2242 var nType = elem.nodeType;
2243
2244 // don't get/set properties on text, comment and attribute nodes
2245 if ( !elem || nType === 3 || nType === 8 || nType === 2 ) {
2246 return undefined;
2247 }
2248
2249 var ret, hooks,
2250 notxml = nType !== 1 || !jQuery.isXMLDoc( elem );
2251
2252 // Try to normalize/fix the name
2253 name = notxml && jQuery.propFix[ name ] || name;
2254
2255 hooks = jQuery.propHooks[ name ];
2256
2257 if ( value !== undefined ) {
2258 if ( hooks && "set" in hooks && (ret = hooks.set( elem, value,
name )) !== undefined ) {
2259 return ret;
2260
2261 } else {
2262 return (elem[ name ] = value);
2263 }
2264
2265 } else {
2266 if ( hooks && "get" in hooks && (ret = hooks.get( elem, name )) !
== undefined ) {
2267 return ret;
2268
2269 } else {
2270 return elem[ name ];
2271 }
2272 }
2273 },
2274
2275 propHooks: {}
2276});
2277
2278// IE6/7 do not support getting/setting some attributes with get/
setAttribute
2279if ( !jQuery.support.getSetAttribute ) {
2280 jQuery.attrFix = jQuery.extend( jQuery.attrFix, {
2281 "for": "htmlFor",
2282 "class": "className",
2283 maxlength: "maxLength",
2284 cellspacing: "cellSpacing",
2285 cellpadding: "cellPadding",
2286 rowspan: "rowSpan",
2287 colspan: "colSpan",
2288 usemap: "useMap",
2289 frameborder: "frameBorder"
2290 });
2291
2292 // Use this for any attribute on a form in IE6/7
2293 // And the name attribute
2294 formHook = jQuery.attrHooks.name = jQuery.attrHooks.value =
jQuery.valHooks.button = {
2295 get: function( elem, name ) {
2296 if ( name === "value" && !jQuery.nodeName( elem, "button" ) ) {
2297 return elem.getAttribute( name );
2298 }
2299 var ret = elem.getAttributeNode( name );
2300 // Return undefined if not specified instead of empty string
2301 return ret && ret.specified ?
2302 ret.nodeValue :
2303 undefined;
2304 },
2305 set: function( elem, value, name ) {
2306 // Check form objects in IE (multiple bugs related)
2307 // Only use nodeValue if the attribute node exists on the form
2308 var ret = elem.getAttributeNode( name );
2309 if ( ret ) {
2310 ret.nodeValue = value;
2311 return value;
2312 }
2313 }
2314 };
2315
2316 // Set width and height to auto instead of 0 on empty string( Bug
#8150 )
2317 // This is for removals
2318 jQuery.each([ "width", "height" ], function( i, name ) {
2319 jQuery.attrHooks[ name ] =
jQuery.extend( jQuery.attrHooks[ name ], {
2320 set: function( elem, value ) {
2321 if ( value === "" ) {
2322 elem.setAttribute( name, "auto" );
2323 return value;
2324 }
2325 }
2326 });
2327 });
2328}
2329
2330// Remove certain attrs if set to false
2331jQuery.each([ "selected", "checked", "readOnly", "disabled" ],
function( i, name ) {
2332 jQuery.attrHooks[ name ] =
jQuery.extend( jQuery.attrHooks[ name ], {
2333 set: function( elem, value ) {
2334 if ( value === true ) {
2335 elem.setAttribute( name, name );
2336 return value;
2337 } else if ( value === false ) {
2338 jQuery.removeAttr( elem, name );
2339 return value;
2340 }
2341 }
2342 });
2343});
2344
2345// Some attributes require a special call on IE
2346if ( !jQuery.support.hrefNormalized ) {
2347 jQuery.each([ "href", "src", "width", "height" ], function( i,
name ) {
2348 jQuery.attrHooks[ name ] =
jQuery.extend( jQuery.attrHooks[ name ], {
2349 get: function( elem ) {
2350 var ret = elem.getAttribute( name, 2 );
2351 return ret === null ? undefined : ret;
2352 }
2353 });
2354 });
2355}
2356
2357if ( !jQuery.support.style ) {
2358 jQuery.attrHooks.style = {
2359 get: function( elem ) {
2360 // Return undefined in the case of empty string
2361 // Normalize to lowercase since IE uppercases css property names
2362 return elem.style.cssText.toLowerCase() || undefined;
2363 },
2364 set: function( elem, value ) {
2365 return (elem.style.cssText = "" + value);
2366 }
2367 };
2368}
2369
2370// Safari mis-reports the default selected property of an option
2371// Accessing the parent's selectedIndex property fixes it
2372if ( !jQuery.support.optSelected ) {
2373 jQuery.propHooks.selected =
jQuery.extend( jQuery.propHooks.selected, {
2374 get: function( elem ) {
2375 var parent = elem.parentNode;
2376
2377 if ( parent ) {
2378 parent.selectedIndex;
2379
2380 // Make sure that it also works with optgroups, see #5701
2381 if ( parent.parentNode ) {
2382 parent.parentNode.selectedIndex;
2383 }
2384 }
2385 }
2386 });
2387}
2388
2389// Radios and checkboxes getter/setter
2390if ( !jQuery.support.checkOn ) {
2391 jQuery.each([ "radio", "checkbox" ], function() {
2392 jQuery.valHooks[ this ] = {
2393 get: function( elem ) {
2394 // Handle the case where in Webkit "" is returned instead of "on"
if a value isn't specified
2395 return elem.getAttribute("value") === null ? "on" : elem.value;
2396 }
2397 };
2398 });
2399}
2400jQuery.each([ "radio", "checkbox" ], function() {
2401 jQuery.valHooks[ this ] = jQuery.extend( jQuery.valHooks[ this ],
{
2402 set: function( elem, value ) {
2403 if ( jQuery.isArray( value ) ) {
2404 return (elem.checked = jQuery.inArray( jQuery(elem).val(),
value ) >= 0);
2405 }
2406 }
2407 });
2408});
2409