Japanese Kanji Quiz Website

Build a dark-themed Kanji quiz with image fallback and score tracking.

Introduction

30 Second Summary

Learning Japanese Kanji is tough because there are thousands of characters to memorize. Flashcard apps help, but they never quite match the specific vocabulary you're actually studying.

In this project, you will build your own Japanese Kanji quiz website using HTML, CSS, and JavaScript. The quiz shows you an image (or English word as a fallback), challenges you to pick the correct Kanji from four choices, and gives you instant red/green feedback on every answer.

What You'll Build

Picture opening a local webpage, seeing a vocabulary image appear, clicking the Kanji you think matches, and watching your answer flash green as a review card slides in showing the full breakdown of the word.

By the end of this project, you'll have:

  • A dark-themed quiz that shuffles questions randomly each time you play, with red/green instant feedback that keeps you on a question until you get it right.
  • An automatic image fallback that gracefully shows English text whenever an image file is missing, so the quiz always works.
  • A review card after each correct answer showing the image, Kanji, Romaji, and English meaning together, plus a final score screen with a restart button.
  • Secret Mission: Expand the quiz with your full Excel dataset and add category filtering to study specific vocabulary groups.

Do I need to know JavaScript already?

Nope. This project provides all the code for you to copy and paste. You will learn what the code does as you go, but no prior experience with HTML, CSS, or JavaScript is required.

Before We Start

Before we dive in, let's lock in what you're about to build and why it matters to you.

Set Up Your Project Folder

Every web project starts with a folder that holds all its pieces together. Your quiz needs its files to live in one place so the browser can find each piece when loading the page.

Your Japanese Kanji quiz uses HTML for structure, CSS for styling, JavaScript for logic, and image files for the quiz questions. In this step, you will create a dedicated folder to keep everything organized.

In this step, get ready to:
  • Create a japanese-quiz folder on your Desktop.
  • Add three empty project files and an images subfolder.
  • Open the folder in VS Code and verify the structure.
Create the project folder

