Skip to content

Commit c934995

Browse files
Resizable: Fix content shrink on resize
Make resizable elements not shrink on resize when they have scrollbars and "box-sizing: content-box". Fixes: gh-2277 Closes gh-2281
1 parent d564731 commit c934995

File tree

3 files changed

+191
-17
lines changed

3 files changed

+191
-17
lines changed

tests/unit/resizable/core.js

+68
Original file line numberDiff line numberDiff line change
@@ -244,4 +244,72 @@ QUnit.test( "nested resizable", function( assert ) {
244244
outer.remove();
245245
} );
246246

247+
QUnit.test( "Resizable with scrollbars and box-sizing: border-box", function( assert ) {
248+
assert.expect( 4 );
249+
testResizableWithBoxSizing( assert, {
250+
isBorderBox: true,
251+
applyScaleTransform: false
252+
} );
253+
} );
254+
255+
QUnit.test( "Resizable with scrollbars and box-sizing: content-box", function( assert ) {
256+
assert.expect( 4 );
257+
testResizableWithBoxSizing( assert, {
258+
isBorderBox: false,
259+
applyScaleTransform: false
260+
} );
261+
} );
262+
263+
QUnit.test( "Resizable with scrollbars, a transform and box-sizing: border-box", function( assert ) {
264+
assert.expect( 4 );
265+
testResizableWithBoxSizing( assert, {
266+
isBorderBox: true,
267+
applyScaleTransform: true
268+
} );
269+
} );
270+
271+
QUnit.test( "Resizable with scrollbars, a transform and box-sizing: content-box", function( assert ) {
272+
assert.expect( 4 );
273+
testResizableWithBoxSizing( assert, {
274+
isBorderBox: false,
275+
applyScaleTransform: true
276+
} );
277+
} );
278+
279+
function testResizableWithBoxSizing( assert, options ) {
280+
var widthBefore, heightBefore,
281+
cssBoxSizing = options.isBorderBox ? "border-box" : "content-box",
282+
cssTransform = options.applyScaleTransform ? "scale(1.5)" : "",
283+
elementContent = $( "<div>" )
284+
.css( {
285+
width: "200px",
286+
height: "200px",
287+
padding: "10px",
288+
border: "5px",
289+
borderStyle: "solid",
290+
margin: "20px"
291+
} )
292+
.appendTo( "#resizable1" ),
293+
element = $( "#resizable1" ).css( { overflow: "auto", transform: cssTransform } ).resizable(),
294+
handle = ".ui-resizable-se";
295+
296+
$( "<style> * { box-sizing: " + cssBoxSizing + "; } </style>" ).appendTo( "#qunit-fixture" );
297+
298+
// In some browsers scrollbar may change element size (when "box-sizing: content-box")
299+
widthBefore = element.innerWidth();
300+
heightBefore = element.innerHeight();
301+
302+
// Both scrollbars
303+
testHelper.drag( handle, 10, 10 );
304+
assert.equal( parseFloat( element.innerWidth() ), widthBefore + 10, "element width (both scrollbars)" );
305+
assert.equal( parseFloat( element.innerHeight() ), heightBefore + 10, "element height (both scrollbars)" );
306+
307+
// Single (vertical) scrollbar.
308+
elementContent.css( "width", "50px" );
309+
310+
testHelper.drag( handle, 10, 10 );
311+
assert.equal( parseFloat( element.innerWidth() ), widthBefore + 20, "element width (only vertical scrollbar)" );
312+
assert.equal( parseFloat( element.innerHeight() ), heightBefore + 20, "element height (only vertical scrollbar)" );
313+
}
314+
247315
} );

tests/unit/resizable/options.js

