HTML/Tutorials/Adventskalender

Aus SELFHTML-Wiki
Wechseln zu: Navigation, Suche

Informationen zu diesem Text

Lesedauer
30min
Schwierigkeitsgrad
mittel
Vorausgesetztes Wissen
Grundkenntnisse in
• HTML
• CSS
• JavaScript

2019 bot SELFHTML wieder einen Adventskalender an, den wir nun ein bisschen näher vorstellen wollen. Dabei stellen wir einerseits Best practice-Methoden vor, erkären andererseits aber auch, warum wir manch vermeintlich einfacheren Weg nicht beschritten haben.

Aus Gründen der Einfachheit (z.B. der Einbindung in ein CMS) beschränken wir uns in diesem Tutorial auf serverseitige Technologien. Ein zweiter Teil bietet eine serverseitige Variante mit PHP.

HTML[Bearbeiten]

Ein Monatskalender ist häufig eine Tabelle, da die Wochentage untereinander in einer Beziehung stehen. Bei einem Adventskalender sind die Türchen entweder nur nummeriert - die Anordnung ist erst einmal egal. Wir entscheiden uns aus Gründen der Semantik für eine geordnete Liste. Schließlich sollte man die Türchen eines Adventskalenders auch der Reihe nach öffnen. Aber ganz ehrlich - wer hat das in der Kindheit immer so gemacht?

Die einzelnen Listenelemente enthalten genau einen Link. Entweder zum Artikel, zur Spendenseite oder den Hinweis, dass dieses Kästchen noch nicht geöffnet werden kann.

HTML-Markup des Adventskalenders
<ol>
<li>
  <a href="Link/zum/Artikel">
    <h2>Überschrift</h2>
    <p>Autor</p>
    <p>Teaser</p>
  </a>
</li>
<li>
  <a class="donate" href="https://selfhtml.org/spenden.html">
    <h2>Es ist noch zu früh.</h2>
    <p>Wussten Sie, dass Sie <span class="self">self</span>HTML unterstützen können?</p>
  </a>
</li>
<li>
  <a class="coming_soon">
    Dieses Türchen öffnet sich erst am 3.&nbsp;Dezember
  </a>
</li>

Damit es innerhalb der Datumsangabe zu keinen Umbrüchen kommen kann, wird das Leerzeichen durch einen &nbsp; ersetzt.

CSS[Bearbeiten]

Mit CSS wird nun die Anordnung und das Aussehen festgelegt.

Grid Layout[Bearbeiten]

Wie oben bereits erwähnt, haben die meisten Adventskalender eine Matrix, innerhalb der die einzelnen Türchen unregelmäßig verteilt werden. Mit Grid Layout kommen wir (fast) ohne media queries aus:

Matrix in Grid Layout
ol {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(20rem, 1fr));
  max-width: 120rem;
}
@media (min-width: 80rem) { /* 4x6 */ 
  ol {
    grid-template-columns: repeat(4, 1fr);
  }
}
@media (min-width: 120rem) { /* 6x4 */ 
  ol {
    grid-template-columns: repeat(6, 1fr);
  } 
}

Die geordnete Liste wird mit display:grid zum responsiven Raster. Dabei werden mit grid-template-columns Spalten angelegt, die keine feste Breite haben, sondern sich mittels der repeat(auto-fill, minmax(20rem, 1fr))-Funktion an die vorhandene Viewportgröße anpassen. Dabei ist jedes Rasterelement mindestens 20em breit - bei 39em würde es noch eine Spalte geben, bei 40em dann automatisch zwei - bei 59em noch zwei, bei 60em dann drei Spalten.

Dabei kommt uns zugute, dass sich 24 Tage durch so ziemlich alles gleichmäßig teilen lassen:

1x24
2x12
3x8
4x6
5x4 %4
6x4
7x3 %3

Um zu verhindern, dass es in unserem Raster einen Rest an Feldern gibt, legen wir mit media queries für die maximalen Breiten 80em und 120em eine feste Spaltenanzahl mit grid-template-columns: repeat(4, 1fr); bzw. 6 Spalten fest.

zufällige Anordnung[Bearbeiten]

Mit der CSS-Eigenschaft order können Sie die Reihenfolge innerhalb der Liste durcheinanderbringen.

Beispiel
li:nth-of-type(1) {
  order: 5;
}
li:nth-of-type(4) {
  order: 7;
}
li:nth-of-type(5) {
  order: 21;
}
li:nth-of-type(6) {
  order: 18;
}
li:nth-of-type(8) {
  order: 5;
}
li:nth-of-type(9) {
  order: 7;
}

Unschön ist hier die Vielzahl von CSS-Regelsätzen. Eine solche Zufalls-Sortierung lässt sich mit JavaScript oder PHP viel einfacher erreichen.

Nummerierung[Bearbeiten]

Eine geordnete Liste stellt jedem Listenelement eine Ordinalzahl in der jeweiligen Formatierung voran. Um diese Zahl individuell gestalten zu können, bietet sich die CSS-Eigenschaft counter-increment an:

