Projektziel: Die Lernkartei ist ein Vokabeltrainer im Browser.
Man wählt ein Sprachpaket, die Karten werden gemischt und lassen sich per Klick
umdrehen: Vorne steht das deutsche Wort mit einem Beispielsatz, hinten die
Übersetzung mit Beispielsatz. Es gibt drei Sprachpakete: Deutsch - Englisch,
Deutsch - Französisch und Französisch - Grammatik.
Die erste Version (August 2024) war eine einzelne PHP-Datei. Die Anwendung wurde
inzwischen neu aufgebaut und ist am 20.09.2026 von micha-it.de auf diese Seite umgezogen.
Dieser Beitrag beschreibt die neue Version.
Bild 1 - Lernkartei mit Sprachpaket Deutsch - Englisch, zwei Karten sind umgedreht
Projektlink:
Lernkartei
Was sich gegenüber der ersten Version geändert hat
-
Drei Dateien statt einer:
lernkartei.php (Aufbau der Seite),
lernkartei.css (Darstellung) und lernkartei.js (Logik).
-
Jede Karte ist eine Schaltfläche (
button). Dadurch lassen sich die Karten
auch mit der Tastatur bedienen, und aria-pressed zeigt an, ob eine Karte
umgedreht ist.
- Beispielsätze auf Vorder- und Rückseite.
-
"Neu mischen" bringt die Karten in eine zufällige Reihenfolge, eine Statuszeile zeigt
den Fortschritt (zum Beispiel "2 / 70 aufgedeckt").
-
Das zuletzt gewählte Sprachpaket wird im Browser gemerkt (
localStorage).
- Kann ein Sprachpaket nicht geladen werden, erscheint eine Fehlermeldung.
- Neu: das dritte Sprachpaket Französisch - Grammatik.
Sprachpakete als JSON
Jedes Sprachpaket ist eine JSON-Datei mit einer Liste von Karten. Jede Karte hat das
deutsche Wort (de), die Übersetzung (en oder fr)
und je einen Beispielsatz (deEx, enEx oder frEx).
Ein neues Sprachpaket ist damit nur eine weitere Datei.
// data1.json (Deutsch - Englisch)
{
"id": 0,
"de": "das Haus",
"en": "house",
"deEx": "Das Haus ist groß.",
"enEx": "The house is big."
}
// data2.json (Deutsch - Französisch)
{
"id": 1,
"de": "der Baum",
"fr": "l'arbre",
"deEx": "Der Baum ist hoch.",
"frEx": "L'arbre est haut."
}
Der JavaScript-Teil
Welche Felder gehören zu welchem Sprachpaket:
getLanguageMeta liefert zur gewählten Datei die Beschriftung der Rückseite
und die Namen der Felder, aus denen Übersetzung und Beispielsatz gelesen werden.
function getLanguageMeta(fileName) {
if (fileName === 'data3.json') {
return { label: 'Französisch - Grammatik', key: 'fr', exampleKey: 'frEx' };
}
if (fileName === 'data2.json') {
return { label: 'Deutsch - Französisch', key: 'fr', exampleKey: 'frEx' };
}
return { label: 'Deutsch - Englisch', key: 'en', exampleKey: 'enEx' };
}
Karten mischen:
Das Mischen folgt dem Fisher-Yates-Verfahren: Von hinten nach vorne wird jede Karte mit
einer zufällig gewählten Karte aus dem Bereich bis zu ihrer eigenen Position getauscht.
Das Original-Array bleibt unverändert, gemischt wird eine Kopie.
function shuffleCards(cards) {
const shuffled = [...cards];
for (let index = shuffled.length - 1; index > 0; index -= 1) {
const randomIndex = Math.floor(Math.random() * (index + 1));
[shuffled[index], shuffled[randomIndex]] = [shuffled[randomIndex], shuffled[index]];
}
return shuffled;
}
Eine Karte erzeugen:
Die Karte besteht aus einer Schaltfläche mit Vorder- und Rückseite. Ein Klick schaltet
die Klasse is-flipped um, das CSS dreht die Karte, und der Fortschritt
wird neu berechnet.
function createCardElement(cardData, meta) {
const article = document.createElement('article');
article.className = 'flashcard';
article.dataset.id = String(cardData.id);
const button = document.createElement('button');
button.type = 'button';
button.className = 'flashcard-button';
button.setAttribute('aria-pressed', 'false');
button.setAttribute('aria-label', `Karte ${cardData.de} umdrehen`);
button.innerHTML = `
<div class="flashcard-inner">
<div class="flashcard-face flashcard-front">
<div class="flashcard-label">Vorderseite</div>
<div class="flashcard-word">${cardData.de}</div>
<div class="flashcard-example">${cardData.deEx ?? ''}</div>
<div class="flashcard-hint">Klicken zum Aufdecken</div>
</div>
<div class="flashcard-face flashcard-back">
<div class="flashcard-label">${meta.label}</div>
<div class="flashcard-word">${cardData[meta.key] ?? ''}</div>
<div class="flashcard-example">${cardData[meta.exampleKey] ?? ''}</div>
<div class="flashcard-hint">Klicken zum Zurückdrehen</div>
</div>
</div>
`;
button.addEventListener('click', () => {
const isFlipped = article.classList.toggle('is-flipped');
button.setAttribute('aria-pressed', String(isFlipped));
if (isFlipped) {
openCards.add(cardData.id);
} else {
openCards.delete(cardData.id);
}
updateProgress();
});
article.appendChild(button);
return article;
}
Sprachpaket laden:
loadCards merkt sich die Auswahl, lädt die JSON-Datei mit der Fetch-API,
mischt auf Wunsch und baut die Karten auf. Schlägt das Laden fehl, wird die
Fehlermeldung angezeigt.
async function loadCards({ shuffle = true } = {}) {
clearError();
const selectedFile = languageSelect.value;
localStorage.setItem(STORAGE_KEY, selectedFile);
try {
const response = await fetch(selectedFile, { cache: 'no-store' });
if (!response.ok) {
throw new Error(`Datei konnte nicht geladen werden: ${response.status}`);
}
const data = await response.json();
currentCards = shuffle ? shuffleCards(data) : [...data];
renderCards(currentCards);
} catch (error) {
currentCards = [];
cardContainer.innerHTML = '';
statusText.textContent = 'Fehler beim Laden';
progressText.textContent = '0 / 0 aufgedeckt';
showError('Das Sprachpaket konnte nicht geladen werden.');
console.error(error);
}
}
Alle Karten zurücksetzen:
Alle Karten werden wieder auf die Vorderseite gedreht, der Fortschritt beginnt bei null.
function resetCards() {
document.querySelectorAll('.flashcard').forEach((card) => {
card.classList.remove('is-flipped');
});
openCards = new Set();
updateProgress();
}
Zusammen ergibt das eine kleine, gut lesbare Lernumgebung, in der sich Sprachpakete
beliebig ergänzen lassen.
Quelle:
Die erste Version entstand nach dem Tutorial
01 - Flash Card World - JSON +JavaScript + CSS Tutorial.