Einführung
Moonroom ist eine kleine Ausstellung mit echten Fotografien der Apollo-Missionen. Die Sammlung ist vorbereitet, doch in der aktuellen Ausstellung fehlen noch die Fotokarten. Du erstellst eine aussagekräftige Karte, erweiterst sie anhand der Daten und machst die vollständige Galerie auf schmalen und breiten Bildschirmen lesbar. Die separate Study collection zeigt vor dem Start, wie die Betrachtung funktionieren soll.
Du benötigst Grundkenntnisse in HTML-Struktur, CSS-Selektoren und Layout sowie JavaScript-Objekten, Arrays und DOM-Ereignissen. Dieses Lab führt figure, figcaption, native Schaltflächen, stabile Foto-IDs, forEach, dataset und die hier verwendeten responsiven Regeln ein. Die Detailansicht ist in diesem ersten Lab vorbereitet; im nächsten Lab baust du ihr Verhalten selbst. Die Zeitangabe von 50 Minuten ist eine Schätzung des Autors einschließlich Lesen und gewöhnlicher Bearbeitungsfehler.
Warte nach dem Öffnen oder Neuladen einer Website-Seite, bis sie vollständig geladen ist, bevor du die Fotosteuerung bedienst. Bearbeite /home/labex/project/moonroom in WebIDE und verwende die Website in Web 8080. Bearbeite in diesem Lab nur index.html, gallery.js und styles.css. Erhalte photos.js, viewer.js, images/ und credits.html. Die bereitgestellten Bilddateien und Metadaten müssen ihren ursprünglichen IDs zugeordnet bleiben. Alle Beschriftungen der Website bleiben auf Englisch. Diese praktischen Tastaturprüfungen sind keine vollständige Zertifizierung der Barrierefreiheit.
Eine Fotografie mit Ihrer Bedeutung Verknüpfen
In diesem Schritt bedienst du eine funktionierende Ausstellung mit der Tastatur und baust anschließend die erste Karte deiner eigenen Ausstellung.
Wechsle zu Web 8080 und wähle den Seitenlink Study collection. Dies ist ein separat vorbereitetes Beispiel; seine Bearbeitung gehört nicht zu deinem Arbeitsbereich. Drücke Tab, bis die erste Fotoschaltfläche Earthrise einen sichtbaren Umriss zeigt, und dann Enter. Die Detailansicht zeigt die Erde über dem Mondhorizont. Drücke Esc: Der Fokus kehrt zu derselben Karte zurück. Beachte, dass die andere Earthrise-Karte ein anderes Foto und eine andere Bildunterschrift hat. Ein Titel ist eine Bezeichnung, keine eindeutige Identität.
Wähle den Seitenlink Current exhibition, um zu deiner eigenen Seite zurückzukehren. Dieser Link navigiert innerhalb der Website; er wechselt nicht den LabEx-Tab. Deine Seite enthält zunächst die Überschrift und Bildnachweise, aber keine Karten. Diesen unvollständigen Zustand wirst du verändern.
Wechsle zu WebIDE. Öffne im Explorer den Ordner moonroom unter /home/labex/project. Falls der Explorer ausgeblendet ist, wähle View → Explorer. Öffne photos.js. Das Array photos enthält ein Objekt pro Fotografie. Jedes Objekt hält diese zusammengehörigen Werte zusammen:
| Feld | Bedeutung |
|---|---|
id |
Stabile Identität, etwa earthrise-eight; zwei Titel dürfen gleich sein. |
thumbnail |
Eine echte, kleinere lokale Bildversion für die Karte. |
large |
Die dazugehörige größere Version für die Detailansicht. |
alt |
Aussagekräftiger Text, der das Bild für Menschen beschreibt, die es nicht sehen können. |
title und caption |
Sichtbarer Name und Kontext. |
Bearbeite diese Datei nicht. Im ersten Datensatz heißt die Miniatur images/earthrise-eight-thumb.jpg, das größere Bild images/earthrise-eight.jpg. Ein relativer Bildpfad wird von der HTML-Seite aus aufgelöst, nicht vom aktuellen Ordner des Editors. Groß- und Kleinschreibung sowie die Erweiterung .jpg sind wichtig.
Ein figure gruppiert ein Bild mit seiner figcaption. Die Schaltfläche macht das Öffnen des Fotos zu einer nativen Tastaturaktion: Sie kann über Tab den Fokus erhalten und mit Enter aktiviert werden. Ein div mit einem Mausklick-Handler würde diese Verhaltensweisen nicht automatisch bereitstellen. type="button" legt das beabsichtigte Schaltflächenverhalten ausdrücklich fest. Sichtbarer Titel und Bildunterschrift sind ohne Zeigerbewegung über das Element verfügbar. aria-label gibt der Schaltfläche den kurzen zugänglichen Namen Open Earthrise; alt beschreibt den Bildinhalt. Da zwei Fotografien denselben Namen haben, verknüpft aria-describedby die Schaltfläche zusätzlich über die eindeutige id mit ihrer Bildunterschrift. Die Bildunterschrift liefert unterscheidenden Kontext wie Apollo 8 oder Apollo 11. Ein zugänglicher Name identifiziert ein Bedienelement; eine zugeordnete Beschreibung ergänzt Kontext. So werden zwei gleich benannte Bedienelemente für jemanden, der ohne die Bilder navigiert, nicht ununterscheidbar. Ein Platzhalter oder Hintergrundbild würde diese Bildbeschreibung nicht liefern.
Das Attribut data-photo-card identifiziert den vollständigen Kartencontainer; data-photo-id identifiziert seine Schaltfläche zum Öffnen. Beide verwenden die stabile ID des Datensatzes. Diese kleinen Verknüpfungsattribute sorgen dafür, dass Daten, Karten und Detailansicht dasselbe Foto bezeichnen; sie sind kein sichtbarer Text. Eine Karte darf zusätzliche umschließende Elemente haben, aber ihre Schaltfläche und Bildunterschrift müssen zum nächstgelegenen Container mit passendem data-photo-card gehören.
Öffne index.html, markiere mit Ctrl+A oder Cmd+A den gesamten Text und ersetze ihn durch das vollständige HTML unten. Behalte die script-Zeilen bei: defer führt diese Skripte nach dem Einlesen des HTML in der angegebenen Reihenfolge aus. photos.js liefert Daten, gallery.js wird im nächsten Schritt die Sammlung erzeugen und das vorbereitete viewer.js übernimmt in diesem Lab die Detailansicht. Das dialog-Markup ist das gerade ausprobierte Fenster; sein Verhalten wird im nächsten Lab erklärt und aufgebaut.
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Moonroom — A little farther from home</title>
<link rel="stylesheet" href="styles.css">
<script src="photos.js" defer></script>
<script src="gallery.js" defer></script>
<script src="viewer.js" defer></script>
</head>
<body>
<main>
<header>
<nav aria-label="Exhibitions"><a href="/">Current exhibition</a> · <a href="/example/">Study collection</a></nav>
<p class="eyebrow">Moonroom / Photographic studies</p>
<h1>A little farther from home.</h1>
<p class="intro">Three moments from the Apollo archive. Take a closer look.</p>
</header>
<section id="gallery" class="photo-grid" aria-label="Photographs">
<figure data-photo-card="earthrise-eight">
<button type="button" class="photo-button" data-photo-id="earthrise-eight" aria-label="Open Earthrise" aria-describedby="caption-earthrise-eight">
<img src="images/earthrise-eight-thumb.jpg" alt="Blue and white Earth above the grey lunar horizon">
<span class="photo-name">Earthrise</span>
</button>
<figcaption id="caption-earthrise-eight">Apollo 8 · Earth above the Moon, December 1968.</figcaption>
</figure>
</section>
<footer>Photographs: NASA. An independent educational exhibition. <a href="credits.html">Image credits</a></footer>
</main>
<dialog id="viewer" aria-labelledby="photo-title" aria-describedby="photo-caption">
<button id="close" type="button" autofocus>Close</button>
<h2 id="photo-title"></h2>
<img id="large-photo" alt="">
<p id="photo-error" role="status"></p>
<p id="photo-caption"></p>
<nav aria-label="Photo navigation">
<button id="previous" type="button">Previous</button>
<p id="photo-position"></p>
<button id="next" type="button">Next</button>
</nav>
</dialog>
</body>
</html>
Speichere mit Ctrl+S oder Cmd+S. Wechsle zu Web 8080, wähle gegebenenfalls Current exhibition und lade diese Website-Seite neu. Du solltest eine Earthrise-Karte, ihr echtes Bild und die Apollo-8-Bildunterschrift sehen. Das Layout ist noch schlicht; du gestaltest es später. Erreiche die Karte mit Tab, öffne sie mit Enter und kehre mit Esc zurück. Das große Bild sollte dieselbe Szene mit mehr Details zeigen.
Wenn das Bild nicht geladen wird oder die Seite leer ist, kehre zu WebIDE zurück. Prüfe, ob die Datei index.html heißt, in moonroom gespeichert ist und src, Anführungszeichen und schließende Tags dem Beispiel entsprechen. Öffne nach dem Speichern erneut Current exhibition und lade die Seite neu. Setze die Umgebung nicht zurück und ersetze das Foto nicht durch eine andere Datei, um einen falschen Pfad zu verstecken. Du kannst die gesamte Datei durch erneutes Kopieren des vollständigen HTML wiederherstellen.
Führe die Prüfung dieses Schritts aus, nachdem die echte Karte sichtbar ist. Sie prüft deine aktuelle Ausstellung, niemals das separate Beispiel.
Alle Datensätze ohne Identitätsverlust Darstellen
In diesem Schritt ersetzt du die einzelne, von Hand geschriebene Karte durch Karten, die aus dem vollständigen Array erzeugt werden.
Bleibe in WebIDE und öffne photos.js zum Vergleich erneut. Der erste und dritte Datensatz heißen beide Earthrise, aber ihre IDs, Bildpfade und Bildunterschriften unterscheiden sich. Eine Suche nach dem Titel würde diese Unterscheidung verlieren. Die fertige Galerie muss für jede der drei vorgegebenen IDs genau eine sichtbare Schaltfläche zum Öffnen enthalten, jeweils mit passender Miniatur, Titel, Alternativtext und Bildunterschrift. Behalte Reihenfolge und Daten bei.
Öffne nun gallery.js. document.querySelector('#gallery') wählt das Element mit der id gallery aus. replaceChildren() leert dieses Element und entfernt die vorübergehend von Hand geschriebene Karte, damit sie nicht doppelt erscheint. photos.forEach(function (photo) { ... }) ruft die enthaltene Funktion der Reihe nach einmal pro Array-Datensatz auf; photo ist das aktuelle Objekt. Das entspricht der Idee wiederholter Arbeit in einer Schleife, wobei jeder Datensatz an die Funktion übergeben wird.
document.createElement() erzeugt ein echtes HTML-Element im Speicher. Eine Zuweisung an textContent fügt Text ein, statt ihn als HTML zu interpretieren. append() fügt die vorbereiteten Elemente in der angegebenen Reihenfolge ein. Der Browser sieht dieselben Beziehungen wie bei deinem ersten figure, der Schaltfläche, dem Bild und der Bildunterschrift.
dataset liest und schreibt data--Attribute. JavaScript verwendet Camel Case: figure.dataset.photoCard schreibt data-photo-card, und button.dataset.photoId schreibt data-photo-id. Beide erhalten photo.id, während der angezeigte Titel photo.title erhält. Jede Bildunterschrift erhält außerdem eine eindeutige id, indem caption- mit photo.id verbunden wird; aria-describedby der Schaltfläche verweist darauf. Die sichtbare Bildunterschrift bleibt außerhalb der Schaltfläche, ist ihr aber zugeordnet. setAttribute() setzt ein Attribut wie aria-label; der Operator + verbindet den Text Open mit dem Titel. Eine Syntax für Zeichenkettenvorlagen wird nicht benötigt.
Ersetze den gesamten Inhalt von gallery.js durch diesen vollständigen Code und speichere:
const gallery = document.querySelector('#gallery');
gallery.replaceChildren();
photos.forEach(function (photo) {
const figure = document.createElement('figure');
figure.dataset.photoCard = photo.id;
const button = document.createElement('button');
button.type = 'button';
button.className = 'photo-button';
button.dataset.photoId = photo.id;
button.setAttribute('aria-label', 'Open ' + photo.title);
const image = document.createElement('img');
image.src = photo.thumbnail;
image.alt = photo.alt;
const title = document.createElement('span');
title.className = 'photo-name';
title.textContent = photo.title;
const caption = document.createElement('figcaption');
caption.id = 'caption-' + photo.id;
button.setAttribute('aria-describedby', caption.id);
caption.textContent = photo.caption;
button.append(image, title);
figure.append(button, caption);
gallery.append(figure);
});
Wechsle zu Web 8080 und lade Current exhibition neu. Du solltest Earthrise, A trace of a visitor und den zweiten Earthrise in dieser Reihenfolge sehen. Jede Bildunterschrift bleibt unter ihrer Schaltfläche sichtbar. Der Stiefelabdruck ist ein Hochformatfoto; die Earthrise-Bilder sind quadratisch. Die Galerie wird ihre Miniaturen bald einheitlich einrahmen, während die Detailansicht die unterschiedlichen Proportionen erhält.
Öffne jede Karte einzeln mit Tab und Enter. Der erste Earthrise hat die Apollo-8-Bildunterschrift, der letzte die Apollo-11-Bildunterschrift. Drücke jeweils Esc und prüfe, ob der Fokus zur verwendeten Karte zurückkehrt, auch zur letzten Karte statt immer zur ersten.
Wenn nur eine Karte bleibt, ist das Skript möglicherweise nicht gespeichert, enthält einen Syntaxfehler oder liegt im falschen Ordner. Kehre zu WebIDE zurück, vergleiche gallery.js mit dem vollständigen Block und prüfe das abschließende }); am Ende von forEach. Ändere photos.js nicht so, dass seine Datensätze gleich aussehen. Speichere, lade die Website neu und vergleiche die Karten erneut, bevor du die Prüfung startest.
Das Raster Anpassen und den Tastaturfokus Anzeigen
In diesem Schritt machst du die vollständige Ausstellung auf einem schmalen Bildschirm lesbar und die fokussierte Karte leicht erkennbar.
Kehre zu WebIDE zurück und öffne styles.css. Ein responsives Layout passt sich dem verfügbaren Platz an, statt eine feste Seitenbreite zu erzwingen. Dieses Stylesheet verwendet Flexbox, das du aus den CSS-Grundlagen kennst. display: flex ordnet die Karten in einer Zeile an; flex-wrap: wrap erlaubt einen Zeilenumbruch; gap lässt Abstand zwischen den Karten. flex: 1 1 260px erlaubt einem figure zu wachsen und zu schrumpfen, mit 260 Pixeln als bevorzugter Ausgangsbreite. min-width: 0 lässt lange Inhalte innerhalb einer Karte schrumpfen, statt die Seite zu verbreitern.
Das Kartenbild verwendet width: 100% und aspect-ratio: 4 / 3 für einen einheitlichen Rahmen. object-fit: cover füllt diesen Miniaturrahmen durch Abschneiden der angezeigten Ränder; die Datei wird nicht geändert. Das Detailbild verwendet dagegen object-fit: contain, max-width und max-height, damit das ganze Bild in seinen eigenen Proportionen sichtbar bleibt. vw und vh sind Hundertstel der Breite und Höhe des Ansichtsbereichs. min() begrenzt eine Größe, während clamp() die Überschrift zwischen einem Mindest- und einem Höchstwert wachsen lässt. overflow-wrap: anywhere erlaubt einen Umbruch sehr langer Titel, statt die Seite zu verbreitern.
:focus-visible wählt ein Bedienelement aus, wenn sein Tastaturfokus angezeigt werden soll. Ein kontrastreicher Umriss und outline-offset machen die aktuelle Schaltfläche sichtbar, ohne das Layout zu verschieben. Entferne den Browserumriss nicht, ohne einen anderen eindeutigen Hinweis anzubieten. Dieses Projekt akzeptiert einen deutlichen Fokusumriss von mindestens 2 Pixeln, einen deutlichen sichtbaren Rahmen von mindestens 2 Pixeln oder einen sichtbaren Schatten, der beim Erhalt des Tastaturfokus erscheint. Farben und Layout dürfen geändert werden, solange diese beobachtbaren Verhaltensweisen erhalten bleiben.
Die unteren Regeln erhalten die vorbereitete Detailansicht: ::backdrop gestaltet den Hintergrund hinter einem modalen Dialog, :disabled kennzeichnet eine Grenzschaltfläche optisch und [hidden] muss ein fehlgeschlagenes Bild tatsächlich ausblenden, obwohl normale Bilder der Detailansicht display: block verwenden. Die Medienabfrage für reduzierte Bewegung berücksichtigt die Bewegungseinstellung des Browsers; diese Ausstellung enthält keine animierten Übergänge.
Ersetze den gesamten Inhalt von styles.css durch das folgende vollständige Stylesheet und speichere:
* { box-sizing: border-box; }
body { margin: 0; background: #f5f1e8; color: #252c28; font: 17px/1.6 system-ui, sans-serif; }
main { max-width: 1160px; margin: auto; padding: 40px 24px; }
header { max-width: 760px; margin-bottom: 40px; }
.eyebrow { color: #6e593f; font-size: 12px; letter-spacing: .16em; text-transform: uppercase; }
h1 { margin: 12px 0; font: clamp(2.5rem, 6vw, 5rem)/1.08 Georgia, serif; }
.intro { max-width: 40rem; color: #5c655e; }
.photo-grid { display: flex; flex-wrap: wrap; gap: 28px; }
figure { flex: 1 1 260px; min-width: 0; margin: 0; }
button { color: inherit; font: inherit; cursor: pointer; }
.photo-button { width: 100%; padding: 0; text-align: left; background: transparent; border: 0; }
.photo-button img { display: block; width: 100%; aspect-ratio: 4 / 3; object-fit: cover; border-radius: 4px; }
.photo-name { display: block; margin-top: 12px; font: 1.5rem/1.3 Georgia, serif; overflow-wrap: anywhere; }
figcaption { margin-top: 6px; color: #5c655e; font-size: 14px; }
a { color: #814820; text-underline-offset: 4px; }
button:focus-visible, a:focus-visible { outline: 3px solid #af421f; outline-offset: 5px; }
footer { margin-top: 48px; border-top: 1px solid #d1c8b8; padding-top: 18px; color: #5c655e; font-size: 13px; }
dialog { width: min(880px, 94vw); max-height: 94vh; overflow: auto; padding: 24px; border: 0; border-radius: 8px; background: #fffcf6; color: #252c28; }
dialog::backdrop { background: #19251fe6; }
dialog h2 { font: 1.8rem/1.3 Georgia, serif; overflow-wrap: anywhere; }
dialog img { display: block; max-width: 100%; max-height: 52vh; margin: auto; object-fit: contain; }
dialog img[hidden] { display: none; }
dialog button { padding: 8px 14px; border: 1px solid #6c746b; border-radius: 4px; background: #fffcf6; }
dialog nav { display: flex; align-items: center; justify-content: space-between; gap: 12px; }
dialog button:disabled { opacity: .45; cursor: default; }
#photo-position { white-space: nowrap; }
#photo-error { color: #9b301c; }
#photo-error:empty { display: none; }
@media (prefers-reduced-motion: reduce) { * { scroll-behavior: auto; } }
Wechsle zu Web 8080 und lade Current exhibition neu. Bei einem breiten Ansichtsbereich teilen die Karten eine Zeile; bei einem schmalen werden sie in einer lesbaren Spalte gestapelt. Ändere die Breite des Browsers oder Website-Panels auf ungefähr 390 Pixel und verbreitere es danach wieder. Es darf kein horizontales Scrollen der Seite, keine abgeschnittenen Titel und keine fehlende Karte geben. Die echte größere Fotografie muss beim Öffnen ihr Hochformat oder ihre quadratische Form behalten.
Drücke von der Seite aus wiederholt Tab. Die Website-Links und alle drei Fotoschaltflächen müssen einen sichtbaren Fokusumriss zeigen. Enter öffnet das fokussierte Foto; Esc kehrt zu ihm zurück. Keine Beschreibung darf ein Darüberfahren mit der Maus erfordern. Öffne Image credits und prüfe, ob die echte Seite die NASA-Bildquellen auflistet; wähle danach Current exhibition, um zurückzukehren. Dies sind Website-Links und nicht der Wechsel zwischen WebIDE und Web 8080.
Wenn das Layout schlicht bleibt, prüfe den gespeicherten Dateinamen styles.css und ob index.html weiterhin darauf verweist. Ist eine Karte breiter als die Seite, prüfe min-width: 0, width: 100% auf ihrem Bild und die Flex-Umbruchregeln. Stelle bei Bedarf das vollständige Stylesheet wieder her; behalte jedes Foto, statt ein schwieriges auszublenden. Führe die abschließende Prüfung aus, nachdem du beide Bildschirmgrößen und den Tastaturweg kontrolliert hast.

Vergleiche in dieser abschließenden Breitbildansicht die Bildunterschriften des ersten und letzten Earthrise: Die Fotografien stammen aus unterschiedlichen Missionen. Der Stiefelabdruck bleibt eine eigenständige mittlere Karte. Dieselben drei Karten werden auf einem schmalen Bildschirm gestapelt.
Zusammenfassung
Du hast ein echtes Bild mit seiner Beschreibung verbunden, eine semantische Karte zu einer vollständigen datengesteuerten Sammlung erweitert und zwei gleich betitelte Fotografien durch stabile IDs unterschieden. Dein responsives Raster erhält alle Karten, echte Bildinhalte und den sichtbaren Tastaturfokus. Im nächsten Lab baust du die Detailansicht selbst, einschließlich Navigationsgrenzen und eines behebbaren Bildfehlers.
