rowspan-height-redistribution.html (14352B)
1 <!doctype html> 2 <title>ROWSPAN redistribution</title> 3 <script src='/resources/testharness.js'></script> 4 <script src='/resources/testharnessreport.js'></script> 5 <script src="/resources/check-layout-th.js"></script> 6 <link rel="stylesheet" type="text/css" href="./support/table-tentative.css"> 7 <link rel="author" title="Aleks Totic" href="atotic@chromium.org" /> 8 <link rel="help" href="https://drafts.csswg.org/css-tables-3/#row-layout" /> 9 <link rel="help" href="https://github.com/w3c/csswg-drafts/issues/4418" /> 10 <style> 11 main table { 12 margin-top: 8px; 13 border-collapse: collapse; 14 background: rgba(0,0,255,0.1); 15 background-image: linear-gradient(45deg, #DDD 25%, transparent 25%), linear-gradient(-45deg, #DDD 25%, transparent 25%), linear-gradient(45deg, transparent 75%, #DDD 75%), linear-gradient(-45deg, transparent 75%, #DDD 75%); 16 background-size: 20px 20px; 17 background-position: 0 0, 0 10px, 10px -10px, -10px 0px; 18 } 19 .sizer { 20 width: 30px; 21 height: 100px; 22 } 23 main tbody tr:nth-child(odd) { 24 background: rgba(255,255,0,1.0); 25 } 26 main tbody tr:nth-child(even) { 27 background: rgba(255,165,0,1.0); 28 } 29 main td div { 30 background: radial-gradient(ellipse at center, rgba(255,255,255,0.5) 0%, rgba(0,255,0,0.7) 100%); 31 } 32 main .td-padding td { 33 padding: 2px; 34 } 35 main .td-padding-xl td { 36 padding: 10px; 37 } 38 </style> 39 <main> 40 <h1>ROWSPAN > 1 to row distribution</h1> 41 <p>The algorithm has not been standardized. This is my understanding of how it works.</p> 42 <ol> 43 <li>rowspan>1 TDs are sorted:</li> 44 <ol> 45 <li>If TD span the same rows, taller TD is distributed first.</li> 46 <li>If one TD is fully enclosed by another, inner TD is distributed first.</li> 47 <li>Otherwise, higher TD is distributed first.</li> 48 </ol> 49 <li>Each rowspan>1 TD's height is distributed as following</li> 50 <ol> 51 <li>rowspan > 1 TDs have height TDh, span N rows. N rows have total height of Rh. TDh - Rh height, Dh, must be distributed as follows.</li> 52 <li>If percentage resolution size is available (this happens when redistributiong table/section height), percentage rows grow to their percentage size, proportional to (percentage size - current size). Dh shrinks by distributed height. Justification: explicit percentage rows should grow to their percentage.</li> 53 <li>Rows that originate rowspan>1 cells get all the Dh height, distributed evenly. Justification: rowspan>1 rows are likely to need to grow later. If there are multiple rowspan>1 cells, there can be multiple originating rows.</li> 54 <li>Unconstrained non-empty rows grow, proportional to their existing height.</li> 55 <li>If all rows are empty, last row gets all the height. Justification: ???</li> 56 <li>Contstrained rows grow in proportion to current height.</li> 57 </ol> 58 </ol> 59 <p class="error">It is unclear what the existing ChromeLegacy/FF algorithms do for distribution over rowspan>1 and empty cells. <a href="https://dxr.mozilla.org/mozilla-central/source/layout/tables/nsTableRowGroupFrame.cpp#509">FF special cases</a> "there is no cell originating in the row with owspan=1 and there are at least 2 cells spanning the row. Chrome Legacy also tries to do something similar, but they disagree on what. TablesNG will try to ship without special cases.</p> 60 <p class="error">Safari fails most of these tests</p> 61 <p>Color scheme</p> 62 <table> 63 <tr> 64 <td>odd rows are yellow</td> 65 </tr> 66 <tr> 67 <td>even rows are orange</td> 68 </tr> 69 <tr> 70 <td><div style="height:50px">inner divs have a green gradient</div> 71 </td> 72 </tr> 73 <tr> 74 <td>row</td> 75 </tr> 76 <tr> 77 <td>row</td> 78 </tr> 79 <tr> 80 <td>row</td> 81 </tr> 82 <tr> 83 <td>row</td> 84 </tr> 85 86 </table> 87 <h2>Unconstrained rows</h2> 88 <p>Rows whose height is not fixed or percent are unconstrained.</p> 89 <p class="testdesc">Unconstrained rows 90 Unconstrained rows are redistributed proportionally. Rows are constrained if their height is fixed, or percent.</p> 91 <table> 92 <tbody> 93 <tr data-expected-height="50"> 94 <td>0,0</td> 95 <td rowspan="2"><div class="sizer"></div></td> 96 </tr> 97 <tr data-expected-height="50"> 98 <td>0,1</td> 99 </tr> 100 </table> 101 102 <p class="testdesc">Unconstrained rows with zero height do not grow.</p> 103 <table> 104 <tbody> 105 <tr data-expected-height="50"> 106 <td>0,0</td> 107 <td rowspan="3"><div class="sizer"></div></td> 108 </tr> 109 <tr data-expected-height="0"> 110 </tr> 111 <tr data-expected-height="50"> 112 <td>0,2</td> 113 </tr> 114 </table> 115 116 <p class="testdesc">rowspan>1 is zero height, spanned rows have height.</p> 117 <table> 118 <tbody> 119 <tr data-expected-height="0"> 120 <td></td> 121 <td rowspan="3"><div class="sizer"></div></td> 122 <td></td> 123 </tr> 124 <tr data-expected-height="50"> 125 <td>1,0</td> 126 <td></td> 127 </tr> 128 <tr data-expected-height="50"> 129 <td>2,0</td> 130 <td></td> 131 </tr> 132 </table> 133 134 <p class="testdesc">Unconstrained rows are redistributed proportionally to heights</p> 135 <table> 136 <tbody> 137 <tr data-expected-height="75"> 138 <td><div style="height:45px">0,0</div></td> 139 <td rowspan="2"><div class="sizer"></div></td> 140 </tr> 141 <tr data-expected-height="25"> 142 <td><div style="height:15px">0,1</div></td> 143 </tr> 144 </table> 145 146 <h2>Fixed rows</h2> 147 148 <p class="testdesc">Constrained fixed rows 149 do not grow if there are unconstrained ones</p> 150 <p class="error">Edge grows constrained rows too</p> 151 <table> 152 <tbody> 153 <tr style="height: 30px" data-expected-height="30"> 154 <td>0,0 30px</td> 155 <td rowspan="2"><div class="sizer"></div></td> 156 </tr> 157 <tr data-expected-height="70"> 158 <td>0,1</td> 159 </tr> 160 </table> 161 162 <p class="testdesc"> Constrained fixed rows 163 grow proportionally to their size if there are no unconstrained rows</p> 164 <table> 165 <tbody> 166 <tr style="height: 20px" data-expected-height="25"> 167 <td>20</div></td> 168 <td rowspan="3"><div class="sizer"></div></td> 169 </tr> 170 <tr style="height: 20px" data-expected-height="25"> 171 <td>20</td> 172 </tr> 173 <tr style="height: 40px" data-expected-height="50"> 174 <td>40</td> 175 </tr> 176 </table> 177 178 <h2>Percent rows</h2> 179 180 <p class="testdesc">Constrained percent rows 181 grow like unconstrained ones when percent resolution size is undefined.</p> 182 <p class="error">FF always treats percent rows as constrained. Chrome legacy does resolve percentage against final height of the table. I do not think that can work. Edge follows NG.</p> 183 <table> 184 <tbody> 185 <tr style="height: 30%" data-expected-height="50"> 186 <td>0,0 30%</td> 187 <td rowspan="2"><div class="sizer"></div></td> 188 </tr> 189 <tr data-expected-height="50"> 190 <td>0,1</td> 191 </tr> 192 <tr style="height:100px"><td>100px</td></tr> 193 </table> 194 195 196 <p class="testdesc">Percent rows with zero height 197 do not grow.</p> 198 <p class="error">Legacy Chrome has a strange gap between rows</p> 199 <table> 200 <tbody> 201 <tr data-expected-height="50"> 202 <td>0,0</td> 203 <td rowspan="3"><div class="sizer"></div></td> 204 </tr> 205 <tr style="height:10%;background:red" data-expected-height="0"> 206 </tr> 207 <tr data-expected-height="50"> 208 <td>2,0</td> 209 </tr> 210 </table> 211 212 <h2>Order of rowspan distribution</h2> 213 214 215 <p class="testdesc">If cells span the same rows, bigger cell is distributed first 216 Not sure how to test this, I think it is just an optimization, there is no observable effect. 217 <p class="error">FF and Legacy Chrome unexpectedly distribute height evenly between rows in the first test case. Edge and TablesNG do not.</p> 218 <table> 219 <tr data-expected-height=0> 220 <td rowspan=3><div style="height:50px"></div></td> 221 <td rowspan=3><div style="height:99px"></div></td> 222 </tr> 223 <tr data-expected-height=0> 224 </tr> 225 <tr data-expected-height=99> 226 </tr> 227 <tr> 228 <td>bottom</td> 229 <td>bottom</td> 230 </tr> 231 </table> 232 <table> 233 <tr data-expected-height=0> 234 <td rowspan=3><div style="height:50px"></div></td> 235 <td rowspan=3><div style="height:99px"></div></td> 236 <td style="width:20px"></td> 237 </tr> 238 <tr data-expected-height=0> 239 <td></td> 240 </tr> 241 <tr data-expected-height=99> 242 <td></td> 243 </tr> 244 <tr> 245 <td>bottom</td> 246 <td>bottom</td> 247 </tr> 248 </table> 249 <table> 250 <tr data-expected-height=0> 251 <td rowspan=3><div style="height:99px;width:20px"></div></td> 252 </tr> 253 <tr></tr> 254 <tr data-expected-height=99></tr> 255 <tr> 256 <td>bottom</td> 257 <td>bottom</td> 258 </tr> 259 </table> 260 261 262 <p class="testdesc">If one cell is fully enclosed by another, inner cell wins. 263 <p class="error">Not in Edge</p> 264 <table> 265 <tr data-expected-height=0> 266 <td rowspan=4><div style="height:50px;width:20px"></div></td> 267 <td></td> 268 <tr data-expected-height=0> 269 <td></td> 270 <td rowspan=2><div style="height:100px;width:20px"></div></td> 271 </tr> 272 <tr data-expected-height=100></tr> 273 <tr data-expected-height=0></tr> 274 </tr> 275 </table> 276 277 <p class="testdesc">First row wins. 278 rowspan-4 distributes 50 to last empty row, row3. rowspan-3 distributes 100px to only nonempty row, row3. 279 <p class="error">Edge disagrees here.</p> 280 <table> 281 <tr data-expected-height=0> 282 <td rowspan=4><div style="height:50px;width:20px"></div></td> 283 <td></td> 284 <tr data-expected-height=0></tr> 285 <tr data-expected-height=0></tr> 286 <tr data-expected-height=100> 287 <td></td> 288 <td rowspan=3><div style="height:100px;width:20px"></div></td> 289 </tr> 290 <tr data-expected-height=0></tr> 291 <tr data-expected-height=0></tr> 292 </tr> 293 </table> 294 295 <h2>Rowspan distribution over empty rows.</h2> 296 297 <p class="testdesc">Rowspans that span non-existent rows 298 Span is truncated so only existing rows are spanned.</p> 299 <table> 300 <tbody data-expected-height="100"> 301 <tr> 302 <td data-expected-height="50">0,0</td> 303 <td data-expected-height="100" rowspan="5"><div style="height:100px;">rowspan 5</div></td> 304 </tr> 305 <tr> 306 <td data-expected-height="50">1,0</td> 307 </tr> 308 <tr data-expected-height="0"></tr> 309 </tbody> 310 <tbody> 311 <tr> 312 <td>body 2</td> 313 </tr> 314 </tbody> 315 </table> 316 317 <p class="testdesc">Rowspan spans only empty rows 318 Last spanned row gets all the height. 319 <p class="error">Edge distributes height to all empty rows, not just last.</p> 320 <table> 321 <tr> 322 <td>first row</td> 323 </tr> 324 <tr data-expected-height=0> 325 <td></td> 326 <td rowspan=5><div style="height:100px;width:30px;"></div></td> 327 </tr> 328 <tr data-expected-height=0><td></td></tr> 329 <tr data-expected-height=0><td></td></tr> 330 <tr data-expected-height=0><td></td></tr> 331 <tr data-expected-height=100><td></td></tr> 332 <tr> 333 <td>last row</td> 334 </tr> 335 </table> 336 337 <p class="testdesc">TD is not considered empty if it has padding, but no content 338 <table> 339 <tr> 340 <td>first row</td> 341 </tr> 342 <tr data-expected-height=0> 343 <td></td> 344 <td style="height:100px" rowspan=3></td> 345 </tr> 346 <tr data-expected-height=100><td style="padding:2px"></td></tr> 347 <tr data-expected-height=0></tr> 348 <tr> 349 <td>last row</td> 350 </tr> 351 </table> 352 353 <p class="testdesc">row with an empty tall cell is not considered empty. 354 <p class="error"> 355 <table> 356 <tr> 357 <td rowspan=5><div style="height:100px">rowspan</div></td> 358 <td></td> 359 <td></td> 360 </tr> 361 <tr data-expected-height=100> 362 <td rowspan=5></td> 363 <td></td> 364 </tr> 365 <tr></tr> 366 <tr></tr> 367 <tr></tr> 368 <tr></tr> 369 </table> 370 371 <p class="testdesc">Empty rows with border-spacing big enough for rowspan cell 372 rows are 0 height, cell spans the entire table. 373 <table style="border-spacing:20px;border-collapse:separate " data-expected-height=100> 374 <tr data-expected-height=0> 375 <td rowspan=4><div style="height:60px;width:40px"></div></td> 376 </tr> 377 <tr data-expected-height=0></tr> 378 <tr data-expected-height=0></tr> 379 <tr data-expected-height=0></tr> 380 </table> 381 382 <p class="testdesc">row with a non-empty rowspan>0 cell is empty. 383 Distributes to all rows except start row? 384 <p class="error"> 385 <table> 386 <tr> 387 <td rowspan=5><div style="height:100px">rowspan</div></td> 388 <td></td> 389 </tr> 390 <tr data-expected-height=100> 391 <td rowspan=5><div style="height:100px">rowspan</div></td> 392 </tr> 393 <tr data-expected-height=0></tr> 394 <tr data-expected-height=0></tr> 395 <tr data-expected-height=0></tr> 396 <tr></tr> 397 <tr></tr> 398 </table> 399 <table> 400 <tr> 401 <td rowspan=5><div style="height:100px">rowspan</div></td> 402 <td></td> 403 </tr> 404 <tr data-expected-height=100> 405 <td>yo</td> 406 </tr> 407 <tr data-expected-height=0></tr> 408 <tr data-expected-height=0></tr> 409 <tr data-expected-height=0></tr> 410 </table> 411 412 413 <p class="testdesc">Distribution over rowspan > 1 rows 414 Distribution over rowspan > 1 rows 415 416 <table class="td-padding-xl" data-expected-height=360> 417 <tr> 418 <td rowspan=6><div style="width:50px;height:280px"></div></td> 419 <td></td> 420 <td></td> 421 </tr> 422 <tr> 423 <td></td> 424 </tr> 425 <tr style="height:30%;background:purple"> 426 <td data-expected-height=20></td> 427 </tr> 428 <tr data-expected-height=110> 429 <td rowspan=7 ></td> 430 </tr> 431 <tr data-expected-height=110> 432 <td rowspan=17 ><div style="width:50px;height:40px"></div></td> 433 </tr> 434 <tr> 435 <td></td> 436 </tr> 437 <tr> 438 <td></td> 439 </tr> 440 <tr> 441 <td></td> 442 </tr> 443 <tr> 444 <td></td> 445 </tr> 446 </table> 447 448 <p class="testdesc">Distribution of table height over rowspan > 1 rows 449 If there are any unconstrained non-empty rows, they get it. 450 When all rows are empty, last row takes it</p> 451 <table class="td-padding-xl" style="height:460px"> 452 <tr> 453 <td rowspan=6><div style="width:50px;height:280px" data-expected-height=280></div></td> 454 <td></td> 455 <td></td> 456 </tr> 457 <tr> 458 <td></td> 459 </tr> 460 <tr style="height:30%;background:purple" data-expected-height=120> 461 <td ></td> 462 </tr> 463 <tr data-expected-height=110> 464 <td rowspan=7 ></td> 465 </tr> 466 <tr data-expected-height=110> 467 <td rowspan=17><div style="width:50px;height:40px"></div></td> 468 </tr> 469 <tr> 470 <td></td> 471 </tr> 472 <tr> 473 <td></td> 474 </tr> 475 <tr> 476 <td></td> 477 </tr> 478 <tr> 479 <td></td> 480 </tr> 481 </table> 482 483 <p class="testdesc">Distribution of rowspan over percentage rows 484 Percentage rows are considered empty if they cannot resolve</p> 485 <table> 486 <tbody> 487 <tr style="height:20%"> 488 <td rowspan=3><div style="height:100px;width:100px"></div></td> 489 <td></td> 490 </tr> 491 <tr style="height:30%"> 492 <td></td> 493 </tr> 494 <tr data-expected-height=100 style="height:50%"> 495 <td></td> 496 </tr> 497 </tbody> 498 </table> 499 500 </main> 501 <script> 502 checkLayout("table"); 503 </script>