Event: Fix handling of multiple async focus events
[jquery.git] / src / css.js
blobd6354a3103d75192ded85439e53a710b08a5845a
1 define( [
2         "./core",
3         "./core/access",
4         "./core/camelCase",
5         "./var/rcssNum",
6         "./css/var/rnumnonpx",
7         "./css/var/cssExpand",
8         "./css/isAutoPx",
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, camelCase, rcssNum, rnumnonpx, cssExpand, isAutoPx,
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"
36         };
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;
58         }
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 );
65                 }
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 );
80                         }
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 );
89                         }
91                         // For "content" or "padding", subtract border
92                         if ( box !== "margin" ) {
93                                 delta -= jQuery.css( elem, "border" + cssExpand[ i ] + "Width", true, styles );
94                         }
95                 }
96         }
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 -
108                         0.5
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;
113         }
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;
138                 }
139                 val = "auto";
140         }
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 ];
164                 }
165         }
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)
180                         val
181                 )
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;
197                                 }
198                         }
199                 }
200         },
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;
212                 }
214                 // Make sure that we're working with the right name
215                 var ret, type, hooks,
216                         origName = camelCase( 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 );
225                 }
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";
240                         }
242                         // Make sure that null and NaN values aren't set (#7116)
243                         if ( value == null || value !== value ) {
244                                 return;
245                         }
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" : "" );
250                         }
252                         // background-* props affect original clone's values
253                         if ( !support.clearCloneStyle && value === "" && name.indexOf( "background" ) === 0 ) {
254                                 style[ name ] = "inherit";
255                         }
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;
265                                 }
266                         }
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;
275                         }
277                         // Otherwise just get the value from the style object
278                         return style[ name ];
279                 }
280         },
282         css: function( elem, name, extra, styles ) {
283                 var val, num, hooks,
284                         origName = camelCase( 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 );
292                 }
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 );
300                 }
302                 // Otherwise, if a way to get the computed value exists, use that
303                 if ( val === undefined ) {
304                         val = curCSS( elem, name, styles );
305                 }
307                 // Convert "normal" to computed value
308                 if ( val === "normal" && name in cssNormalTransform ) {
309                         val = cssNormalTransform[ name ];
310                 }
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;
316                 }
318                 return val;
319         }
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 );
342                         }
343                 },
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
365                                         ) :
366                                         0;
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 ) -
375                                         0.5
376                                 );
377                         }
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 );
385                         }
387                         return setPositiveNumber( elem, value, subtract );
388                 }
389         };
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;
399                                         } )
400                                 ) + "px";
401                 }
402         }
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 ];
422                         }
424                         return expanded;
425                 }
426         };
428         if ( prefix !== "margin" ) {
429                 jQuery.cssHooks[ prefix + suffix ].set = setPositiveNumber;
430         }
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 );
446                                 }
448                                 return map;
449                         }
451                         return value !== undefined ?
452                                 jQuery.style( elem, name, value ) :
453                                 jQuery.css( elem, name );
454                 }, name, value, arguments.length > 1 );
455         }
456 } );
458 return jQuery;
459 } );