File: colspan_redistribution.tentative.html

package info (click to toggle)
thunderbird 1%3A78.14.0-1~deb10u1
  • links: PTS, VCS
  • area: main
  • in suites: buster
  • size: 2,922,628 kB
  • sloc: cpp: 5,990,120; javascript: 4,418,692; ansic: 3,063,889; python: 915,509; asm: 304,197; xml: 206,623; sh: 109,253; java: 108,679; makefile: 22,985; perl: 15,867; yacc: 4,565; objc: 3,026; pascal: 1,787; lex: 1,720; ada: 1,681; cs: 879; exp: 505; awk: 485; sql: 452; php: 436; lisp: 432; ruby: 99; sed: 69; csh: 45
file content (233 lines) | stat: -rw-r--r-- 9,026 bytes parent folder | download | duplicates (4)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
<!doctype html>
<script src='/resources/testharness.js'></script>
<script src='/resources/testharnessreport.js'></script>
<script src="/resources/check-layout-th.js"></script>
<link rel="author" title="Aleks Totic" href="atotic@chromium.org" />
<link rel="help" href="https://www.w3.org/TR/css-tables-3/#distributing-width-to-columns" />
<style>
  table {
    background: gray;
    margin-bottom: 24px;
    border-spacing: 8px 8px;
  }
  td {
    padding: 0;
    background: #BFB;
    font-size: 10px;
  }
  td > div {
    display: inline-block;
    background: rgba(56,162,56,0.3);
  }
  .error {
    color: red;
  }
  .brick {
    width: 30px;
    height: 20px;
  }
  p {
    margin-top:4px;
    margin-bottom:4px;
  }
  .rule1 {
    background: #87dc8a;
  }
  .rule2 {
    background: #3ae4cc;
  }
  .rule1and2 {
    background: #fda4a4;
  }
</style>
<h1>Colspan > 1 width redistribution</h1>
<p>This page is intended to be used as a wpt testharness test, and for examining what algorithms do. Hovering over cells will display information about the table.</p>
<p>Principles</p>
<ul>
  <li>Distribution should be stable: reordered span cells retain their width.</li>
</ul>
<p>Wide cell redistributes its different widths to the spanned cells: percentage, min and max.</p>
<p>Creating understandable tests with percentage cells is complex because the relationship between percentage cell width, and table width is complex. Rules that govern relationship between table grid width and percentage cell width are:</p>
<ol>
  <li><span class="rule1">Rule#1</span>, Single column sets minimum table width. Minimum table width is column.min_width / column.percent * 100.</li>
  <li><span class="rule2">Rule#2</span>, All percentage columns set minimum table width. <br>Let P% be sum of all percentages, and Mpx sum of minimum widths of all non-percentage columns. The equations below determine minimum table width:
    <ul>
    <li>table width = Mpx + Ppx </li>
    <li>P% + M% = 100%</li>
    <li>Ppx * P% = Mpx * M% </li>
    <li>Ppx * P% = Mpx * (100-P%) </li>
    <li>Ppx = Mpx * (100-P%) / P% </li>
    <li>table width = Mpx + Mpx * (100-P%) / P%.</li>
    <li>table width = Mpx * (1 + (100-P%)/P%)</li>
    </ul>
  </li>
  <p class="error">FF and Edge interpret rule#2 as maximum table width, while Chrome interprets it as minimum table width. </p>
  <p>Table backgrounds will change if they match the 2 rules above: <span class="rule1">Rule#1</span>, <span class="rule2">Rule#2</span>, and both <span class="rule1and2">Rule#1 and Rule#2</span>.</p>
</ol>
  <p>All examples have border-spacing:8, td.padding:0.</p>

<h2>Percentage redistribution</h2>

<h3>Percentage CSS constrained span cells</h3>

<p>Rules<br>
  <ul>
    <li>min width never gets smaller than it started.
    <li>min width becomes cell.percent/cells.percent * wide_cell.min_width</li>
    <li>max width becomes cell.percent/cells.percent * wide_cell.max_width</li>
  </ul>
</p>

<p>10) auto/300px wide cell, 25%/50px span min width<br>
wide min width gets distributed evenly (-border_spacing) to both cells at 146px<br>
Table width: 146/0.25 + 4*8 = 584 + 32 = 616
<table data-expected-width="616">
  <tr>
    <td style="width:25%" data-expected-width="146"><div style="width:50px">25%/30px</div></td>
    <td style="width:25%"><div style="width:50px">25%/30px</div></td>
    <td style="width:20px">x</td>
  </tr>
  <tr>
    <td colspan=2 style=""><div style="width:300px">300px min</div></td>
  </tr>
</table>

<p>11) auto/400px wide cell, 20%/50px, 60%/50px spans.<br>
400 - 8px min width gets redistributed to span cells as 98px/294px.<br>
98px/0.2 => min grid width 490 + 4*8 = 522.</p>
<p class="error">Edge disagrees, table is 870</p>
<div style="width:600px">
<table data-expected-width="522">
  <tr>
    <td style="width:20%" data-expected-width="98"><div style="width:50px">20%/50px</div></td>
    <td style="width:60%" data-expected-width="294"><div style="width:50px">60%/50px</div></td>
    <td style="width:20px">x</td>
  </tr>
  <tr>
    <td colspan=2 style=""><div style="width:400px">400px min</div></td>
  </tr>
</table>
</div>

