PHP/Tutorials/Adventskalender

Aus SELFHTML-Wiki
< PHP‎ | Tutorials
Wechseln zu: Navigation, Suche

Der gestern vorgestellte Adventskalender soll nun in einer Komfort-Variante so umgebaut werden, dass man die Dateien nur einmal hochladen muss. Dabei werden die Türchen stets am richtigen Tag freigeschaltet.

HTML und CSS

HTML

Der Adventskalender besteht aus einer HTML-Datei. Im Unterschied zur gestrigen Variante ist diese nun in einer index.php-Datei integriert:

index.php (Ausschnitt)
<!DOCTYPE html>
<html lang="de">
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title><?=$title?></title>
    <style><?php include('css.php') ?></style>
    <script><?php include('js.php') ?></script>
  </head>
  <body>
    <h1><a href="https://selfhtml.org"><?=$title?></a></h1>
    <ol> <?php 
      for ($k = 0; $k < sizeof($listitems); $k++) : ?>
        <li><?=$listitems[$k]?></li> <?php
      endfor ?>
    </ol>
  </body>
</html>

Dabei fällt auf, dass der HTML-Abschnitt nur aus dem Gerüst besteht:

  • Veränderbare Inhalte wie die Überschrift und der Titel werden mit Platzhaltern <?=$title?> versehen und dynamisch gefüllt.
  • Externe Ressourcen wie das Stylesheet oder das JavaScript werden mit <?php include('css.php') ?> nachgeladen.
  • Die Listenelemente des Kalenders werden dynamisch erstellt und die Inhalte der Türchen über eine externe Datei data.php, die mit require() geladen wird, gefüllt.

Mischen der Türchen

Auf den ersten Blick mag es komisch erscheinen, eine CSS-Datei als css.php einzubinden. ein Blick in den Quellcode zeigt aber, dass neben dem reinen CSS auch eine PHP-Schleife die Position der Türchen bestimmt. Anstelle der vielen manuellen Festlegungen mit order kann PHP die Reihenfolge ebenso hübsch, aber völlig zufällig durcheinanderbringen.

css.php (Ausschnitt)
<?php
$order = range(1, 24);
shuffle($order); 
$orderstring = implode(',',$order); 

for ($k = 0; $k < 24; $k++) : ?>
  li:nth-of-type(<?=$k+1?>) {
    order: <?=$order[$k]?>;
} <?php
endfor ?>
Den Inhalt der Variablen $orderstring speichern wir später im localStorage, damit man nicht jeden Tag mit einer neuen Reihenfolge der Kästchen konfrontiert wird. In der Muggelwelt sind solche Kalender eher selten.

Die Daten

Nachdem wir uns über die HTML-Struktur geeinigt haben (wir verwenden eine geordnete Liste von Links), geht es jetzt darum, die Daten für die Kalendertürchen bereitzustellen. Wir brauchen für jeden Tag ein Linkziel, eine Überschrift, den Autor und einen Teaser. Da sich das für jeden Tag wiederholt, verwenden wir ein Array, bei dem jedes der 24 Array-Elemente aus einem Array mit den 4 Elementen für Linkziel, Überschrift, Autor und Teaser besteht.

Datenstruktur der Kalendereinträge
$data = [
  1 => [
    'href' => 'https://forum.selfhtml.org/m17175430',
    'heading' => 'SVG skalieren',
    'author' => 'Gunnar Bittersmann am 13.11.2019',
    'teaser' => 'Ich hab mal ein Beispiel gebaut, ...',
  ],
// ...
  24 => [
    'href' => 'https://example.com',
    'heading' => 'Überschrift',
    'author' => 'Autor',
    'teaser' => 'Teaser',
  ],
];

Da sich die Türchen nicht vorzeitig öffnen dürfen, brauchen wir noch einen Hinweistext für die ganz ungeduldigen.

Beispiel
$default = 'Dieses Türchen öffnet sich erst am ##date##.&nbsp;Dezember.';
Die Zeichenfolge ##date## wird bei der Scriptausführung durch den dazugehörenden Tag ersetzt.

Ein bisschen Werbung kann man den ganz ungeduldigen (die, die immer in die 24 luschern wollen) auch zumuten:

