旋转表格列标题… 现在没有那么多魔数了!

Avatar of Cappie Pomeroy
Cappie Pomeroy

DigitalOcean 为您旅程的每个阶段提供云产品。 立即开始使用 200 美元的免费积分!

旋转 <table> 列标题是之前在 CSS-Tricks 上 讨论过 的内容,因此感谢它帮助我起步并实现这种效果。 正如文章中所指出的,如果您没有使用三角函数来计算表格样式,则必须依靠 魔数,您的表格将会很脆弱,任何响应式的梦想都会破灭。

幸运的是,在这种情况下,我们可以去除三角函数并用一些细致的几何图形来代替它,并且我们的魔数都变成了 0(一个真正的神奇数字)。

对于那些赶时间的人来说,这里有 CSS 代码(它与另一篇文章中的样式非常相似)。 下面是详细的逐步指南。

<th class="rotate"><div><span>Column Header 1</span></div></th>
table {
 border-collapse: collapse;
 --table-border-width: 1px;
}
th.rotate {
  white-space: nowrap;
  position: relative;

}
th.rotate > div {
  /* place div at bottom left of the th parent */
  position: absolute;
  bottom: 0;
  left: 0;
  /* Make sure short labels still meet the corner of the parent otherwise you'll get a gap */
  text-align: left;
  /* Move the top left corner of the span's bottom-border to line up with the top left corner of the td's border-right border so that the border corners are matched
   * Rotate 315 (-45) degrees about matched border corners */
  transform: 
    translate(calc(100% - var(--table-border-width) / 2), var(--table-border-width))
    rotate(315deg);
  transform-origin: 0% calc(100% - var(--table-border-width));
  width: 100%;

}
th.rotate > div > span {
  /* make sure the bottom of the span is matched up with the bottom of the parent div */
  position: absolute;
  bottom: 0;
  left: 0;
  border-bottom: var(--table-border-width) solid gray;
}
td {
  border-right: var(--table-border-width) solid gray;
  /* make sure this is at least as wide as sqrt(2) * height of the tallest letter in your font or the headers will overlap each other*/
  min-width: 30px;
  padding-top: 2px;
  padding-left: 5px;
  text-align: right;
}

让我们来拆解这个表格并看看发生了什么。 魔法从这个有趣的 HTML 标签链开始。 我们在 <th> 中的 <div> 中放置一个 <span>。 这真的有必要吗? 在边框的行为、我们需要的定位灵活性以及决定表格列宽度的因素之间… 是的,它们各自都有用途,并且是必要的。

让我们看看如果我们直接旋转 <th> 会发生什么

<th class="rotate">Column header 1</th>
table {
  border-collapse: collapse;
}
th.rotate {
  border-bottom: 1px solid gray;
  transform: rotate(315deg);
  white-space: nowrap;
}
td {
  border-right: 1px solid gray;
  min-width: 30px;
  padding-top: 2px;
  padding-left: 5px;
  text-align: right;
}

不考虑我们没有纠正位置的事实,这里有两个主要问题:

  1. 列宽仍然根据标题长度计算,而这正是我们要避免的。
  2. 我们的边框并没有随着旋转一起旋转,因为它实际上是表格的一部分。

这些问题并不难解决。 我们知道,如果 <th> 具有带边框的子元素,浏览器不会将该边框视为表格的一部分。 此外,我们知道,绝对定位的元素将从文档流中移除,并且不会影响父元素的宽度。 让我们从左侧引入 <div> 标签… 以及右侧,我想。

<th class="rotate"><div>Column header 1</div></th>
table {
  border-collapse: collapse;
}
th.rotate {
  white-space: nowrap;
  position: relative;
}
th.rotate > div {
  position: absolute;
  transform: rotate(315deg);
  border-bottom: 1px solid gray;
}
td {
  border-right: 1px solid gray;
  min-width: 30px;
  text-align: right;
  padding-top: 2px;
  padding-left: 5px;
}
现在,我们的标题不再影响列宽,并且边框也已旋转。 我们只需要将它们对齐。

在带有旋转 <th> 元素的图像中更容易看出,但该旋转是在元素的中心进行的(这是 transform-origin 的默认行为)。 它只是在 x 和 y 上的另一个变换来将其放到正确的位置,但这就是我们需要三角函数来计算出将它与列边框对齐需要多少 x 和 y 的地方。 如果我们相反仔细选择旋转标题的点,并使用 transform-origin 来选择它,那么最终得到的距离将比魔数更直接。

下面的动画有助于说明我们将要做什么来避免复杂的数学运算。 蓝色边框左上角的黑色点需要与表格列右边的红色点匹配,并围绕它旋转。 这样,两个边框之间就不会有间隙。

如果你不知道你在哪里,去某个地方是没有帮助的。 绝对定位将在这方面帮助我们。 通过在 <div> 上指定 bottom: 0; left: 0;,它最终将在父 <th> 的左下角。 这意味着 <div> 边框的左下角位于左列边框的顶部并穿过它的一半。 从这里可以明显看出,我们需要向下移动一个边框宽度,向右移动一个单元格宽度,但我们如何才能以响应方式实现这一点呢? 正是在这一刻,您可能会想起我们还没有添加 <span> — 我们需要它!

我们将使用 <div> 来“找出”表格单元格有多大,并使用 <span> 来实际容纳文本并将其绝对定位,以便溢出父元素。

<th class="rotate"><div><span>Column header 1</span></div></th>
th.rotate{
  white-space: nowrap;
  position: relative;
}
th.rotate > div {
  position: absolute;
  bottom: 0;
  left: 0;
  width: 100%;  /* <- now the div parent is as wide as the columns */
}
th.rotate > div > span {
  position: absolute;
  bottom: 0;
  left: 0;
  border-bottom: 1px solid gray;
}

太好了! 当我们将 <div> 的宽度设置为 100% 时,它会保存有关列大小的信息,而不管表格单元格中的内容是什么。 在此基础上,我们可以轻松地将内容平移到 <div> 的宽度 — 但不要忘记,我们需要削减半个边框宽度。 我们的平移变成了

transform: translate( calc( 100% - var(--table-border-width)/2), var(--table-border-width));

<div> 现在处于正确的位置进行旋转,但我们必须确保选择正确的 transform-origin。 我们希望它位于边框的左上角,该点将在我们的 <div> 元素底部左侧并向上移动一个边框宽度

transform-origin: 0%, calc(100% - var(--table-border-width));

这让我们得到了表格标题的最终样式。

table {
  border-collapse: collapse;
  --table-border-width: 1px;
}
th.rotate{
  white-space: nowrap;
  position: relative;
}
th.rotate > div {
  position: absolute;
  bottom: 0;
  left: 0;
  width: 100%;
  transform:
    translate( calc( 100% - var(--table-border-width)/2), var(--table-border-width));
    rotate(315deg);
  transform-origin: 0%, calc(100% - var(--table-border-width));
}
th.rotate > div > span {
  position: absolute;
  bottom: 0;
  left: 0;
  border-bottom: var(--table-border-width) solid gray;
}

请注意,变换发生在所有内容都被放置之后。 这意味着旋转的标题将尽可能地溢出到所有内容上。 您需要将整个表格包裹在某个东西中,以弥补意外高度。 我将标题和表格一起放在一个 flexbox <div> 中,并将标题的 flex-basis 设置为足够大的值,以弥补高标题。

#div-with-table {
  display: flex;
  flex-direction: column;
  justify-content: space-around;
}
#title {
  flex-basis: 140px;
}