Rotated Table Column Headers… Jetzt mit weniger magischen Zahlen!

Avatar of Cappie Pomeroy
Cappie Pomeroy am

DigitalOcean bietet Cloud-Produkte für jede Phase Ihrer Reise. Starten Sie mit 200 $ kostenlosem Guthaben!

Rotated <table> column headers wurde bereits hier auf CSS-Tricks behandelt, also ein Dankeschön dafür, dass es mir den Einstieg erleichtert und mir geholfen hat, diesen Effekt zu erzielen. Wie im Artikel erwähnt, müssen Sie, wenn Sie keine Trigonometrie zur Berechnung Ihrer Tabellenstile verwenden, auf magische Zahlen zurückgreifen, und Ihre Tabelle wird spröde sein und alle Träume von Responsivität zerschlagen. 

Glücklicherweise können wir in diesem Fall die Trigonometrie entfernen und durch sorgfältige Geometrie ersetzen, und all unsere magischen Zahlen werden zu 0 (einer wirklich magischen Zahl).

Für diejenigen, die es eilig haben, hier ist das CSS (es ist dem Stil im anderen Artikel sehr ähnlich). Unten finden Sie eine ausführliche Erläuterung.

<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;
}

Lassen Sie uns diese Tabelle aufschlüsseln und sehen, was vor sich geht. Der Zauber beginnt mit dieser merkwürdigen Kette von HTML-Tags. Wir platzieren einen <span> innerhalb eines <div> innerhalb unseres <th>. Ist das alles wirklich notwendig? Zwischen dem Verhalten von Rändern, der benötigten Flexibilität bei der Positionierung und dem, was die Breite einer Tabellenspalte bestimmt ... ja, jeder hat seinen Zweck und ist notwendig.

Mal sehen, was passiert, wenn wir die <th> direkt drehen

<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;
}

Wenn wir die Tatsache ignorieren, dass wir die Position nicht korrigiert haben, gibt es hier zwei große Probleme: 

  1. Die Spaltenbreite wird immer noch aus der Länge der Kopfzeile berechnet, was wir vermeiden wollten.
  2. Unser Rand kam nicht mit uns in die Drehung, weil er tatsächlich Teil der Tabelle ist.

Diese Probleme sind nicht so schwer zu beheben. Wir wissen, dass der Browser, wenn die <th> ein Kindelement mit einem Rand hat, diesen Rand nicht als Teil der Tabelle behandelt. Weiterhin wissen wir, dass absolut positionierte Elemente aus dem Dokumentfluss genommen werden und die Breite des übergeordneten Elements nicht beeinflussen. Betreten Sie das <div> Tag, links auf der Bühne... und rechts, denke ich.

<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;
}
Jetzt beeinflussen unsere Kopfzeilen nicht mehr die Spaltenbreite und die Ränder sind gedreht. Wir müssen nur noch die Dinge ausrichten.

Im Bild mit den gedrehten <th> Elementen ist es einfacher zu erkennen, aber diese Drehung erfolgt um die Mitte des Elements (das ist das Standardverhalten von transform-origin). Es ist nur eine weitere Transformation in x und y, um es an die richtige Stelle zu bringen, aber hier müssten wir Trigonometrie verwenden, um herauszufinden, wie viel x und y benötigt werden, um es mit den Spaltenrändern auszurichten. Wenn wir stattdessen sorgfältig den Punkt wählen, um den die Kopfzeile gedreht werden soll, und transform-origin verwenden, um ihn auszuwählen, können wir zu Distanzen gelangen, die einfacher als magische Zahlen sind.

Die Animation unten hilft zu veranschaulichen, was wir tun werden, um komplizierte Mathematik zu vermeiden. Der schwarze Punkt in der oberen linken Ecke des blauen Rands muss mit dem roten Punkt am rechten Rand der Tabellenspalte übereinstimmen und sich um diesen drehen. Dann wird es keine Lücken zwischen den beiden Rändern geben.

Es ist nicht hilfreich, irgendwohin zu gehen, wenn man nicht weiß, wo man ist. Die absolute Positionierung wird uns dabei helfen. Durch die Angabe von bottom: 0; left: 0; auf dem <div> landet es unten links in der übergeordneten <th>. Das bedeutet, dass die untere linke Ecke des <div>-Randes auf dem linken Spaltenrand liegt und auf halbem Weg durch diesen hindurchgeht. Von hier aus ist ersichtlich, dass wir uns um eine Randbreite nach unten und um eine Zellenbreite nach rechts bewegen müssen, aber wie bekommen wir das responsiv hin? Genau in diesem Moment erinnern Sie sich vielleicht, dass wir den <span> noch nicht hinzugefügt haben - wir werden ihn brauchen!

Wir verwenden das <div>, um "herauszufinden", wie groß die Tabellenzellen sind, und den <span>, um den Text tatsächlich zu halten und ihn absolut zu positionieren, damit er die übergeordnete Komponente überlappt.

<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;
}

Großartig! Wenn wir die Breite des <div> auf 100% setzen, enthält es die Informationen über die Größe der Spalte, unabhängig vom Inhalt der Tabellenzellen. Mit diesem eingerichtet können wir die Dinge einfach um die Breite des <div> verschieben - aber vergessen Sie nicht, dass wir eine halbe Randbreite abziehen müssen. Unsere Verschiebung wird

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

Das <div> befindet sich nun an der richtigen Stelle zum Drehen, aber wir müssen sicherstellen, dass wir die richtige transform-origin wählen. Wir wollen, dass sie sich an der oberen linken Ecke des Randes befindet, die sich links und eine Randbreite über der Unterseite unseres <div> Elements befindet.

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

Das bringt uns zu unserem endgültigen Stil für die Tabellenkopfzeile.

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;
}

Beachten Sie, dass Transformationen nach der Platzierung aller Elemente stattfinden. Das bedeutet, dass die gedrehten Kopfzeilen so gut wie möglich über alles andere überlappen werden. Sie müssen die gesamte Tabelle in etwas einpacken, um die unerwartete Höhe auszugleichen. Ich habe den Titel und die Tabelle in ein Flexbox <div> zusammengefasst und die flex-basis des Titels auf einen Wert gesetzt, der groß genug ist, um die hohen Kopfzeilen auszugleichen.

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