Beispiel
$donate = [
  'href' => 'https://selfhtml.org/spenden.html',
  'heading' => 'Es ist noch zu früh.',
  'text' => 'Wussten Sie, dass Sie <span class="self">self</span>HTML unterstützen können?',
];
Der Inhalt des Span-Elements wird durch text-transform in die übliche Schreibweise umgewandelt. Damit wird für Nutzer assisitiver Technologien tatsächlich die Silbe self und nicht ES - E - EL - EF vorgelesen.

Nun haben wir alles bereit, um zunächst das Array der Listenelemente zusammenzustellen.

Beispiel

index.php (Ausschnitt)

Zum Abschluss ein kleines Quiz

Was verbirgt sich hinter dem 15. Türchen am

  • 14. November 2019,
  • 14. Dezember 2019,
  • 14. Januar 2020?

Komfort mit JavaScript

Im ersten Teil haben wir mithilfe von PHP eine zufällige Reihenfolge der Kalendertürchen erzeugt. Natürlich wird bei jedem Aufruf der Seite eine neue zufällige Reihenfolge generiert. Um unsere Besucher nicht in den Wahnsinn zu treiben, müssen wir also dafür sorgen, dass die aktuelle Konfiguration des Kalenders (dazu zählen neben der erstmaligen Reihenfolge auch die Status der Kalendertürchen) im Browser gespeichert bleibt. Dafür bedienen wir uns des localStorage. Gut, wer JavaScript deaktiviert hat, muss halt jeden Tag neu suchen.

Beim Aufruf der Seite wird geprüft, ob das localStorage-Objekt existiert. Falls es den Eintrag SELForder nicht gibt, wird er angelegt und mit der (durch PHP erzeugten) zufälligen Reihenfolge befüllt. Das setzt voraus, dass die JavaScript-Ressource ebenso wie die CSS-Datei dynamisch durch PHP erzeugt werden muss.

Beispiel
if (!localStorage.getItem('SELFOrder')) {
  localStorage.setItem('SELFOrder','<?=$orderstring?>');
}

Falls es den Eintrag gibt, wird die gespeicherte Reihenfolge (die als kommaseparierte Liste von Zahlen vorliegt) ausgelesen, aufgeteilt und den einzelnen Kalendertürchen zugewiesen.

Beispiel
else { 
  const SELForder = localStorage.getItem('SELFOrder').split(',');
  for (let k = 1; k < 25; k++) {
    document.querySelector('li:nth-child('+k+')').style.cssText = 'order: ' + SELForder[k-1];
  }
}

Außerdem lesen wir noch aus, welche Türchen schon geöffnet sind

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

und registrieren einen Eventlistener auf dem Kalender, damit jedes neu geöffnete Türchen auch flugs gespeichert werden kann.

Beispiel
let calendar = document.querySelector('ol');
calendar.addEventListener('click', saveTheDoor);

Bei Aktivierung eines Links im Kalender wird die Funktion saveTheDoor aufgerufen. Wir registrieren hier nicht für jedes Türchen einen eigenen Eventhandler, sondern machen uns Eventdelegation zu Nutze.

Beispiel
function saveTheDoor (event) {
  const elem = event.target,
        link = elem.closest('a'),
        listitem = link.parentElement,
        day = Array.prototype.indexOf.call(calendar.children, listitem) + 1;
event.target liefert das Element, auf dem die Aktion (beispielsweise der Mausklick) erfolgte, das kann auch das Span-Element mit dem Inhalt self sein. Mit elem.closest('a') erhalten wir das a-Element, innerhalb dessen die Aktion erfolgte, listitem liefert das Listenelement und in day steckt die Nummer des geklickten Listenelements

Die Klasse open zu setzen und das Türchen als geöffnet im localStorage zu speichern, ist dann wirklich nicht schwer.

Beispiel
  if (link.classList.length == 0) {
    link.classList.add('open');
    if (localStorage) {
      localStorage.setItem('SELFday'+day,'open');
    }
  }
}
Die Klasse open wird nur hinzugefügt, wenn das Linkelement noch keine Klasse besitzt. So werden die Türchen, die sich noch nicht öffnen lassen, nicht verändert.

Das komplette Projekt

Das gesamte Projekt finden Sie unter https://github.com/MatthiasApsel/Advent.