Skip to content

Commit e8b4646

Browse files
mr21markelog
authored andcommitted
CSS: Support relative adjustment in any applicable unit
Fixes gh-1711 Closes gh-2011
1 parent b5687f6 commit e8b4646

File tree

5 files changed

+135
-58
lines changed

5 files changed

+135
-58
lines changed

src/css.js

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,13 @@ 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/var/getStyles",
1011
"./css/curCSS",
12+
"./css/adjustCSS",
1113
"./css/defaultDisplay",
1214
"./css/addGetHookIf",
1315
"./css/support",
@@ -17,16 +19,15 @@ define([
1719
"./css/swap",
1820
"./core/ready",
1921
"./selector" // contains
20-
], function( jQuery, pnum, access, rmargin, rnumnonpx, cssExpand, isHidden,
21-
getStyles, curCSS, defaultDisplay, addGetHookIf, support, dataPriv ) {
22+
], function( jQuery, pnum, access, rmargin, rcssNum, rnumnonpx, cssExpand, isHidden,
23+
getStyles, curCSS, adjustCSS, defaultDisplay, addGetHookIf, support, dataPriv ) {
2224

2325
var
2426
// Swappable if display is none or starts with table
2527
// except "table", "table-cell", or "table-caption"
2628
// See here for display values: https://developer.mozilla.org/en-US/docs/CSS/display
2729
rdisplayswap = /^(none|table(?!-c[ea]).+)/,
2830
rnumsplit = new RegExp( "^(" + pnum + ")(.*)$", "i" ),
29-
rrelNum = new RegExp( "^([+-])=(" + pnum + ")", "i" ),
3031

3132
cssShow = { position: "absolute", visibility: "hidden", display: "block" },
3233
cssNormalTransform = {
@@ -272,8 +273,8 @@ jQuery.extend({
272273
type = typeof value;
273274

274275
// Convert "+=" or "-=" to relative numbers (#7345)
275-
if ( type === "string" && (ret = rrelNum.exec( value )) ) {
276-
value = ( ret[1] + 1 ) * ret[2] + parseFloat( jQuery.css( elem, name ) );
276+
if ( type === "string" && (ret = rcssNum.exec( value )) && ret[ 1 ] ) {
277+
value = adjustCSS( elem, name, ret );
277278
// Fixes bug #9237
278279
type = "number";
279280
}
@@ -283,9 +284,9 @@ jQuery.extend({
283284
return;
284285
}
285286

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

291292
// Support: IE9-11+

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: 6 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
define([
22
"./core",
33
"./var/document",
4-
"./var/pnum",
4+
"./var/rcssNum",
55
"./css/var/cssExpand",
66
"./css/var/isHidden",
7+
"./css/adjustCSS",
78
"./css/defaultDisplay",
89
"./data/var/dataPriv",
910

@@ -13,63 +14,18 @@ define([
1314
"./css",
1415
"./deferred",
1516
"./traversing"
16-
], function( jQuery, document, pnum, cssExpand, isHidden, defaultDisplay, dataPriv ) {
17+
], function( jQuery, document, rcssNum, cssExpand,
18+
isHidden, adjustCSS, defaultDisplay, dataPriv ) {
1719

1820
var
1921
fxNow, timerId,
2022
rfxtypes = /^(?:toggle|show|hide)$/,
21-
rfxnum = new RegExp( "^(?:([+-])=|)(" + pnum + ")([a-z%]*)$", "i" ),
2223
rrun = /queueHooks$/,
2324
animationPrefilters = [ defaultPrefilter ],
2425
tweeners = {
2526
"*": [ function( prop, value ) {
26-
var tween = this.createTween( prop, value ),
27-
target = tween.cur(),
28-
parts = rfxnum.exec( value ),
29-
unit = parts && parts[ 3 ] || ( jQuery.cssNumber[ prop ] ? "" : "px" ),
30-
31-
// Starting value computation is required for potential unit mismatches
32-
start = ( jQuery.cssNumber[ prop ] || unit !== "px" && +target ) &&
33-
rfxnum.exec( jQuery.css( tween.elem, prop ) ),
34-
scale = 1,
35-
maxIterations = 20;
36-
37-
if ( start && start[ 3 ] !== unit ) {
38-
// Trust units reported by jQuery.css
39-
unit = unit || start[ 3 ];
40-
41-
// Make sure we update the tween properties later on
42-
parts = parts || [];
43-
44-
// Iteratively approximate from a nonzero starting point
45-
start = +target || 1;
46-
47-
do {
48-
// If previous iteration zeroed out, double until we get *something*.
49-
// Use string for doubling so we don't accidentally see scale as unchanged below
50-
scale = scale || ".5";
51-
52-
// Adjust and apply
53-
start = start / scale;
54-
jQuery.style( tween.elem, prop, start + unit );
55-
56-
// Update scale, tolerating zero or NaN from tween.cur(),
57-
// break the loop if scale is unchanged or perfect, 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-
27+
var tween = this.createTween( prop, value );
28+
adjustCSS( tween.elem, prop, rcssNum.exec( value ), tween );
7329
return tween;
7430
} ]
7531
};

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
@@ -203,6 +203,58 @@ test( "css() explicit and relative values", 29, function() {
203203
equal( $elem.css("opacity"), "1", "'+=0.5' on opacity (params)" );
204204
});
205205

206+
test( "css() non-px relative values (gh-1711)", 17, function() {
207+
var cssCurrent,
208+
units = {},
209+
$child = jQuery( "#nothiddendivchild" ),
210+
add = function( prop, val, unit ) {
211+
var str = ( val < 0 ? "-=" : "+=" ) + Math.abs( val ) + unit;
212+
$child.css( prop, str );
213+
equal(
214+
Math.round( parseFloat( $child.css( prop ) ) ),
215+
Math.round( cssCurrent += val * units[ prop ][ unit ] ),
216+
prop + ": '" + str + "'"
217+
);
218+
},
219+
getUnits = function( prop ) {
220+
units[ prop ] = {
221+
"px": 1,
222+
"em": parseFloat( $child.css( prop, "100em" ).css( prop ) ) / 100,
223+
"pt": parseFloat( $child.css( prop, "100pt" ).css( prop ) ) / 100,
224+
"pc": parseFloat( $child.css( prop, "100pc" ).css( prop ) ) / 100,
225+
"cm": parseFloat( $child.css( prop, "100cm" ).css( prop ) ) / 100,
226+
"mm": parseFloat( $child.css( prop, "100mm" ).css( prop ) ) / 100,
227+
"%" : parseFloat( $child.css( prop, "100%" ).css( prop ) ) / 100
228+
};
229+
};
230+
231+
jQuery( "#nothiddendiv" ).css({ height: 1, padding: 0, width: 400 });
232+
$child.css({ height: 1, padding: 0 });
233+
234+
getUnits( "width" );
235+
cssCurrent = parseFloat( $child.css( "width", "50%" ).css( "width" ) );
236+
add( "width", 25, "%" );
237+
add( "width", -50, "%" );
238+
add( "width", 10, "em" );
239+
add( "width", 10, "pt" );
240+
add( "width", -2.3, "pt" );
241+
add( "width", 5, "pc" );
242+
add( "width", -5, "em" );
243+
add( "width", +2, "cm" );
244+
add( "width", -15, "mm" );
245+
add( "width", 21, "px" );
246+
247+
getUnits( "lineHeight" );
248+
cssCurrent = parseFloat( $child.css( "lineHeight", "1em" ).css( "lineHeight" ) );
249+
add( "lineHeight", 2, "em" );
250+
add( "lineHeight", -10, "px" );
251+
add( "lineHeight", 20, "pt" );
252+
add( "lineHeight", 30, "pc" );
253+
add( "lineHeight", 1, "cm" );
254+
add( "lineHeight", -20, "mm" );
255+
add( "lineHeight", 50, "%" );
256+
});
257+
206258
test("css(String, Object)", function() {
207259
expect( 19 );
208260
var j, div, display, ret, success;

0 commit comments

Comments
 (0)