HTML/Tutorials/Adventskalender
Aus Gründen der Einfachheit (z. B. der Einbindung in ein CMS) beschränken wir uns in diesem Tutorial auf clientseitige Technologien. Ein zweiter Teil bietet eine serverseitige Variante mit PHP.
Inhaltsverzeichnis
HTML
Ein Monatskalender ist häufig eine Tabelle, da die Wochentage untereinander in einer Beziehung stehen. Bei einem Adventskalender sind die Türchen allerdings 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.
<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. Dezember
</a>
</li>
</ol>
Damit es innerhalb der Datumsangabe zu keinen Umbrüchen kommen kann, wird das Leerzeichen durch ein festes Leerzeichen
ersetzt.
CSS
Mit CSS wird nun die Anordnung und das Aussehen festgelegt.
Grid Layout
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:
ol {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(20rem, 1fr));
max-width: 120rem;
}
@media (min-width: 80rem) { /* 4×6 */
ol {
grid-template-columns: repeat(4, 1fr);
}
}
@media (min-width: 120rem) { /* 6×4 */
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 20rem – bei 39rem Viewportbreite würde es noch eine Spalte geben, bei 40rem dann automatisch zwei – bei 59em noch zwei, bei 60rem dann drei Spalten.
Dabei kommt uns zugute, dass die 24 recht viele Teiler besitzt:
1 × 24 2 × 12 3 × 8 4 × 6 5 × 4 Rest 4 6 × 4 7 × 3 Rest 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
Mit der CSS-Eigenschaft order können Sie die Reihenfolge innerhalb der Liste durcheinanderbringen.
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 zufällige Sortierung lässt sich mit JavaScript oder PHP erreichen.
Nummerierung
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:
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
Der Inhalt der Türchen, die noch nicht geöffnet werden können, soll hübsch zentriert sein,
li > .donate, li > .coming_soon {
margin: 0;
align-self: center;
text-align: center;
}
aber dennoch soll die ganze Fläche maussensitiv sein.
li > .donate::after, li > .coming_soon::after {
content: "";
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
}
Die Türchen sollen sich ansprechend öffnen und genauso ansprechend auch wieder schließen.
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?
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
Besuchte Türchen zeigen
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, kann auf diese Weise nicht zwischen besuchten und unbesuchten Türchen unterschieden werden.
Deshalb wird dies nun mit JavaScript nachgebaut:
'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
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
- Tutorial: Digitaler Adventskalender Türchen für Türchen (webkrauts.de von 2012)
- https://de.hideout-lastation.com/how-build-simple-advent-calendar-javascript
- https://www.html-seminar.de/forum/thread/6583-adventskalender/
static
abweichende Positionierung besitzt, nimmt das Pseudoelement den ganzen Platz des li-Elements ein, das schon wegen der Nummerierungrelative
positioniert ist.