Data: Separate data & css/effects camelCase implementations
[jquery.git] / src / css.js
blobf9277bf6c2553a923ae7158065578578daca7506
1 define( [
2 "./core",
3 "./core/access",
4 "./var/rcssNum",
5 "./css/var/rnumnonpx",
6 "./css/var/cssExpand",
7 "./css/isAutoPx",
8 "./css/cssCamelCase",
9 "./css/var/getStyles",
10 "./css/var/swap",
11 "./css/curCSS",
12 "./css/adjustCSS",
13 "./css/addGetHookIf",
14 "./css/support",
15 "./css/finalPropName",
17 "./core/init",
18 "./core/ready",
19 "./selector" // contains
20 ], function( jQuery, access, rcssNum, rnumnonpx, cssExpand, isAutoPx, cssCamelCase,
21 getStyles, swap, curCSS, adjustCSS, addGetHookIf, support, finalPropName ) {
23 "use strict";
25 var
27 // Swappable if display is none or starts with table
28 // except "table", "table-cell", or "table-caption"
29 // See here for display values: https://developer.mozilla.org/en-US/docs/CSS/display
30 rdisplayswap = /^(none|table(?!-c[ea]).+)/,
31 rcustomProp = /^--/,
32 cssShow = { position: "absolute", visibility: "hidden", display: "block" },
33 cssNormalTransform = {
34 letterSpacing: "0",
35 fontWeight: "400"
38 function setPositiveNumber( elem, value, subtract ) {
40 // Any relative (+/-) values have already been
41 // normalized at this point
42 var matches = rcssNum.exec( value );
43 return matches ?
45 // Guard against undefined "subtract", e.g., when used as in cssHooks
46 Math.max( 0, matches[ 2 ] - ( subtract || 0 ) ) + ( matches[ 3 ] || "px" ) :
47 value;
50 function boxModelAdjustment( elem, dimension, box, isBorderBox, styles, computedVal ) {
51 var i = dimension === "width" ? 1 : 0,
52 extra = 0,
53 delta = 0;
55 // Adjustment may not be necessary
56 if ( box === ( isBorderBox ? "border" : "content" ) ) {
57 return 0;
60 for ( ; i < 4; i += 2 ) {
62 // Both box models exclude margin
63 if ( box === "margin" ) {
64 delta += jQuery.css( elem, box + cssExpand[ i ], true, styles );
67 // If we get here with a content-box, we're seeking "padding" or "border" or "margin"
68 if ( !isBorderBox ) {
70 // Add padding
71 delta += jQuery.css( elem, "padding" + cssExpand[ i ], true, styles );
73 // For "border" or "margin", add border
74 if ( box !== "padding" ) {
75 delta += jQuery.css( elem, "border" + cssExpand[ i ] + "Width", true, styles );
77 // But still keep track of it otherwise
78 } else {
79 extra += jQuery.css( elem, "border" + cssExpand[ i ] + "Width", true, styles );
82 // If we get here with a border-box (content + padding + border), we're seeking "content" or
83 // "padding" or "margin"
84 } else {
86 // For "content", subtract padding
87 if ( box === "content" ) {
88 delta -= jQuery.css( elem, "padding" + cssExpand[ i ], true, styles );
91 // For "content" or "padding", subtract border
92 if ( box !== "margin" ) {
93 delta -= jQuery.css( elem, "border" + cssExpand[ i ] + "Width", true, styles );
98 // Account for positive content-box scroll gutter when requested by providing computedVal
99 if ( !isBorderBox && computedVal >= 0 ) {
101 // offsetWidth/offsetHeight is a rounded sum of content, padding, scroll gutter, and border
102 // Assuming integer scroll gutter, subtract the rest and round down
103 delta += Math.max( 0, Math.ceil(
104 elem[ "offset" + dimension[ 0 ].toUpperCase() + dimension.slice( 1 ) ] -
105 computedVal -
106 delta -
107 extra -
110 // If offsetWidth/offsetHeight is unknown, then we can't determine content-box scroll gutter
111 // Use an explicit zero to avoid NaN (gh-3964)
112 ) ) || 0;
115 return delta;
118 function getWidthOrHeight( elem, dimension, extra ) {
120 // Start with computed style
121 var styles = getStyles( elem ),
123 // To avoid forcing a reflow, only fetch boxSizing if we need it (gh-4322).
124 // Fake content-box until we know it's needed to know the true value.
125 boxSizingNeeded = !support.boxSizingReliable() || extra,
126 isBorderBox = boxSizingNeeded &&
127 jQuery.css( elem, "boxSizing", false, styles ) === "border-box",
128 valueIsBorderBox = isBorderBox,
130 val = curCSS( elem, dimension, styles ),
131 offsetProp = "offset" + dimension[ 0 ].toUpperCase() + dimension.slice( 1 );
133 // Support: Firefox <=54
134 // Return a confounding non-pixel value or feign ignorance, as appropriate.
135 if ( rnumnonpx.test( val ) ) {
136 if ( !extra ) {
137 return val;
139 val = "auto";
143 // Fall back to offsetWidth/offsetHeight when value is "auto"
144 // This happens for inline elements with no explicit setting (gh-3571)
145 // Support: Android <=4.1 - 4.3 only
146 // Also use offsetWidth/offsetHeight for misreported inline dimensions (gh-3602)
147 // Support: IE 9-11 only
148 // Also use offsetWidth/offsetHeight for when box sizing is unreliable
149 // We use getClientRects() to check for hidden/disconnected.
150 // In those cases, the computed value can be trusted to be border-box
151 if ( ( !support.boxSizingReliable() && isBorderBox ||
152 val === "auto" ||
153 !parseFloat( val ) && jQuery.css( elem, "display", false, styles ) === "inline" ) &&
154 elem.getClientRects().length ) {
156 isBorderBox = jQuery.css( elem, "boxSizing", false, styles ) === "border-box";
158 // Where available, offsetWidth/offsetHeight approximate border box dimensions.
159 // Where not available (e.g., SVG), assume unreliable box-sizing and interpret the
160 // retrieved value as a content box dimension.
161 valueIsBorderBox = offsetProp in elem;
162 if ( valueIsBorderBox ) {
163 val = elem[ offsetProp ];
167 // Normalize "" and auto
168 val = parseFloat( val ) || 0;
170 // Adjust for the element's box model
171 return ( val +
172 boxModelAdjustment(
173 elem,
174 dimension,
175 extra || ( isBorderBox ? "border" : "content" ),
176 valueIsBorderBox,
177 styles,
179 // Provide the current computed size to request scroll gutter calculation (gh-3589)
182 ) + "px";
185 jQuery.extend( {
187 // Add in style property hooks for overriding the default
188 // behavior of getting and setting a style property
189 cssHooks: {
190 opacity: {
191 get: function( elem, computed ) {
192 if ( computed ) {
194 // We should always get a number back from opacity
195 var ret = curCSS( elem, "opacity" );
196 return ret === "" ? "1" : ret;
202 // Add in properties whose names you wish to fix before
203 // setting or getting the value
204 cssProps: {},
206 // Get and set the style property on a DOM Node
207 style: function( elem, name, value, extra ) {
209 // Don't set styles on text and comment nodes
210 if ( !elem || elem.nodeType === 3 || elem.nodeType === 8 || !elem.style ) {
211 return;
214 // Make sure that we're working with the right name
215 var ret, type, hooks,
216 origName = cssCamelCase( name ),
217 isCustomProp = rcustomProp.test( name ),
218 style = elem.style;
220 // Make sure that we're working with the right name. We don't
221 // want to query the value if it is a CSS custom property
222 // since they are user-defined.
223 if ( !isCustomProp ) {
224 name = finalPropName( origName );
227 // Gets hook for the prefixed version, then unprefixed version
228 hooks = jQuery.cssHooks[ name ] || jQuery.cssHooks[ origName ];
230 // Check if we're setting a value
231 if ( value !== undefined ) {
232 type = typeof value;
234 // Convert "+=" or "-=" to relative numbers (#7345)
235 if ( type === "string" && ( ret = rcssNum.exec( value ) ) && ret[ 1 ] ) {
236 value = adjustCSS( elem, name, ret );
238 // Fixes bug #9237
239 type = "number";
242 // Make sure that null and NaN values aren't set (#7116)
243 if ( value == null || value !== value ) {
244 return;
247 // If the value is a number, add `px` for certain CSS properties
248 if ( type === "number" ) {
249 value += ret && ret[ 3 ] || ( isAutoPx( origName ) ? "px" : "" );
252 // background-* props affect original clone's values
253 if ( !support.clearCloneStyle && value === "" && name.indexOf( "background" ) === 0 ) {
254 style[ name ] = "inherit";
257 // If a hook was provided, use that value, otherwise just set the specified value
258 if ( !hooks || !( "set" in hooks ) ||
259 ( value = hooks.set( elem, value, extra ) ) !== undefined ) {
261 if ( isCustomProp ) {
262 style.setProperty( name, value );
263 } else {
264 style[ name ] = value;
268 } else {
270 // If a hook was provided get the non-computed value from there
271 if ( hooks && "get" in hooks &&
272 ( ret = hooks.get( elem, false, extra ) ) !== undefined ) {
274 return ret;
277 // Otherwise just get the value from the style object
278 return style[ name ];
282 css: function( elem, name, extra, styles ) {
283 var val, num, hooks,
284 origName = cssCamelCase( name ),
285 isCustomProp = rcustomProp.test( name );
287 // Make sure that we're working with the right name. We don't
288 // want to modify the value if it is a CSS custom property
289 // since they are user-defined.
290 if ( !isCustomProp ) {
291 name = finalPropName( origName );
294 // Try prefixed name followed by the unprefixed name
295 hooks = jQuery.cssHooks[ name ] || jQuery.cssHooks[ origName ];
297 // If a hook was provided get the computed value from there
298 if ( hooks && "get" in hooks ) {
299 val = hooks.get( elem, true, extra );
302 // Otherwise, if a way to get the computed value exists, use that
303 if ( val === undefined ) {
304 val = curCSS( elem, name, styles );
307 // Convert "normal" to computed value
308 if ( val === "normal" && name in cssNormalTransform ) {
309 val = cssNormalTransform[ name ];
312 // Make numeric if forced or a qualifier was provided and val looks numeric
313 if ( extra === "" || extra ) {
314 num = parseFloat( val );
315 return extra === true || isFinite( num ) ? num || 0 : val;
318 return val;
320 } );
322 jQuery.each( [ "height", "width" ], function( i, dimension ) {
323 jQuery.cssHooks[ dimension ] = {
324 get: function( elem, computed, extra ) {
325 if ( computed ) {
327 // Certain elements can have dimension info if we invisibly show them
328 // but it must have a current display style that would benefit
329 return rdisplayswap.test( jQuery.css( elem, "display" ) ) &&
331 // Support: Safari 8+
332 // Table columns in Safari have non-zero offsetWidth & zero
333 // getBoundingClientRect().width unless display is changed.
334 // Support: IE <=11 only
335 // Running getBoundingClientRect on a disconnected node
336 // in IE throws an error.
337 ( !elem.getClientRects().length || !elem.getBoundingClientRect().width ) ?
338 swap( elem, cssShow, function() {
339 return getWidthOrHeight( elem, dimension, extra );
340 } ) :
341 getWidthOrHeight( elem, dimension, extra );
345 set: function( elem, value, extra ) {
346 var matches,
347 styles = getStyles( elem ),
349 // Only read styles.position if the test has a chance to fail
350 // to avoid forcing a reflow.
351 scrollboxSizeBuggy = !support.scrollboxSize() &&
352 styles.position === "absolute",
354 // To avoid forcing a reflow, only fetch boxSizing if we need it (gh-3991)
355 boxSizingNeeded = scrollboxSizeBuggy || extra,
356 isBorderBox = boxSizingNeeded &&
357 jQuery.css( elem, "boxSizing", false, styles ) === "border-box",
358 subtract = extra ?
359 boxModelAdjustment(
360 elem,
361 dimension,
362 extra,
363 isBorderBox,
364 styles
368 // Account for unreliable border-box dimensions by comparing offset* to computed and
369 // faking a content-box to get border and padding (gh-3699)
370 if ( isBorderBox && scrollboxSizeBuggy ) {
371 subtract -= Math.ceil(
372 elem[ "offset" + dimension[ 0 ].toUpperCase() + dimension.slice( 1 ) ] -
373 parseFloat( styles[ dimension ] ) -
374 boxModelAdjustment( elem, dimension, "border", false, styles ) -
379 // Convert to pixels if value adjustment is needed
380 if ( subtract && ( matches = rcssNum.exec( value ) ) &&
381 ( matches[ 3 ] || "px" ) !== "px" ) {
383 elem.style[ dimension ] = value;
384 value = jQuery.css( elem, dimension );
387 return setPositiveNumber( elem, value, subtract );
390 } );
392 jQuery.cssHooks.marginLeft = addGetHookIf( support.reliableMarginLeft,
393 function( elem, computed ) {
394 if ( computed ) {
395 return ( parseFloat( curCSS( elem, "marginLeft" ) ) ||
396 elem.getBoundingClientRect().left -
397 swap( elem, { marginLeft: 0 }, function() {
398 return elem.getBoundingClientRect().left;
400 ) + "px";
405 // These hooks are used by animate to expand properties
406 jQuery.each( {
407 margin: "",
408 padding: "",
409 border: "Width"
410 }, function( prefix, suffix ) {
411 jQuery.cssHooks[ prefix + suffix ] = {
412 expand: function( value ) {
413 var i = 0,
414 expanded = {},
416 // Assumes a single number if not a string
417 parts = typeof value === "string" ? value.split( " " ) : [ value ];
419 for ( ; i < 4; i++ ) {
420 expanded[ prefix + cssExpand[ i ] + suffix ] =
421 parts[ i ] || parts[ i - 2 ] || parts[ 0 ];
424 return expanded;
428 if ( prefix !== "margin" ) {
429 jQuery.cssHooks[ prefix + suffix ].set = setPositiveNumber;
431 } );
433 jQuery.fn.extend( {
434 css: function( name, value ) {
435 return access( this, function( elem, name, value ) {
436 var styles, len,
437 map = {},
438 i = 0;
440 if ( Array.isArray( name ) ) {
441 styles = getStyles( elem );
442 len = name.length;
444 for ( ; i < len; i++ ) {
445 map[ name[ i ] ] = jQuery.css( elem, name[ i ], false, styles );
448 return map;
451 return value !== undefined ?
452 jQuery.style( elem, name, value ) :
453 jQuery.css( elem, name );
454 }, name, value, arguments.length > 1 );
456 } );
458 return jQuery;
459 } );