You need a folder on your Desktop to hold all the quiz files. You will create it using the terminal inside VS Code.

  • Press Cmd+Space (macOS) or the Windows key (Windows) to open your search bar.
  • Type VS Code and press Enter to open it.
  • Open the integrated terminal by pressing Ctrl+` (macOS and Windows).
  • Move to your Desktop by running this command:
cd ~/Desktop
  • Create your project folder by running this command:
mkdir japanese-quiz
  • Confirm the folder was created by running:
ls

You should see japanese-quiz listed in the output.

Don't see the folder?

Make sure you typed the folder name exactly as shown. If you see a "No such file or directory" error on the cd ~/Desktop command, your system may use a different path for the Desktop. Try running ls ~ to see available folders.

Help me find my Desktop folder in the terminal.

Set up your project files in VS Code

Now you will open the folder in VS Code and create the files your quiz needs. Creating files through VS Code's sidebar works the same way on every operating system.

  • In VS Code, click File in the top menu bar.
  • Click Open Folder.
  • Navigate to your Desktop, select the japanese-quiz folder, then click Open.

The Explorer sidebar on the left now shows your empty japanese-quiz folder.

  • Click the New File icon at the top of the Explorer sidebar.
  • Type index.html and press Enter.
  • Click New File again and type style.css.
  • Click New File once more and type script.js.
  • Click the New Folder icon (next to New File) and type images.

Your Explorer sidebar should now show three files and one folder: index.html, style.css, script.js, and the images folder.

What about your image files?

If you have PNG files ready for your quiz questions, drag and drop them into the images folder in the Explorer sidebar now. If you do not have images yet, that is perfectly fine. The quiz will automatically show English text instead of images when a file is missing.

Don't see the New File or New Folder icons?

The icons appear when you hover over the folder name in the Explorer sidebar. Make sure you opened the japanese-quiz folder (not just a single file). If the sidebar is closed, click the top icon in the left activity bar or press Ctrl+Shift+E (Windows) or Cmd+Shift+E (macOS) to reopen it.

Help me find the New File button in VS Code.

Your project folder is ready to go. Next up, you will build the quiz's HTML skeleton so you have something to see in the browser.

Build the Quiz Structure with HTML

Your project folder is set up and ready to go. Now it's time to build the actual quiz.

The HTML defines the skeleton of your quiz. It creates the question area, answer buttons, review card, and end screen. Without this structure, there is nothing for CSS to style or JavaScript to control.

In this step, get ready to:
  • Paste the full HTML structure into index.html.
  • Open the file in your browser to confirm the structure exists.
Paste the quiz structure into index.html

Your HTML file needs three main areas: a quiz area where questions and answer buttons live, a review card that appears after each correct answer, and an end screen that shows your final score.

  • In VS Code, click on index.html in the Explorer sidebar to open it.
  • Paste the following code to create the page head, header, and quiz question area:
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Japanese Kanji Quiz</title>
    <link rel="stylesheet" href="style.css">
</head>
<body>
    <div class="quiz-container">
        <div class="header">
            <h1>Japanese Kanji Quiz</h1>
            <div class="score">Score: <span id="score">0</span> / <span id="total">0</span></div>
        </div>

        <div id="quiz-area">
            <div class="question-area">
                <div class="image-container">
                    <img id="question-image" src="" alt="Question image">
                    <p id="question-text" class="question-text"></p>
                </div>
            </div>

            <div class="choices" id="choices">
                <button class="choice-btn" data-index="0"></button>
                <button class="choice-btn" data-index="1"></button>
                <button class="choice-btn" data-index="2"></button>
                <button class="choice-btn" data-index="3"></button>
            </div>
        </div>

What does this code do?

The <head> section sets up the page title and links to your style.css stylesheet.

The quiz-area div contains two things: an image container (where question images or fallback text will appear) and four answer buttons arranged in a grid. Each button has a data-index attribute so JavaScript can identify which one was clicked.

  • Directly below the code you just pasted, add the review card and end screen by pasting this code:

        <div id="review-card" class="review-card hidden">
            <div class="review-image-container">
                <img id="review-image" src="" alt="Answer image">
                <p id="review-text" class="review-text"></p>
            </div>
            <div class="review-details">
                <p class="review-kanji" id="review-kanji"></p>
                <p class="review-romaji" id="review-romaji"></p>
                <p class="review-english" id="review-english"></p>
            </div>
            <button id="next-btn" class="next-btn">Next</button>
        </div>

        <div id="end-screen" class="end-screen hidden">
            <h2>Quiz Complete!</h2>
            <p class="final-score">You got <span id="final-score"></span> out of <span id="final-total"></span> correct!</p>
            <button id="restart-btn" class="next-btn">Try Again</button>
        </div>
    </div>

    <script src="script.js"></script>
</body>
</html>

What does this code do?

The review-card section is hidden by default (class="hidden"). After a correct answer, JavaScript will reveal it to show the full breakdown: the image, Kanji, Romaji, and English translation.

The end-screen section shows the final score and a Try Again button. It stays hidden until all questions are answered. The <script> tag at the bottom links to script.js so the browser loads your JavaScript after the HTML is ready.

  • Save index.html by pressing Cmd+S (macOS) or Ctrl+S (Windows).

See red underlines or warnings in VS Code?

Make sure the second chunk is pasted directly below the first chunk with no gap or overlap. The last line of chunk one is </div> (closing quiz-area), and chunk two should start on the very next line.

If you see a red squiggly on the </html> line, check that you have not accidentally pasted the code inside another tag.

help me fix the HTML structure in my index.html file

✔️ Awesome, I've got everything!

Great. Double check you have saved your file before moving on.

ⓧ I'd like to double check the full code

Your index.html should contain both chunks in order. Scroll up and confirm:

  • Chunk 1 is present: starts with <!DOCTYPE html> and ends with the closing </div> for quiz-area.
  • Chunk 2 is present: starts with <div id="review-card"... and ends with </html>.
  • No duplicate or missing closing tags between the two chunks.
Open the file in your browser

Before you open the file, what do you think the page will look like? There is no CSS yet and no JavaScript running.

  • Open your file manager (Finder on macOS or File Explorer on Windows).
  • Navigate to the japanese-quiz folder on your Desktop.
  • Double-click index.html to open it in your default browser.

You should see a plain white page with raw text: the heading "Japanese Kanji Quiz", the word "Score: 0 / 0", and empty button outlines. It looks broken and ugly. That is exactly right.

Why does it look so broken?

HTML on its own just defines structure. Without CSS, the browser shows everything in its default unstyled format. Without JavaScript, the buttons do nothing when clicked. You are looking at the raw skeleton of your quiz.

The fact that you can see the text and button outlines proves all your elements are in place. CSS and JavaScript will transform this in the next steps.

See a completely blank page?

Check that you saved index.html after pasting the code. In VS Code, a dot next to the filename in the tab means unsaved changes.

Make sure you opened index.html directly from your japanese-quiz folder, not a different file.

help me figure out why my index.html page is blank in the browser

Your HTML skeleton is in place. Next up, you will add CSS to transform this ugly raw text into a polished dark-themed quiz interface.

Style the Quiz with CSS

Your HTML structure is in place, but the page looks broken and unreadable in the browser.

In this step, you will add CSS to transform the raw elements into a polished dark-themed quiz card. The styles also solve an image sizing problem by scaling any image to fit a fixed container without stretching or distortion.

In this step, get ready to:
  • Add all the CSS styles to your empty style.css file.
  • Verify the dark-themed quiz layout renders correctly in your browser.
Add the foundation and layout styles

The first chunk of CSS resets browser defaults, sets the dark background, and creates the centered quiz card.

  • Switch back to style.css in VS Code and paste the following code:
/* Reset browser default spacing */
* {
    margin: 0;
    padding: 0;
    box-sizing: border-box;
}

/* Dark background with centered content */
body {
    font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
    background-color: #1a1a2e;
    color: #ffffff;
    min-height: 100vh;
    display: flex;
    align-items: center;
    justify-content: center;
}

/* Main card container */
.quiz-container {
    background-color: #16213e;
    border-radius: 16px;
    padding: 40px;
    max-width: 600px;
    width: 90%;
    box-shadow: 0 10px 30px rgba(0, 0, 0, 0.3);
}

What does this code do?

The * selector resets margins and padding on every element so you start with a clean slate.

The body uses flexbox to center the quiz card vertically and horizontally on a dark navy background. The .quiz-container creates a slightly lighter card with rounded corners and a subtle shadow.

  • Now paste the header and image container styles directly below the code you just added:
/* Quiz title and score display */
.header {
    text-align: center;
    margin-bottom: 30px;
}

.header h1 {
    font-size: 1.8rem;
    margin-bottom: 10px;
    color: #e94560;
}

.score {
    font-size: 1.1rem;
    color: #a8a8b3;
}

/* Fixed-height box for question images */
.image-container {
    width: 100%;
    height: 200px;
    display: flex;
    align-items: center;
    justify-content: center;
    background-color: #0f3460;
    border-radius: 12px;
    margin-bottom: 24px;
    overflow: hidden;
}

.image-container img {
    max-width: 100%;
    max-height: 100%;
    object-fit: contain;
}

What does object-fit: contain do?

The object-fit: contain property scales any image to fit inside the 200px container while keeping its original proportions. No matter what size your PNG files are, they will display neatly without stretching or cropping.

  • Save style.css and refresh your browser.
  • You should see a dark navy page with a lighter centered card, a red-pink "Japanese Kanji Quiz" title, grey score text, and a blue rounded rectangle where images will appear.

Still seeing a plain white page?

  • Make sure you saved style.css (look for the dot on the tab in VS Code that disappears after saving).
  • Confirm your HTML file has <link rel="stylesheet" href="style.css"> in the <head> section.
  • Check that style.css is in the same folder as index.html (not inside the images subfolder).

Help me figure out why my CSS is not loading.

Style the answer buttons

The quiz uses four answer buttons arranged in a two-column grid. This chunk styles their default appearance and hover effect.

  • In style.css, paste the following code below your existing styles:
/* Fallback text when image is missing */
.question-text {
    font-size: 1.5rem;
    text-align: center;
    padding: 20px;
    display: none;
}

/* 2x2 grid for answer buttons */
.choices {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 12px;
}

/* Individual answer button */
.choice-btn {
    padding: 16px;
    font-size: 1.3rem;
    border: 2px solid #0f3460;
    border-radius: 10px;
    background-color: #0f3460;
    color: #ffffff;
    cursor: pointer;
    transition: all 0.2s ease;
}

.choice-btn:hover {
    border-color: #e94560;
    background-color: #1a1a4e;
}

What does this code do?

The grid-template-columns: 1fr 1fr creates two equal-width columns so the four buttons sit in a neat 2x2 layout.

The transition: all 0.2s ease makes the hover colour change smooth instead of instant. The .question-text is hidden by default and only appears when JavaScript cannot load an image.

  • Save style.css and refresh your browser.
  • You should now see four blue buttons arranged in a 2x2 grid below the image area.
  • Hover over a button to confirm the border highlights in pink.

Buttons stacked vertically instead of in a grid?

  • Confirm you have .choices (with a dot) as the selector, not just choices without the dot.
  • Check that display: grid and grid-template-columns: 1fr 1fr are both inside the .choices block.

Help me debug my CSS grid layout.

Add the interactive states and hidden panel styles

The remaining styles handle button colour changes (green for correct, red for wrong), the review card that appears after a correct answer, and the end screen. These elements are hidden until JavaScript reveals them, so the page will look the same after saving.

  • Paste the button state and review card styles below your existing code in style.css:
/* Button feedback colours */
.choice-btn.correct {
    background-color: #2ecc71;
    border-color: #2ecc71;
}

.choice-btn.wrong {
    background-color: #e74c3c;
    border-color: #e74c3c;
}

.choice-btn:disabled {
    cursor: not-allowed;
    opacity: 0.7;
}

/* Review card shown after correct answer */
.review-card {
    text-align: center;
}

.review-image-container {
    width: 100%;
    height: 200px;
    display: flex;
    align-items: center;
    justify-content: center;
    background-color: #0f3460;
    border-radius: 12px;
    margin-bottom: 20px;
    overflow: hidden;
}

What does this code do?

The .correct and .wrong classes will be added by JavaScript when the learner clicks an answer. Green means right, red means wrong.

The .review-card and .review-image-container style the card that reveals the full answer (image, kanji, romaji, and English) after a correct pick.

  • Now paste the review detail styles and the Next button directly below:
/* Review card image and text elements */
.review-image-container img {
    max-width: 100%;
    max-height: 100%;
    object-fit: contain;
}

.review-text {
    font-size: 1.5rem;
    padding: 20px;
    display: none;
}

.review-details {
    margin-bottom: 20px;
}

.review-kanji {
    font-size: 2.5rem;
    margin-bottom: 8px;
}

.review-romaji {
    font-size: 1.2rem;
    color: #a8a8b3;
    margin-bottom: 4px;
}

.review-english {
    font-size: 1.1rem;
    color: #e94560;
}

What does this code do?

These styles size the kanji text large (2.5rem) so it is easy to read, with smaller romaji and a pink English translation below. The review image container reuses the same sizing approach as the question image.

  • Save style.css and refresh your browser.
  • The page should look the same as before (dark card, blue image area, 2x2 button grid). The review card is hidden until JavaScript adds interactivity.
  • Finally, paste the remaining styles for the Next button, end screen, and the hidden utility class:
/* Next and Try Again button */
.next-btn {
    padding: 14px 40px;
    font-size: 1.1rem;
    border: none;
    border-radius: 10px;
    background-color: #e94560;
    color: #ffffff;
    cursor: pointer;
    transition: background-color 0.2s ease;
}

.next-btn:hover {
    background-color: #c0392b;
}

/* Final score screen */
.end-screen {
    text-align: center;
}

.end-screen h2 {
    font-size: 2rem;
    margin-bottom: 16px;
    color: #e94560;
}

.final-score {
    font-size: 1.3rem;
    margin-bottom: 24px;
}

/* Utility class to hide elements */
.hidden {
    display: none;
}

What does this code do?

The .next-btn styles the Next and Try Again buttons with the same pink accent colour used in the title.

The .hidden class is the key toggle. JavaScript will add and remove this class to show or hide the review card and end screen at the right moments.

  • Save style.css.

Before you refresh, what do you expect to see change on the page?

  • Refresh your browser.
  • The page still looks the same: a dark-themed centered card with a blue image area and four styled buttons in a grid. The review card and end screen remain hidden because their HTML elements have the hidden class.
  • Try clicking a button. Nothing happens yet because there is no JavaScript to handle clicks.

Seeing broken layout or overlapping elements?

  • Check for missing closing braces } in your CSS. A missing brace causes every rule below it to break.
  • Make sure each CSS chunk was pasted below the previous one, not inside another selector block.
  • Use the double-check tab below to compare your full file against the expected code.

Help me find the CSS error breaking my layout.

✔️ Awesome, I've got everything!

Great. Double check you have saved style.css and you can move on.

ⓧ I'd like to double check the full code

Scroll up and confirm each of these chunks is present in your style.css, in this order:

  • CSS reset (* selector), body styles, and .quiz-container styles.
  • .header, .header h1, .score, .image-container, and .image-container img styles.
  • .question-text, .choices, .choice-btn, and .choice-btn:hover styles.
  • .choice-btn.correct, .choice-btn.wrong, .choice-btn:disabled, .review-card, and .review-image-container styles.
  • .review-image-container img, .review-text, .review-details, .review-kanji, .review-romaji, and .review-english styles.
  • .next-btn, .next-btn:hover, .end-screen, .end-screen h2, .final-score, and .hidden styles.

Your quiz now looks polished and ready for action. Next up, you will bring it to life with JavaScript so clicking those buttons actually does something.

Add the Quiz Logic with JavaScript

Your quiz has the structure and the style, but clicking those buttons does absolutely nothing. The HTML is a skeleton and the CSS is paint. JavaScript is what makes it alive.

In this step, you will wire up the entire quiz engine. That means loading questions, checking answers with instant red/green feedback, showing a review card after each correct answer, and tracking your score through to a final results screen.

In this step, get ready to:
  • Add the quiz data and DOM setup to script.js.
  • Build the functions that load questions, handle answers, and show results.
  • Test the complete quiz flow including the image fallback.
Paste the JavaScript into script.js

The JavaScript file has several parts: the quiz data, references to HTML elements, and the functions that control quiz behavior. You will paste each section in order.

  • Switch back to VS Code and open script.js.
  • Add the quiz data array by pasting this code at the top of the file:
// ===== YOUR QUIZ DATA - ADD MORE QUESTIONS HERE =====
const quizData = [
    {
        id: "TND_001",
        category: "Time & Dates",
        kanji: "毎日 (まいにち)",
        romaji: "mainichi",
        english: "Every day",
        image: "images/time_every_day.png"
    },
    {
        id: "TND_002",
        category: "Time & Dates",
        kanji: "今 (いま)",
        romaji: "ima",
        english: "Now",
        image: "images/time_now.png"
    },
    {
        id: "TND_003",
        category: "Time & Dates",
        kanji: "今日 (きょう)",
        romaji: "kyō",
        english: "Today",
        image: "images/time_today.png"
    },
    {
        id: "TND_004",
        category: "Time & Dates",
        kanji: "明日 (あした)",
        romaji: "ashita",
        english: "Tomorrow",
        image: "images/time_tomorrow.png"
    }
];
// ===== END OF QUIZ DATA =====

What does this code do?

This array holds your quiz questions. Each entry has a unique id, a category for grouping, the kanji answer the learner must pick, romaji pronunciation, the english meaning, and a path to an image file.

Right now there are only 4 trial entries. In the Secret Mission you will expand this to your full dataset.

  • Below the quiz data, paste the state variables, DOM references, and the shuffle helper function:
let currentQuestion = 0;
let score = 0;
let shuffledQuestions = [];

const questionImage = document.getElementById("question-image");
const questionText = document.getElementById("question-text");
const choicesContainer = document.getElementById("choices");
const choiceButtons = document.querySelectorAll(".choice-btn");
const reviewCard = document.getElementById("review-card");
const reviewImage = document.getElementById("review-image");
const reviewText = document.getElementById("review-text");
const reviewKanji = document.getElementById("review-kanji");
const reviewRomaji = document.getElementById("review-romaji");
const reviewEnglish = document.getElementById("review-english");
const quizArea = document.getElementById("quiz-area");
const endScreen = document.getElementById("end-screen");
const nextBtn = document.getElementById("next-btn");
const restartBtn = document.getElementById("restart-btn");
const scoreDisplay = document.getElementById("score");
const totalDisplay = document.getElementById("total");
const finalScore = document.getElementById("final-score");
const finalTotal = document.getElementById("final-total");

function shuffle(array) {
    const shuffled = [...array];
    for (let i = shuffled.length - 1; i > 0; i--) {
        const j = Math.floor(Math.random() * (i + 1));
        [shuffled[i], shuffled[j]] = [shuffled[j], shuffled[i]];
    }
    return shuffled;
}

What does this code do?

The let variables track quiz state: which question you are on, your score, and the shuffled question order.

The const lines grab every HTML element the quiz needs to update. Each getElementById call matches an id attribute from your index.html.

The shuffle() function randomizes an array using the Fisher-Yates algorithm so questions appear in a different order every time you play.

  • Save script.js and refresh your browser.
  • Open DevTools by pressing F12 (or Cmd+Option+I on macOS).
  • Click the Console tab. You should see no red error messages.

Seeing a red error in the console?

  • Check that your script.js file is saved. Unsaved changes will not load.
  • Make sure you pasted the code in order: quiz data first, then the variables and shuffle function directly below.
  • Confirm the file is named exactly script.js (not script.JS or scripts.js).

Help me debug a console error in my quiz JavaScript.

  • Back in VS Code, paste the startQuiz() function below your shuffle function:
function startQuiz() {
    currentQuestion = 0;
    score = 0;
    shuffledQuestions = shuffle(quizData);
    totalDisplay.textContent = shuffledQuestions.length;
    scoreDisplay.textContent = score;
    endScreen.classList.add("hidden");
    reviewCard.classList.add("hidden");
    quizArea.classList.remove("hidden");
    loadQuestion();
}

What does this code do?

This function resets everything for a fresh game. It zeroes the score, shuffles the questions into a new random order, updates the score display, hides the end/review screens, shows the quiz area, then loads the first question.

  • Directly below startQuiz(), paste the loadQuestion() function:
function loadQuestion() {
    const question = shuffledQuestions[currentQuestion];

    questionImage.style.display = "none";
    questionText.style.display = "none";

    const testImg = new Image();
    testImg.onload = function () {
        questionImage.src = question.image;
        questionImage.style.display = "block";
    };
    testImg.onerror = function () {
        questionText.textContent = question.english;
        questionText.style.display = "block";
    };
    testImg.src = question.image;

    const wrongChoices = quizData
        .filter(item => item.id !== question.id)
        .sort(() => Math.random() - 0.5)
        .slice(0, 3);

    const allChoices = shuffle([question, ...wrongChoices]);

    choiceButtons.forEach((btn, index) => {
        btn.textContent = allChoices[index].kanji;
        btn.className = "choice-btn";
        btn.disabled = false;
        btn.onclick = () => handleAnswer(btn, allChoices[index].id === question.id);
    });
}

What does this code do?

This is the heart of the quiz. It grabs the current question, then tries to load its image. If the image file exists, it displays it. If not, the onerror handler kicks in and shows the English meaning as text instead.

It then picks 3 random wrong answers from the full data, shuffles all 4 choices together, and assigns each kanji to a button. The onclick handler checks whether the clicked button's answer matches the correct question ID.

  • Save script.js and refresh your browser.
  • In the DevTools Console tab, type startQuiz() and press Enter.
  • You should see the four buttons fill in with kanji characters. The image area shows an image or English text.

Buttons still blank after running startQuiz()?

  • Make sure you saved after pasting loadQuestion(). The console runs whatever version the browser last loaded.
  • Check the console for errors like loadQuestion is not defined. If you see this, confirm the function is pasted below startQuiz() with no typos in the function name.

Help me figure out why startQuiz() does not load questions.

  • Back in VS Code, paste the handleAnswer() function below loadQuestion():
function handleAnswer(button, isCorrect) {
    if (isCorrect) {
        button.classList.add("correct");
        score++;
        scoreDisplay.textContent = score;

        choiceButtons.forEach(btn => btn.disabled = true);

        setTimeout(() => {
            showReview();
        }, 600);
    } else {
        button.classList.add("wrong");
        button.disabled = true;

        setTimeout(() => {
            button.classList.remove("wrong");
        }, 400);
    }
}

What does this code do?

When the learner clicks a correct answer, the button turns green, the score increments, all buttons are disabled, and after 600ms the review card appears.

When the learner clicks a wrong answer, the button flashes red for 400ms then resets. The button gets disabled so it cannot be clicked again, but the other buttons stay active. You keep trying until you find the right one.

  • Below handleAnswer(), paste the showReview() function:
function showReview() {
    const question = shuffledQuestions[currentQuestion];

    quizArea.classList.add("hidden");
    reviewCard.classList.remove("hidden");

    reviewImage.style.display = "none";
    reviewText.style.display = "none";

    const testImg = new Image();
    testImg.onload = function () {
        reviewImage.src = question.image;
        reviewImage.style.display = "block";
    };
    testImg.onerror = function () {
        reviewText.textContent = question.english;
        reviewText.style.display = "block";
    };
    testImg.src = question.image;

    reviewKanji.textContent = question.kanji;
    reviewRomaji.textContent = question.romaji;
    reviewEnglish.textContent = question.english;
}

What does this code do?

After a correct answer, this function hides the quiz area and reveals the review card. It shows the image (or English fallback), plus the full kanji, romaji, and English meaning so you can study the answer before moving on.

  • Save script.js and refresh your browser.
  • In the DevTools Console, type startQuiz() and press Enter.
  • Click a wrong answer. It should flash red and become unclickable.
  • Click the correct answer. It flashes green, then the review card appears showing the kanji, romaji, and English meaning.

Button turns green but no review card?

  • Check the console for an error like showReview is not defined. This means the function was not saved or has a typo.
  • Confirm showReview() is pasted directly below handleAnswer() and the file is saved.

Help me debug why the review card does not appear.

  • Finally, paste the event listeners, the end screen function, and the startup call at the bottom of script.js:
nextBtn.addEventListener("click", () => {
    currentQuestion++;
    if (currentQuestion >= shuffledQuestions.length) {
        showEndScreen();
    } else {
        reviewCard.classList.add("hidden");
        quizArea.classList.remove("hidden");
        loadQuestion();
    }
});

restartBtn.addEventListener("click", startQuiz);

function showEndScreen() {
    reviewCard.classList.add("hidden");
    quizArea.classList.add("hidden");
    endScreen.classList.remove("hidden");
    finalScore.textContent = score;
    finalTotal.textContent = shuffledQuestions.length;
}

startQuiz();

What does this code do?

The nextBtn listener advances to the next question or shows the end screen when all questions are done.

The restartBtn listener calls startQuiz() again so the learner can retry.

The final startQuiz(); line at the very bottom kicks off the quiz automatically when the page loads.

  • Save script.js.
Test the complete quiz

Before you refresh, what do you think will happen now that the file ends with startQuiz();? Will the quiz start automatically, or will you still need to trigger it from the console?

  • Refresh your browser.
  • The quiz loads immediately with a question displayed. No console command needed this time.
  • Click a wrong answer. It flashes red and you stay on the same question.
  • Click the correct answer. It flashes green, then the review card appears showing the kanji, romaji, and English meaning.
  • Click Next to advance to the next question.
  • Answer all 4 questions. The end screen shows your score and a Try Again button.
  • Click Try Again. The quiz restarts with questions in a new random order.

Quiz does not load on refresh?

  • Open the DevTools Console and look for any red error. The most common issue is a missing closing brace } or bracket ] somewhere in the file.
  • Make sure startQuiz(); is the very last line in the file. If it appears before a function definition that it depends on, JavaScript hoisting for const variables will fail.
  • Verify your index.html still has <script src="script.js"></script> before the closing </body> tag.

Help me figure out why my quiz does not start.

Test the image fallback

The quiz is designed to show an image for each question. If the image file does not exist in your images folder, the quiz automatically shows the English meaning as text instead.

  • If you do not have PNG files in your images folder, refresh the quiz. You should see the English word (like "Every day" or "Now") displayed in the blue area instead of an image.
  • If you do have images, temporarily rename one file (for example rename time_every_day.png to time_every_day_backup.png). Refresh and play until that question appears. You should see the English text fallback.
  • Rename it back when you have confirmed the fallback works.

Why does the fallback matter?

The JavaScript Image() constructor quietly tests whether each image file loads. If it fails, the onerror callback fires and the quiz displays text instead. This means you can add hundreds of questions to your data without needing an image for every single one.

✔️ Awesome, I've got everything!

Great. Double check you have saved script.js and your quiz is running in the browser.

ⓧ I'd like to double check the full code

Your script.js should contain these sections in order. Scroll up through your file and confirm each is present:

  • The quizData array with 4 entries (TND_001 through TND_004) wrapped in the comment markers.
  • Three let variables (currentQuestion, score, shuffledQuestions).
  • Eighteen const DOM references (questionImage through finalTotal).
  • The shuffle() function.
  • The startQuiz() function.
  • The loadQuestion() function with image fallback logic.
  • The handleAnswer() function with correct/wrong branching.
  • The showReview() function.
  • Event listeners on nextBtn and restartBtn.
  • The showEndScreen() function.
  • A bare startQuiz(); call as the very last line.

Your quiz is fully functional. You have a working Japanese Kanji study tool that you can open any time to practice. Next up, the Secret Mission challenges you to expand it with your full dataset and add category filtering.

Secret mission

Expand Your Quiz with More Data

Your quiz works, but 4 questions is barely a warm-up. Turn it into a real study tool by loading your full Excel dataset and adding category filtering so you can drill specific vocabulary groups.

Clean Up Your Resources

Clean Up Your Resources

Decide whether to keep your resources running, pause them to come back later, or delete them entirely. This project runs entirely locally, so there are no ongoing costs.

Resources you used:

  • The japanese-quiz folder on your Desktop (contains index.html, style.css, script.js, and the images subfolder).

Keep everything running

No action needed. Choose this if you plan to keep studying with your quiz or want to add more vocabulary entries later.

  • Your japanese-quiz folder stays on your Desktop, ready to open in your browser anytime.
  • You can keep adding questions to the quizData array in script.js whenever you want to expand your quiz.

Pause - I'll come back to this later

Since this project is just local files, there is nothing to pause. Your files sit on your Desktop and use minimal disk space.

  • Close the browser tab showing your quiz.
  • Close the japanese-quiz folder in VS Code.
  • When you want to return, double-click index.html in the japanese-quiz folder to reopen the quiz in your browser.

Delete - I don't want to use this again

Remove all project files from your computer. This cannot be undone.

  • Close the browser tab showing your quiz.
  • Close the japanese-quiz folder in VS Code.
  • Delete the japanese-quiz folder from your Desktop by running this command in your terminal:

macOS

rm -rf ~/Desktop/japanese-quiz

Windows

rmdir /s /q %USERPROFILE%\Desktop\japanese-quiz
  • Confirm the folder is gone by checking your Desktop.

Nice Work!

Nice Work!

Nice work! You just built a fully functional Japanese Kanji quiz website from scratch using nothing but HTML, CSS, and JavaScript.

You've learned how to:

  • Build a dark-themed quiz interface with HTML structure, CSS Grid layout for answer buttons, and image containers that scale any image without distortion.
  • Write quiz logic in JavaScript that shuffles questions, checks answers with instant red/green feedback, tracks your score, and shows a review card after each correct answer.
  • Handle missing images gracefully using the Image() constructor to test-load files and automatically fall back to English text when a PNG does not exist.
  • Secret Mission: Expanded the quiz with your full Excel dataset and added a category dropdown to filter questions before starting.

Ready to quiz yourself?