Skip to content

Commit 6fb2cef

Browse files
mr21gibson042
authored andcommitted
CSS: Support relative adjustment in any applicable unit
Fixes gh-1711 Closes gh-2011 (cherry picked from commit 9b03f6d) Conflicts: src/css.js src/effects.js
1 parent 9edd95f commit 6fb2cef

File tree

5 files changed

+135
-61
lines changed

5 files changed

+135
-61
lines changed

src/css.js

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,12 @@ define([
33
"./var/pnum",
44
"./core/access",
55
"./css/var/rmargin",
6+
"./var/rcssNum",
67
"./css/var/rnumnonpx",
78
"./css/var/cssExpand",
89
"./css/var/isHidden",
910
"./css/curCSS",
11+
"./css/adjustCSS",
1012
"./css/defaultDisplay",
1113
"./css/addGetHookIf",
1214
"./css/support",
@@ -15,8 +17,8 @@ define([
1517
"./css/swap",
1618
"./core/ready",
1719
"./selector" // contains
18-
], function( jQuery, pnum, access, rmargin, rnumnonpx, cssExpand, isHidden,
19-
curCSS, defaultDisplay, addGetHookIf, support ) {
20+
], function( jQuery, pnum, access, rmargin, rcssNum, rnumnonpx, cssExpand, isHidden,
21+
curCSS, adjustCSS, defaultDisplay, addGetHookIf, support ) {
2022

2123
var
2224
// BuildExclude
@@ -30,7 +32,6 @@ var
3032
// https://developer.mozilla.org/en-US/docs/CSS/display
3133
rdisplayswap = /^(none|table(?!-c[ea]).+)/,
3234
rnumsplit = new RegExp( "^(" + pnum + ")(.*)$", "i" ),
33-
rrelNum = new RegExp( "^([+-])=(" + pnum + ")", "i" ),
3435

3536
cssShow = { position: "absolute", visibility: "hidden", display: "block" },
3637
cssNormalTransform = {
@@ -273,9 +274,9 @@ jQuery.extend({
273274
if ( value !== undefined ) {
274275
type = typeof value;
275276

276-
// convert relative number strings (+= or -=) to relative numbers. #7345
277-
if ( type === "string" && (ret = rrelNum.exec( value )) ) {
278-
value = ( ret[1] + 1 ) * ret[2] + parseFloat( jQuery.css( elem, name ) );
277+
// Convert "+=" or "-=" to relative numbers (#7345)
278+
if ( type === "string" && (ret = rcssNum.exec( value )) && ret[ 1 ] ) {
279+
value = adjustCSS( elem, name, ret );
279280
// Fixes bug #9237
280281
type = "number";
281282
}
@@ -285,9 +286,9 @@ jQuery.extend({
285286
return;
286287
}
287288

288-
// If a number was passed in, add 'px' (except for certain CSS properties)
289-
if ( type === "number" && !jQuery.cssNumber[ origName ] ) {
290-
value += "px";
289+
// If a number was passed in, add the unit (except for certain CSS properties)
290+
if ( type === "number" ) {
291+
value += ret && ret[ 3 ] || ( jQuery.cssNumber[ origName ] ? "" : "px" );
291292
}
292293

293294
// Fixes #8908, it can be done more correctly by specifing setters in cssHooks,

src/css/adjustCSS.js

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
define([
2+
"../core",
3+
"../var/rcssNum"
4+
], function( jQuery, rcssNum ) {
5+
6+
function adjustCSS( elem, prop, valueParts, tween ) {
7+
var adjusted,
8+
scale = 1,
9+
maxIterations = 20,
10+
currentValue = tween ?
11+
function() { return tween.cur(); } :
12+
function() { return jQuery.css( elem, prop, "" ); },
13+
initial = currentValue(),
14+
unit = valueParts && valueParts[ 3 ] || ( jQuery.cssNumber[ prop ] ? "" : "px" ),
15+
// Starting value computation is required for potential unit mismatches
16+
initialInUnit = ( jQuery.cssNumber[ prop ] || unit !== "px" && +initial ) &&
17+
rcssNum.exec( jQuery.css( elem, prop ) );
18+
19+
if ( initialInUnit && initialInUnit[ 3 ] !== unit ) {
20+
// Trust units reported by jQuery.css
21+
unit = unit || initialInUnit[ 3 ];
22+
23+
// Make sure we update the tween properties later on
24+
valueParts = valueParts || [];
25+
26+
// Iteratively approximate from a nonzero starting point
27+
initialInUnit = +initial || 1;
28+
29+
do {
30+
// If previous iteration zeroed out, double until we get *something*.
31+
// Use string for doubling so we don't accidentally see scale as unchanged below
32+
scale = scale || ".5";
33+
34+
// Adjust and apply
35+
initialInUnit = initialInUnit / scale;
36+
jQuery.style( elem, prop, initialInUnit + unit );
37+
38+
// Update scale, tolerating zero or NaN from tween.cur()
39+
// Break the loop if scale is unchanged or perfect, or if we've just had enough.
40+
} while (
41+
scale !== (scale = currentValue() / initial) && scale !== 1 && --maxIterations
42+
);
43+
}
44+
45+
if ( valueParts ) {
46+
initialInUnit = +initialInUnit || +initial || 0;
47+
// Apply relative offset (+=/-=) if specified
48+
adjusted = valueParts[ 1 ] ?
49+
initialInUnit + ( valueParts[ 1 ] + 1 ) * valueParts[ 2 ] :
50+
+valueParts[ 2 ];
51+
if ( tween ) {
52+
tween.unit = unit;
53+
tween.start = initialInUnit;
54+
tween.end = adjusted;
55+
}
56+
}
57+
return adjusted;
58+
}
59+
60+
return adjustCSS;
61+
});

src/effects.js

Lines changed: 5 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
define([
22
"./core",
3-
"./var/pnum",
3+
"./var/rcssNum",
44
"./css/var/cssExpand",
55
"./css/var/isHidden",
6+
"./css/adjustCSS",
67
"./css/defaultDisplay",
78

89
"./core/init",
@@ -11,65 +12,17 @@ define([
1112
"./css",
1213
"./deferred",
1314
"./traversing"
14-
], function( jQuery, pnum, cssExpand, isHidden, defaultDisplay ) {
15+
], function( jQuery, rcssNum, cssExpand, isHidden, adjustCSS, defaultDisplay ) {
1516

1617
var
1718
fxNow, timerId,
1819
rfxtypes = /^(?:toggle|show|hide)$/,
19-
rfxnum = new RegExp( "^(?:([+-])=|)(" + pnum + ")([a-z%]*)$", "i" ),
2020
rrun = /queueHooks$/,
2121
animationPrefilters = [ defaultPrefilter ],
2222
tweeners = {
2323
"*": [ function( prop, value ) {
24-
var tween = this.createTween( prop, value ),
25-
target = tween.cur(),
26-
parts = rfxnum.exec( value ),
27-
unit = parts && parts[ 3 ] || ( jQuery.cssNumber[ prop ] ? "" : "px" ),
28-
29-
// Starting value computation is required for potential unit mismatches
30-
start = ( jQuery.cssNumber[ prop ] || unit !== "px" && +target ) &&
31-
rfxnum.exec( jQuery.css( tween.elem, prop ) ),
32-
scale = 1,
33-
maxIterations = 20;
34-
35-
if ( start && start[ 3 ] !== unit ) {
36-
// Trust units reported by jQuery.css
37-
unit = unit || start[ 3 ];
38-
39-
// Make sure we update the tween properties later on
40-
parts = parts || [];
41-
42-
// Iteratively approximate from a nonzero starting point
43-
start = +target || 1;
44-
45-
do {
46-
// If previous iteration zeroed out, double until we get *something*
47-
// Use a string for doubling factor so we don't accidentally see scale
48-
// as unchanged below
49-
scale = scale || ".5";
50-
51-
// Adjust and apply
52-
start = start / scale;
53-
jQuery.style( tween.elem, prop, start + unit );
54-
55-
// Update scale, tolerating zero or NaN from tween.cur()
56-
// And breaking the loop if scale is unchanged or perfect,
57-
// or if we've just had enough
58-
} while (
59-
scale !== (scale = tween.cur() / target) && scale !== 1 && --maxIterations
60-
);
61-
}
62-
63-
// Update tween properties
64-
if ( parts ) {
65-
start = tween.start = +start || +target || 0;
66-
tween.unit = unit;
67-
// If a +=/-= token was provided, we're doing a relative animation
68-
tween.end = parts[ 1 ] ?
69-
start + ( parts[ 1 ] + 1 ) * parts[ 2 ] :
70-
+parts[ 2 ];
71-
}
72-
24+
var tween = this.createTween( prop, value );
25+
adjustCSS( tween.elem, prop, rcssNum.exec( value ), tween );
7326
return tween;
7427
} ]
7528
};

src/var/rcssNum.js

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
define([
2+
"../var/pnum"
3+
], function( pnum ) {
4+
5+
return new RegExp( "^(?:([+-])=|)(" + pnum + ")([a-z%]*)$", "i" );
6+
7+
});

test/unit/css.js

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -217,6 +217,58 @@ test( "css() explicit and relative values", 29, function() {
217217
equal( $elem.css("opacity"), "1", "'+=0.5' on opacity (params)" );
218218
});
219219

220+
test( "css() non-px relative values (gh-1711)", 17, function() {
221+
var cssCurrent,
222+
units = {},
223+
$child = jQuery( "#nothiddendivchild" ),
224+
add = function( prop, val, unit ) {
225+
var str = ( val < 0 ? "-=" : "+=" ) + Math.abs( val ) + unit;
226+
$child.css( prop, str );
227+
equal(
228+
Math.round( parseFloat( $child.css( prop ) ) ),
229+
Math.round( cssCurrent += val * units[ prop ][ unit ] ),
230+
prop + ": '" + str + "'"
231+
);
232+
},
233+
getUnits = function( prop ) {
234+
units[ prop ] = {
235+
"px": 1,
236+
"em": parseFloat( $child.css( prop, "100em" ).css( prop ) ) / 100,
237+
"pt": parseFloat( $child.css( prop, "100pt" ).css( prop ) ) / 100,
238+
"pc": parseFloat( $child.css( prop, "100pc" ).css( prop ) ) / 100,
239+
"cm": parseFloat( $child.css( prop, "100cm" ).css( prop ) ) / 100,
240+
"mm": parseFloat( $child.css( prop, "100mm" ).css( prop ) ) / 100,
241+
"%" : parseFloat( $child.css( prop, "100%" ).css( prop ) ) / 100
242+
};
243+
};
244+
245+
jQuery( "#nothiddendiv" ).css({ height: 1, padding: 0, width: 400 });
246+
$child.css({ height: 1, padding: 0 });
247+
248+
getUnits( "width" );
249+
cssCurrent = parseFloat( $child.css( "width", "50%" ).css( "width" ) );
250+
add( "width", 25, "%" );
251+
add( "width", -50, "%" );
252+
add( "width", 10, "em" );
253+
add( "width", 10, "pt" );
254+
add( "width", -2.3, "pt" );
255+
add( "width", 5, "pc" );
256+
add( "width", -5, "em" );
257+
add( "width", +2, "cm" );
258+
add( "width", -15, "mm" );
259+
add( "width", 21, "px" );
260+
261+
getUnits( "lineHeight" );
262+
cssCurrent = parseFloat( $child.css( "lineHeight", "1em" ).css( "lineHeight" ) );
263+
add( "lineHeight", 2, "em" );
264+
add( "lineHeight", -10, "px" );
265+
add( "lineHeight", 20, "pt" );
266+
add( "lineHeight", 30, "pc" );
267+
add( "lineHeight", 1, "cm" );
268+
add( "lineHeight", -20, "mm" );
269+
add( "lineHeight", 50, "%" );
270+
});
271+
220272
test("css(String, Object)", function() {
221273
expect( 19 );
222274
var j, div, display, ret, success;

0 commit comments

Comments
 (0)