Nummerierung der Türchen ansehen …
html {
  --bgcolor: #e6f2f7;
  --linkcolor: #306f91;
  --bordercolor: #c32e04;
}
ol {
  counter-reset: advents-counter;
  list-style: none;  
}
li {
  padding: 0;
  border: thin solid var(--bordercolor);
  counter-increment: advents-counter;
  position: relative;
}

li::after {
  --size: 2rem;
  content: counter(advents-counter);
  position: absolute;
  color: var(--bgcolor);
  font-size: calc(var(--size) * .75);
  font-weight: bold;
  right: calc(var(--size) * .25);
  top: calc(var(--size) * .25);
  line-height: var(--size);
  width: var(--size);
  height: var(--size);
  padding: calc(var(--size) * .2);
  transform: rotate(-10deg);
  background-color: var(--bordercolor);
  border-radius: 50%;
  text-align: center;
  box-shadow: calc(var(--size) * .05) calc(var(--size) * .05) 0 #999;
}

Zuerst wird die Listenformatierung mit list-style: none; ausgeblendet und durch counter-reset: advents-counter; ersetzt.

Jedes Listenelement wird nun mit counter-increment: advents-counter; gezählt. Diese Nummer erscheint nun als Inhalt eines Pseudoelements content: counter(advents-counter) und wird entsprechend positioniert und gestaltet.

Um spätere Änderungen zu erleichtern, wurden die Farb- und Größenangaben in CSS-Variablen notiert, die so zentral geändert werden können. Für den IE 9-11, der dies noch nicht versteht, werden die Zahlen trotzdem angezeigt (Graceful degradation).

Gestaltung der Türen[Bearbeiten]

Der Inhalt der Türchen, die noch nicht geöffnet werden können, soll hübsch zentriert sein,

Gestaltung der Türchen ansehen …
li > .donate, li > .coming_soon {
  margin: 0;
  align-self: center;
  text-align: center;
}

aber dennoch soll die ganze Fläche maussensitiv sein.

Beispiel
li > .donate::after, li > .coming_soon::after {
  content: "";
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
}
Da das a-Element keine von static abweichende Positionierung besitzt, nimmt das Pseudoelement den ganzen Platz des li-Elements ein, das schon wegen der Nummerierung relative positioniert ist.

Die Türchen sollen sich ansprechend öffnen und genauso ansprechend auch wieder schließen.

Türchen versteckt und auf Klick öffnend ansehen …
li > a {
  text-decoration: none;
  color: var(--linkcolor);
}
li > :not(.open) {
  color: var(--bgcolor);
  transition: color ease .5s;
}
li > :not(.open):hover, li > :not(.open):focus {
  color: var(--linkcolor);
  transition: color ease .5s;
}
li > .coming_soon:hover {
  color: inherit;
}

Einfarbig == langweilig?[Bearbeiten]

Wem die einfarbigen Türchen zu langweilig sind, ist aufgefordert, hübsch gestaltete Adventskalender via projekt@selfhtml.org einzureichen. Alle eingehenden und für gut befundenen Vorschläge werden veröffentlicht. Gegebenenfalls könnte man sogar ein paar Preise ausschreiben - vielleicht Schokoladenosterhasen?

JavaScript[Bearbeiten]

Besuchte Türchen zeigen[Bearbeiten]

Es wäre schön, wenn man die bereits besuchten und geöffneten Türchen sichtbar lassen könnte. Eigentlich gibt es dafür mit :visited eine Pseudoklasse, die bereits besuchte Elemente selektiert. Da dies in der Vergangenheit aber zum History Stealing missbraucht wurde, können so im Firefox nicht die Links eingeblendet werden.

Deshalb wird dies nun mit JavaScript nachgebaut:

Besuchte Türchen bleiben offen ansehen …
'use strict';

function init () {
  if (localStorage) { 
     
    // read opened doors
    for (let k = 1; k < 25; k++) {
      if (localStorage.getItem('SELFday'+k)) {
        document.querySelector('li:nth-child('+k+') > a').classList.add('open');
      }
    }
  }

  const calendar = document.querySelector('ol');
  calendar.addEventListener('click', saveTheDoor);
}

function saveTheDoor (event) {
  const elem = event.target,
      link = elem.closest('a'),
      listitem = link.parentElement,
      day = Array.prototype.indexOf.call(calendar.children, listitem) + 1;
  
  if (link.classList.length == 0) {
    link.classList.add('open');
    if (localStorage) {
      localStorage.setItem('SELFday'+day,'open');
    }
  }
}

document.addEventListener('DOMContentLoaded', init);

Bei Laden der Seite wird nun in der Funktion setOpenDays() überprüft, ob es bereits ein LocalStorage-Objekt mit bereits geklickten Türchen gibt. Diese erhalten nun mit classList.add die Klasse open und sind von Anfang an sichtbar.

Fazit[Bearbeiten]

Dieser Kalender muss täglich neu mit den aktuellen Links gefüttert werden. Schöner wäre es natürlich, wenn Sie mit PHP täglich das Datum ermitteln und die URLs dynamisch einfügen können.


Weblinks[Bearbeiten]