+55-7
Original file line numberDiff line numberDiff line change
@@ -542,21 +542,22 @@ QUnit.test( "alsoResize + multiple selection", function( assert ) {
542542
QUnit.test( "alsoResize with box-sizing: border-box", function( assert ) {
543543
assert.expect( 4 );
544544

545+
$( "<style> * { box-sizing: border-box; } </style>" ).appendTo( "#qunit-fixture" );
546+
545547
var other = $( "<div>" )
546548
.css( {
547-
width: 50,
548-
height: 50,
549-
padding: 10,
550-
border: 5
549+
width: "50px",
550+
height: "50px",
551+
padding: "10px",
552+
border: "5px",
553+
borderStyle: "solid"
551554
} )
552-
.appendTo( "body" ),
555+
.appendTo( "#qunit-fixture" ),
553556
element = $( "#resizable1" ).resizable( {
554557
alsoResize: other
555558
} ),
556559
handle = ".ui-resizable-se";
557560

558-
$( "*" ).css( "box-sizing", "border-box" );
559-
560561
testHelper.drag( handle, 80, 80 );
561562

562563
assert.equal( element.width(), 180, "resizable width" );
@@ -565,4 +566,51 @@ QUnit.test( "alsoResize with box-sizing: border-box", function( assert ) {
565566
assert.equal( parseFloat( other.css( "height" ) ), 130, "alsoResize height" );
566567
} );
567568

569+
QUnit.test( "alsoResize with scrollbars and box-sizing: border-box", function( assert ) {
570+
assert.expect( 4 );
571+
testAlsoResizeWithBoxSizing( assert, {
572+
isBorderBox: true
573+
} );
574+
} );
575+
576+
QUnit.test( "alsoResize with scrollbars and box-sizing: content-box", function( assert ) {
577+
assert.expect( 4 );
578+
testAlsoResizeWithBoxSizing( assert, {
579+
isBorderBox: false
580+
} );
581+
} );
582+
583+
function testAlsoResizeWithBoxSizing( assert, options ) {
584+
var widthBefore, heightBefore,
585+
cssBoxSizing = options.isBorderBox ? "border-box" : "content-box",
586+
other = $( "<div>" )
587+
.css( {
588+
width: "150px",
589+
height: "150px",
590+
padding: "10px",
591+
border: "5px",
592+
borderStyle: "solid",
593+
margin: "20px",
594+
overflow: "scroll"
595+
} )
596+
.appendTo( "#qunit-fixture" ),
597+
element = $( "#resizable1" ).resizable( {
598+
alsoResize: other
599+
} ),
600+
handle = ".ui-resizable-se";
601+
602+
$( "<style> * { box-sizing: " + cssBoxSizing + "; } </style>" ).appendTo( "#qunit-fixture" );
603+
604+
// In some browsers scrollbar may change element computed size.
605+
widthBefore = other.innerWidth();
606+
heightBefore = other.innerHeight();
607+
608+
testHelper.drag( handle, 80, 80 );
609+
610+
assert.equal( element.width(), 180, "resizable width" );
611+
assert.equal( parseFloat( other.innerWidth() ), widthBefore + 80, "alsoResize width" );
612+
assert.equal( element.height(), 180, "resizable height" );
613+
assert.equal( parseFloat( other.innerHeight() ), heightBefore + 80, "alsoResize height" );
614+
}
615+
568616
} );

ui/widgets/resizable.js

+68-10
Original file line numberDiff line numberDiff line change
@@ -80,12 +80,18 @@ $.widget( "ui.resizable", $.ui.mouse, {
8080

8181
_hasScroll: function( el, a ) {
8282

83-
if ( $( el ).css( "overflow" ) === "hidden" ) {
83+
var scroll,
84+
has = false,
85+
overflow = $( el ).css( "overflow" );
86+
87+
if ( overflow === "hidden" ) {
8488
return false;
8589
}
90+
if ( overflow === "scroll" ) {
91+
return true;
92+
}
8693

87-
var scroll = ( a && a === "left" ) ? "scrollLeft" : "scrollTop",
88-
has = false;
94+
scroll = ( a && a === "left" ) ? "scrollLeft" : "scrollTop";
8995

9096
if ( el[ scroll ] > 0 ) {
9197
return true;
@@ -362,7 +368,7 @@ $.widget( "ui.resizable", $.ui.mouse, {
362368

363369
_mouseStart: function( event ) {
364370

365-
var curleft, curtop, cursor,
371+
var curleft, curtop, cursor, calculatedSize,
366372
o = this.options,
367373
el = this.element;
368374

@@ -381,20 +387,24 @@ $.widget( "ui.resizable", $.ui.mouse, {
381387
this.offset = this.helper.offset();
382388
this.position = { left: curleft, top: curtop };
383389

390+
if ( !this._helper ) {
391+
calculatedSize = this._calculateAdjustedElementDimensions( el );
392+
}
393+
384394
this.size = this._helper ? {
385395
width: this.helper.width(),
386396
height: this.helper.height()
387397
} : {
388-
width: el.width(),
389-
height: el.height()
398+
width: calculatedSize.width,
399+
height: calculatedSize.height
390400
};
391401

392402
this.originalSize = this._helper ? {
393403
width: el.outerWidth(),
394404
height: el.outerHeight()
395405
} : {
396-
width: el.width(),
397-
height: el.height()
406+
width: calculatedSize.width,
407+
height: calculatedSize.height
398408
};
399409

400410
this.sizeDiff = {
@@ -690,6 +700,52 @@ $.widget( "ui.resizable", $.ui.mouse, {
690700
};
691701
},
692702

703+
_calculateAdjustedElementDimensions: function( element ) {
704+
var elWidth, elHeight, paddingBorder,
705+
ce = element.get( 0 );
706+
707+
if ( element.css( "box-sizing" ) !== "content-box" ||
708+
( !this._hasScroll( ce ) && !this._hasScroll( ce, "left" ) ) ) {
709+
return {
710+
height: parseFloat( element.css( "height" ) ),
711+
width: parseFloat( element.css( "width" ) )
712+
};
713+
}
714+
715+
// Check if CSS inline styles are set and use those (usually from previous resizes)
716+
elWidth = parseFloat( ce.style.width );
717+
elHeight = parseFloat( ce.style.height );
718+
719+
paddingBorder = this._getPaddingPlusBorderDimensions( element );
720+
elWidth = isNaN( elWidth ) ?
721+
this._getElementTheoreticalSize( element, paddingBorder, "width" ) :
722+
elWidth;
723+
elHeight = isNaN( elHeight ) ?
724+
this._getElementTheoreticalSize( element, paddingBorder, "height" ) :
725+
elHeight;
726+
727+
return {
728+
height: elHeight,
729+
width: elWidth
730+
};
731+
},
732+
733+
_getElementTheoreticalSize: function( element, extraSize, dimension ) {
734+
735+
// offsetWidth/offsetHeight is a rounded sum of content, padding, scroll gutter, and border
736+
var size = Math.max( 0, Math.ceil(
737+
element.get( 0 )[ "offset" + dimension[ 0 ].toUpperCase() + dimension.slice( 1 ) ] -
738+
extraSize[ dimension ] -
739+
0.5
740+
741+
// If offsetWidth/offsetHeight is unknown, then we can't determine theoretical size.
742+
// Use an explicit zero to avoid NaN.
743+
// See https://github.com/jquery/jquery/issues/3964
744+
) ) || 0;
745+
746+
return size;
747+
},
748+
693749
_proportionallyResize: function() {
694750

695751
if ( !this._proportionallyResizeElements.length ) {
@@ -1044,9 +1100,11 @@ $.ui.plugin.add( "resizable", "alsoResize", {
10441100
o = that.options;
10451101

10461102
$( o.alsoResize ).each( function() {
1047-
var el = $( this );
1103+
var el = $( this ),
1104+
elSize = that._calculateAdjustedElementDimensions( el );
1105+
10481106
el.data( "ui-resizable-alsoresize", {
1049-
width: parseFloat( el.css( "width" ) ), height: parseFloat( el.css( "height" ) ),
1107+
width: elSize.width, height: elSize.height,
10501108
left: parseFloat( el.css( "left" ) ), top: parseFloat( el.css( "top" ) )
10511109
} );
10521110
} );

0 commit comments

Comments
 (0)