Eine Pokédex-Webanwendung mit Vanilla HTML, CSS und JavaScript – ohne Framework und ohne Build-Schritt. Die Daten kommen live von der PokéAPI.
Live-Demo: https://noubou1.github.io/Pokedex/
- Features
- Technologien
- Lokal starten
- Projektstruktur
- Architektur
- Was ich dabei gelernt habe
- Ideen für Erweiterungen
- Lizenz
- Kartenübersicht – 20 Pokémon pro Ladeschritt, nachladbar per Button
- Detail-Dialog – natives
<dialog>-Element mit drei Tabs (Main, Stats, Artwork) - Base Stats – visuelle Fortschrittsbalken pro Statuswert
- Typ-basiertes Design – Kartenfarbe und Icons richten sich nach dem Primärtyp
- Live-Suche – filtert die geladenen Pokémon ab drei Zeichen, mit Enter-Support
- Blättern im Dialog – vor und zurück durch die Liste, inklusive Umbruch an den Enden
- Responsive – ein Layout für Desktop und Mobil
- Tastatur- und Screenreader-tauglich – sichtbarer Fokus, ARIA-Labels,
Escschließt den Dialog - Fehler- und Leerzustände – bei API-Fehlern und ergebnisloser Suche erscheint eine Meldung
| Technologie | Einsatz |
|---|---|
| HTML5 | semantisches Markup, natives <dialog> |
| CSS3 | Flexbox, Grid, Custom Properties, modular je Komponente |
| JavaScript (ES2021+) | ES-Module, async/await, Event-Delegation |
| PokéAPI | Datenquelle |
Keine Abhängigkeiten, kein npm install, kein Bundler.
Wichtig: Das Projekt nutzt ES-Module. Ein Öffnen per Doppelklick (
file://) funktioniert deshalb nicht – der Browser blockiert Modul-Imports über das Dateiprotokoll. Es braucht einen lokalen HTTP-Server.
git clone https://github.com/NouBou1/Pokedex.git
cd PokedexDanach eine der folgenden Varianten:
# Variante A: Node.js
npx http-server -p 8000
# Variante B: Python
python -m http.server 8000Variante C: In VS Code die Erweiterung Live Server installieren und „Go Live" klicken.
Anschließend http://localhost:8000 öffnen.
Pokedex/
├── index.html # Entry Point
├── style.css # Basis-Layout, Header, Helper-Klassen
├── css/
│ ├── button.css # Buttons und Fokus-Styles
│ ├── card.css # Kartenliste und Karte
│ ├── dialog.css # Detail-Dialog und Tabs
│ ├── stats.css # Stat-Balken
│ └── loader.css # Ladeanzeige
├── js/
│ ├── main.js # Entry Point: Listener und Startladung
│ ├── config.js # Konstanten, Typ- und Stat-Palette
│ ├── state.js # App-Zustand (geladene Pokémon, Offset)
│ ├── api.js # PokéAPI-Requests
│ ├── format.js # Rohdaten → anzeigbare Werte
│ ├── render.js # DOM-Ausgabe
│ ├── dialog.js # Verhalten des Detail-Dialogs
│ ├── search.js # Namenssuche
│ └── templates/
│ └── template.js # HTML-Templates
├── assets/
│ ├── icons/ # Typ- und Button-Icons
│ └── img/ # Logo, Hintergrund, Screenshot
└── fonts/
└── pokemon-classic.ttf
Jedes Modul hat genau eine Aufgabe, die Abhängigkeiten laufen nur in eine Richtung:
main.js ──► api.js ──► config.js
│
├──► search.js ──┐
├──► dialog.js ──┼──► render.js ──► templates/template.js ──► format.js
│ │ │
└────────────────┴──► state.js
state.jsist die einzige Quelle der Wahrheit für die geladenen Pokémon. Kein Modul hält eine eigene Kopie der Liste.templates/template.jsenthält ausschließlich reine Funktionen, die HTML-Strings zurückgeben – kein DOM-Zugriff, kein Zustand.render.jsist die einzige Stelle, die in das DOM schreibt.- Event-Delegation: Ein Listener auf der Kartenliste und einer auf dem Dialog bedienen alle – auch nachgeladene – Elemente. Im Markup steht kein einziges
onclick.
main.js: loadNextPage()
└─► api.js: fetchPokemonPage(offset) Index-Request, dann alle
│ Detail-Requests via Promise.all
└─► state.js: appendPokemon(page)
└─► render.js: renderAllPokemonCards()
└─► template.js: getPokemonCardTemplate()
GET https://pokeapi.co/api/v2/pokemon?limit=20&offset=0 # Liste
GET https://pokeapi.co/api/v2/pokemon/{id} # Details
- Paralleles Laden: Die Detail-Requests einer Seite laufen über
Promise.allgleichzeitig statt sequenziell in einerawait-Schleife. Das war der größte einzelne Performance-Gewinn im Projekt. - Das native
<dialog>-Element:showModal()bringt Fokus-Falle und Backdrop mit. Wichtig ist, auf dasclose-Event zu hören statt nur auf den Close-Button – sonst bleibt beim Schließen perEscder Scroll-Lock hängen. - Event-Delegation statt Inline-Handler: Bei nachgeladenen Karten müssen so keine Listener neu gebunden werden.
- API-Einheiten prüfen: Die PokéAPI liefert Gewicht in Hektogramm und Größe in Dezimetern. Ungeprüft übernommen wiegt Bisasam 69 kg statt 6,9 kg.
- Defensiv gegen fehlende Daten: Viele Pokémon haben kein
home- oderdream_world-Sprite. Ohne Fallback entstehen kaputte Bildplatzhalter.
- Suche über den vollständigen Pokédex statt nur über die geladenen Einträge
- Filter nach Typ und Generation
- Favoriten via LocalStorage
- Evolutions-Ketten im Detail-Dialog
- Vergleichsmodus für zwei Pokémon
Der Code steht unter der MIT-Lizenz.
Pokémon sowie alle Namen, Bilder und Charaktere sind Eigentum von Nintendo, Game Freak und The Pokémon Company. Dieses Projekt ist ein nicht-kommerzielles Lernprojekt und steht in keiner Verbindung zu diesen Unternehmen. Die Daten stammen aus der PokéAPI, deren Fair-Use-Richtlinie gilt.
Noureddin Boussaada – @NouBou1
