Skip to content

Commit eca2a56

Browse files
authored
CSS: Fix dimensions of table <col> elements
Changes: 1. Fix measurements of `<col span="2">` elements in Firefox. 2. Fix measurements of all implicitly sized `<col>` elements in Safari. Firefox always reports computed width as if `span` was 1. In Safari, computed width for columns is always 0. Work around both issues by using `offsetWidth`. In IE/Edge, `<col>` computed width is `"auto"` unless `width` is set explicitly via CSS so measurements there remain incorrect. Because of the lack of a proper workaround, we accept this limitation. Fixes jquerygh-5628 Closes jquerygh-5630 Ref jquerygh-5634
1 parent e2fe97b commit eca2a56

File tree

5 files changed

+192
-91
lines changed

5 files changed

+192
-91
lines changed

src/css.js

Lines changed: 17 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -18,13 +18,7 @@ import { support } from "./css/support.js";
1818
import "./core/init.js";
1919
import "./core/ready.js";
2020

21-
var
22-
23-
// Swappable if display is none or starts with table
24-
// except "table", "table-cell", or "table-caption"
25-
// See here for display values: https://developer.mozilla.org/en-US/docs/CSS/display
26-
rdisplayswap = /^(none|table(?!-c[ea]).+)/,
27-
cssShow = { position: "absolute", visibility: "hidden", display: "block" },
21+
var cssShow = { position: "absolute", visibility: "hidden", display: "block" },
2822
cssNormalTransform = {
2923
letterSpacing: "0",
3024
fontWeight: "400"
@@ -137,24 +131,22 @@ function getWidthOrHeight( elem, dimension, extra ) {
137131
}
138132

139133

140-
if ( (
134+
if (
135+
(
136+
137+
// Fall back to offsetWidth/offsetHeight when value is "auto"
138+
// This happens for inline elements with no explicit setting (gh-3571)
139+
val === "auto" ||
141140

142-
// Fall back to offsetWidth/offsetHeight when value is "auto"
143-
// This happens for inline elements with no explicit setting (gh-3571)
144-
val === "auto" ||
141+
// Support: IE 9 - 11+
142+
// Use offsetWidth/offsetHeight for when box sizing is unreliable.
143+
// In those cases, the computed value can be trusted to be border-box.
144+
( isIE && isBorderBox ) ||
145145

146-
// Support: IE 9 - 11+
147-
// Use offsetWidth/offsetHeight for when box sizing is unreliable.
148-
// In those cases, the computed value can be trusted to be border-box.
149-
( isIE && isBorderBox ) ||
146+
( !support.reliableColDimensions() && nodeName( elem, "col" ) ) ||
150147

151-
// Support: IE 10 - 11+
152-
// IE misreports `getComputedStyle` of table rows with width/height
153-
// set in CSS while `offset*` properties report correct values.
154-
// Support: Firefox 70 - 135+
155-
// Firefox includes border widths
156-
// in computed dimensions for table rows. (gh-4529)
157-
( !support.reliableTrDimensions() && nodeName( elem, "tr" ) ) ) &&
148+
( !support.reliableTrDimensions() && nodeName( elem, "tr" ) )
149+
) &&
158150

159151
// Make sure the element is visible & connected
160152
elem.getClientRects().length ) {
@@ -316,17 +308,9 @@ jQuery.each( [ "height", "width" ], function( _i, dimension ) {
316308
get: function( elem, computed, extra ) {
317309
if ( computed ) {
318310

319-
// Certain elements can have dimension info if we invisibly show them
320-
// but it must have a current display style that would benefit
321-
return rdisplayswap.test( jQuery.css( elem, "display" ) ) &&
322-
323-
// Support: Safari <=8 - 12+, Chrome <=73+
324-
// Table columns in WebKit/Blink have non-zero offsetWidth & zero
325-
// getBoundingClientRect().width unless display is changed.
326-
// Support: IE <=11+
327-
// Running getBoundingClientRect on a disconnected node
328-
// in IE throws an error.
329-
( !elem.getClientRects().length || !elem.getBoundingClientRect().width ) ?
311+
// Elements with `display: none` can have dimension info if
312+
// we invisibly show them.
313+
return jQuery.css( elem, "display" ) === "none" ?
330314
swap( elem, cssShow, function() {
331315
return getWidthOrHeight( elem, dimension, extra );
332316
} ) :

src/css/support.js

Lines changed: 84 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -1,62 +1,96 @@
1+
import { jQuery } from "../core.js";
12
import { document } from "../var/document.js";
23
import { documentElement } from "../var/documentElement.js";
34
import { support } from "../var/support.js";
5+
import { isIE } from "../var/isIE.js";
46

5-
( function() {
7+
var reliableTrDimensionsVal, reliableColDimensionsVal,
8+
table = document.createElement( "table" );
69

7-
var reliableTrDimensionsVal,
8-
div = document.createElement( "div" );
10+
// Executing table tests requires only one layout, so they're executed
11+
// at the same time to save the second computation.
12+
function computeTableStyleTests() {
13+
if (
914

10-
// Finish early in limited (non-browser) environments
11-
if ( !div.style ) {
12-
return;
13-
}
15+
// This is a singleton, we need to execute it only once
16+
!table ||
17+
18+
// Finish early in limited (non-browser) environments
19+
!table.style
20+
) {
21+
return;
22+
}
23+
24+
var trStyle,
25+
col = document.createElement( "col" ),
26+
tr = document.createElement( "tr" ),
27+
td = document.createElement( "td" );
1428

15-
// Support: IE 10 - 11+
16-
// IE misreports `getComputedStyle` of table rows with width/height
17-
// set in CSS while `offset*` properties report correct values.
18-
// Support: Firefox 70+
19-
// Only Firefox includes border widths
20-
// in computed dimensions. (gh-4529)
21-
support.reliableTrDimensions = function() {
22-
var table, tr, trStyle;
23-
if ( reliableTrDimensionsVal == null ) {
24-
table = document.createElement( "table" );
25-
tr = document.createElement( "tr" );
26-
27-
table.style.cssText = "position:absolute;left:-11111px;border-collapse:separate";
28-
tr.style.cssText = "box-sizing:content-box;border:1px solid;height:1px";
29-
div.style.height = "9px";
30-
31-
// Support: Android Chrome 86+
32-
// In our bodyBackground.html iframe,
33-
// display for all div elements is set to "inline",
34-
// which causes a problem only in Android Chrome, but
35-
// not consistently across all devices.
36-
// Ensuring the div is `display: block`
37-
// gets around this issue.
38-
div.style.display = "block";
39-
40-
documentElement
41-
.appendChild( table )
42-
.appendChild( tr )
43-
.appendChild( div );
44-
45-
// Don't run until window is visible
46-
if ( table.offsetWidth === 0 ) {
47-
documentElement.removeChild( table );
48-
return;
49-
}
50-
51-
trStyle = window.getComputedStyle( tr );
52-
reliableTrDimensionsVal = ( Math.round( parseFloat( trStyle.height ) ) +
53-
Math.round( parseFloat( trStyle.borderTopWidth ) ) +
54-
Math.round( parseFloat( trStyle.borderBottomWidth ) ) ) === tr.offsetHeight;
29+
table.style.cssText = "position:absolute;left:-11111px;" +
30+
"border-collapse:separate;border-spacing:0";
31+
tr.style.cssText = "box-sizing:content-box;border:1px solid;height:1px";
32+
td.style.cssText = "height:9px;width:9px;padding:0";
5533

34+
col.span = 2;
35+
36+
documentElement
37+
.appendChild( table )
38+
.appendChild( col )
39+
.parentNode
40+
.appendChild( tr )
41+
.appendChild( td )
42+
.parentNode
43+
.appendChild( td.cloneNode( true ) );
44+
45+
// Don't run until window is visible
46+
if ( table.offsetWidth === 0 ) {
5647
documentElement.removeChild( table );
48+
return;
49+
}
50+
51+
trStyle = window.getComputedStyle( tr );
52+
53+
// Support: Firefox 135+
54+
// Firefox always reports computed width as if `span` was 1.
55+
// Support: Safari 18.3+
56+
// In Safari, computed width for columns is always 0.
57+
// In both these browsers, using `offsetWidth` solves the issue.
58+
// Support: IE 11+
59+
// In IE, `<col>` computed width is `"auto"` unless `width` is set
60+
// explicitly via CSS so measurements there remain incorrect. Because of
61+
// the lack of a proper workaround, we accept this limitation, treating
62+
// IE as passing the test.
63+
reliableColDimensionsVal = isIE || Math.round( parseFloat(
64+
window.getComputedStyle( col ).width )
65+
) === 18;
66+
67+
// Support: IE 10 - 11+
68+
// IE misreports `getComputedStyle` of table rows with width/height
69+
// set in CSS while `offset*` properties report correct values.
70+
// Support: Firefox 70 - 135+
71+
// Only Firefox includes border widths
72+
// in computed dimensions for table rows. (gh-4529)
73+
reliableTrDimensionsVal = Math.round( parseFloat( trStyle.height ) +
74+
parseFloat( trStyle.borderTopWidth ) +
75+
parseFloat( trStyle.borderBottomWidth ) ) === tr.offsetHeight;
76+
77+
documentElement.removeChild( table );
78+
79+
// Nullify the table so it wouldn't be stored in the memory;
80+
// it will also be a sign that checks were already performed.
81+
table = null;
82+
}
83+
84+
jQuery.extend( support, {
85+
reliableTrDimensions: function() {
86+
computeTableStyleTests();
87+
return reliableTrDimensionsVal;
88+
},
89+
90+
reliableColDimensions: function() {
91+
computeTableStyleTests();
92+
return reliableColDimensionsVal;
5793
}
58-
return reliableTrDimensionsVal;
59-
};
60-
} )();
94+
} );
6195

6296
export { support };

test/unit/dimensions.js

Lines changed: 83 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -345,17 +345,94 @@ QUnit.test( "child of a hidden elem (or unconnected node) has accurate inner/out
345345
$divNormal.remove();
346346
} );
347347

348-
QUnit.test( "table dimensions", function( assert ) {
348+
QUnit.test( "hidden element with dimensions from a stylesheet", function( assert ) {
349349
assert.expect( 2 );
350350

351-
var table = jQuery( "<table><colgroup><col></col><col></col></colgroup><tbody><tr><td></td><td>a</td></tr><tr><td></td><td>a</td></tr></tbody></table>" ).appendTo( "#qunit-fixture" ),
351+
var div = jQuery( "" +
352+
"<div class='display-none-style'>" +
353+
" <style>" +
354+
" .display-none-style {" +
355+
" display: none;" +
356+
" width: 111px;" +
357+
" height: 123px;" +
358+
" }" +
359+
" </style>" +
360+
"</div>" +
361+
"" )
362+
.appendTo( "#qunit-fixture" );
363+
364+
assert.strictEqual( div.width(), 111, "width of a hidden element" );
365+
assert.strictEqual( div.height(), 123, "height of a hidden element" );
366+
} );
367+
368+
QUnit.test( "hidden element with implicit content-based dimensions", function( assert ) {
369+
assert.expect( 2 );
370+
371+
var container = jQuery( "" +
372+
373+
// font-size affects the child dimensions implicitly
374+
"<div style='font-size: 20px'>" +
375+
" <div style='padding: 10px; display: none'>" +
376+
" <div style='width: 3em; height: 2em'></div>" +
377+
" </div>" +
378+
"</div>" +
379+
"" ),
380+
div = container.children().first();
381+
382+
container.appendTo( "#qunit-fixture" );
383+
384+
assert.strictEqual( div.width(), 60, "width of a hidden element" );
385+
assert.strictEqual( div.height(), 40, "height of a hidden element" );
386+
} );
387+
388+
QUnit.test( "table dimensions", function( assert ) {
389+
assert.expect( 3 );
390+
391+
var table = jQuery( "" +
392+
"<table style='border-spacing: 0'>" +
393+
" <colgroup>" +
394+
" <col />" +
395+
" <col span='2' class='col-double' />" +
396+
" </colgroup>" +
397+
" <tbody>" +
398+
" <tr>" +
399+
" <td></td>" +
400+
" <td class='td-a-1'>a</td>" +
401+
" <td class='td-b-1'>b</td>" +
402+
" </tr>" +
403+
" <tr>" +
404+
" <td></td>" +
405+
" <td>a</td>" +
406+
" <td>b</td>" +
407+
" </tr>" +
408+
" </tbody>" +
409+
"</table>"
410+
).appendTo( "#qunit-fixture" ),
352411
tdElem = table.find( "td" ).first(),
353-
colElem = table.find( "col" ).first().width( 300 );
412+
colElem = table.find( "col" ).first(),
413+
doubleColElem = table.find( ".col-double" );
354414

355-
table.find( "td" ).css( { "margin": 0, "padding": 0 } );
415+
table.find( "td" ).css( { margin: 0, padding: 0, border: 0 } );
416+
417+
colElem.width( 300 );
418+
419+
table.find( ".td-a-1" ).width( 200 );
420+
table.find( ".td-b-1" ).width( 400 );
356421

357422
assert.equal( tdElem.width(), tdElem.width(), "width() doesn't alter dimension values of empty cells, see trac-11293" );
358-
assert.equal( colElem.width(), 300, "col elements have width(), see trac-12243" );
423+
assert.equal( colElem.width(), 300, "col elements have width(), (trac-12243)" );
424+
425+
// Support: IE 11+
426+
// In IE, `<col>` computed width is `"auto"` unless `width` is set
427+
// explicitly via CSS so measurements there remain incorrect. Because of
428+
// the lack of a proper workaround, we accept this limitation.
429+
// To make IE pass the test, set the width explicitly.
430+
if ( QUnit.isIE ) {
431+
doubleColElem.width( 600 );
432+
}
433+
434+
assert.equal( doubleColElem.width(), 600,
435+
"col with span measured correctly (gh-5628)" );
359436
} );
360437

361438
QUnit.test( "SVG dimensions (basic content-box)", function( assert ) {
@@ -668,7 +745,7 @@ QUnit.test( "interaction with scrollbars (gh-3589)", function( assert ) {
668745
.appendTo( "#qunit-fixture" ),
669746

670747
// Workarounds for IE kill fractional output here.
671-
fraction = document.documentMode ? 0 : 0.5,
748+
fraction = QUnit.isIE ? 0 : 0.5,
672749
borderWidth = 1,
673750
padding = 2,
674751
size = 100 + fraction,

test/unit/event.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2707,7 +2707,7 @@ testIframe(
27072707
// IE does propagate the event to the parent document. In this test
27082708
// we mainly care about the inner element so we'll just skip this one
27092709
// assertion in IE.
2710-
if ( !document.documentMode ) {
2710+
if ( !QUnit.isIE ) {
27112711
assert.ok( false, "fired a focusin event in the parent document" );
27122712
}
27132713
} );

test/unit/support.js

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,26 +82,32 @@ testIframe(
8282
expectedMap = {
8383
ie_11: {
8484
cssHas: true,
85+
reliableColDimensions: 11,
8586
reliableTrDimensions: false
8687
},
8788
chrome: {
8889
cssHas: true,
90+
reliableColDimensions: true,
8991
reliableTrDimensions: true
9092
},
9193
safari: {
9294
cssHas: true,
95+
reliableColDimensions: false,
9396
reliableTrDimensions: true
9497
},
9598
firefox: {
9699
cssHas: true,
100+
reliableColDimensions: false,
97101
reliableTrDimensions: false
98102
},
99103
ios_16_3: {
100104
cssHas: false,
105+
reliableColDimensions: false,
101106
reliableTrDimensions: true
102107
},
103108
ios: {
104109
cssHas: true,
110+
reliableColDimensions: false,
105111
reliableTrDimensions: true
106112
}
107113
};
@@ -113,7 +119,7 @@ testIframe(
113119
}
114120
}
115121

116-
if ( document.documentMode ) {
122+
if ( QUnit.isIE ) {
117123
expected = expectedMap.ie_11;
118124
} else if ( /\b(?:headless)?chrome\//i.test( userAgent ) ) {
119125

0 commit comments

Comments
 (0)