<p>12) auto/400px wide cell, 50%/150px, 30%/150px spans.<br>
This tests conflict resolution where min-width > redistributed width, and all browsers disagree.<br>
min-width of the 2nd cell is larger than redistributed width, causing differences.<br>
400-8px distributed max width tries to redistribute as 245|147, but gets constrained to 245|150 in Chrome.<br>
table width from cell 1 245/0.5 + 4*8 = 522<br>
table width from cell 2 150/0.3 + 4*8 = 532<br>
cell 1 = 50% of 500 = 250, cell 2 = 30% of 500 = 150 , cell 3 gets the remaining 100</p>
<p class="error">Chrome/FF/Edge end up with tables of different widths: 532/590/685. Chrome's 2nd span cell seems 'most correct' at its original max width of 150. In FF, extra min-width seems to cause more width to be redistributed. If you hover over 30%/150 cell, its min width will change to 100px, and all browsers will agree.</p>
<style>
  .test12:hover {
    width:100px !important;
  }
</style>
<table data-expected-width="532">
  <tr>
    <td style="width:50%" data-expected-width="250"><div style="width:150px">50%/150px</div></td>
    <td style="width:30%" data-expected-width="150"><div class="test12" style="width:150px">30%/150px</div></td>
    <td style="width:20px" data-expected-width="100">x</td>
  </tr>
  <tr>
    <td colspan=2 style=""><div style="width:400px">400px min</div></td>
  </tr>
</table>

<p>13) auto/400px wide cell, 50%/75px/125px, 30%/75px/125px spans.<br>
400-8px min width gets redistributed as 245/147 (no min width limits)<br>
</p>
<p class="error">Edge is different</p>
  <table data-expected-width="522">
  <tr>
    <td style="width:50%" data-expected-width="245"><div style="width:75px">50%/75</div><div style="width:50px">/125</div></td>
    <td style="width:30%" data-expected-width="147"><div style="width:75px">30%/75</div><div style="width:50px">/125</div></td>
    <td style="width:20px">x</td>
  </tr>
  <tr>
    <td colspan=2 style=""><div style="width:400px">300px min</div></td>
  </tr>
</table>


<script>
function measureCellMinMax(cell) {
  let d = document.createElement("div");
  let clone = cell.cloneNode(true);
  for (child of Array.from(clone.childNodes))
    d.appendChild(child);
  d.style.width = "min-content";
  document.body.appendChild(d);
  let min = d.offsetWidth;
  d.style.width = "max-content";
  let max = d.offsetWidth;
  d.remove();
  return {min: min, max: max};
}
function annotateTable(t) {
  let tableWidth = t.offsetWidth;

  let firstCell;
  let totalCellPercent = 0;
  let nonPercentCellWidth = 0;
  let tableMinWidthByCellPercent = 0;
  let cellCount = 0;
  let cells = Array.from(t.querySelectorAll("td"));
  let spacing = (2 + cells.length - 1)*8;
  for (let cell of cells) {
    if (!firstCell)
      firstCell = cell;
    cellCount++;
    let percent = cell.offsetWidth / (tableWidth - spacing) * 100;
    let minmax = measureCellMinMax(cell);
    let title = `${cell.offsetWidth.toFixed(1)}px\nmin:${minmax.min}px max:${minmax.max}px ${percent.toFixed(1)}%`;
    let cssWidth = cell.style.width;
    if (cssWidth) {
      let w = parseFloat(cssWidth);
      if (cssWidth.match(/\%/)) {
        totalCellPercent += w;
        let tableMinWidth = minmax.min / (w / 100);
        tableMinWidthByCellPercent = Math.max(tableMinWidthByCellPercent, tableMinWidth);
        title += `\nmin table: ${tableMinWidth.toFixed(0)}px`;
      } else {
        nonPercentCellWidth += w;
      }
    }
    title += `\nTable: ${tableWidth.toFixed(0)} spacing: ${spacing}px`;
    cell.setAttribute("title", title);
  }


  // Display table statistics in first cell.
  if (firstCell) {
    let title = firstCell.getAttribute("title");
    let ruleMatch = 0;
    if (tableMinWidthByCellPercent != 0) {
      if ((tableMinWidthByCellPercent + spacing) == tableWidth)
        ruleMatch += 1;
      title += `\nTable min by single cell percent: ${tableMinWidthByCellPercent.toFixed(1)}`;
    } else {
      title += "\nTable min by single cell percent NA";
    }
    if (nonPercentCellWidth && totalCellPercent > 0) {
      totalCellPercent = Math.min(totalCellPercent, 100);
      title += `\nPercent sum: ${totalCellPercent.toFixed(1)}%, non percent width: ${nonPercentCellWidth}px`;
      let tableMinBySum = (totalCellPercent / (100 - totalCellPercent) +1) * nonPercentCellWidth;
      if (Math.floor((tableMinBySum + spacing)) == Math.floor(tableWidth))
        ruleMatch += 2;
      title += `\nTable min by sum ${totalCellPercent}%: ${tableMinBySum.toFixed(1)}px`;
    } else {
      "Table min by % sum not available";
    }
    firstCell.setAttribute("title", title);
    switch(ruleMatch) {
      case 1: t.classList.toggle('rule1'); break;
      case 2: t.classList.toggle('rule2'); break;
      case 3: t.classList.toggle('rule1and2'); break;
      default: break;
    }
  }
}
for (let t of Array.from(document.querySelectorAll("table")))
  annotateTable(t);
checkLayout("table");
</script>