DocsHub
JavascriptIntermediate

Student Grade Calculator

Build a grade calculator that takes multiple subject scores and calculates the total, average, and letter grade.

Student Grade Calculator

Problem

Build a grade calculator where the user can add multiple subjects with their scores. The app calculates the total, average, and an overall letter grade — and shows the grade for each subject too.

Subjects added:
Math: 85
Science: 72
English: 91

Output:
Total: 248
Average: 82.67
Overall Grade: B

Per subject:
Math:    85  →  B
Science: 72  →  C
English: 91  →  A

Grading Scale

90 - 100  →  A
80 - 89   →  B
70 - 79   →  C
60 - 69   →  D
Below 60  →  F

Logic

  1. Select inputs for subject name, score, add button, and results area
  2. Store subjects in an array — each with a name and score
  3. On add — validate inputs and push to the array
  4. Render each subject with its individual grade
  5. Calculate total and average from all scores
  6. Determine overall grade from the average
  7. Allow removing a subject — recalculate everything

Flow

no yes yes User clicks Add Subject Name and score valid? Show error Push subject to array Render subject list Calculate total and average Determine overall grade Display results User removes a subject? Filter out from array

HTML Structure

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <title>Student Grade Calculator</title>
    <style>
      body {
        font-family: sans-serif;
        max-width: 480px;
        margin: 60px auto;
        padding: 0 20px;
      }

      .input-row {
        display: flex;
        gap: 10px;
        margin-bottom: 16px;
      }

      .input-row input {
        padding: 12px;
        font-size: 1rem;
        border: 2px solid #ccc;
        border-radius: 8px;
        outline: none;
        box-sizing: border-box;
      }

      #subject-name {
        flex: 2;
      }

      #subject-score {
        flex: 1;
      }

      .input-row input:focus {
        border-color: #555;
      }

      .input-row button {
        padding: 12px 20px;
        font-size: 1rem;
        font-weight: 600;
        background: #111;
        color: #fff;
        border: none;
        border-radius: 8px;
        cursor: pointer;
      }

      .input-row button:hover {
        opacity: 0.85;
      }

      #error {
        color: #c0392b;
        font-size: 0.9rem;
        margin-bottom: 12px;
        display: none;
      }

      /* subject list */
      #subject-list {
        list-style: none;
        padding: 0;
        margin: 0 0 20px;
      }

      #subject-list li {
        display: flex;
        align-items: center;
        gap: 12px;
        padding: 12px 14px;
        border: 1px solid #e0e0e0;
        border-radius: 8px;
        margin-bottom: 8px;
      }

      #subject-list .subject-name {
        flex: 1;
        font-size: 1rem;
        color: #333;
      }

      #subject-list .subject-score {
        font-weight: 600;
        color: #111;
      }

      /* grade badge — color depends on grade */
      .grade-badge {
        padding: 4px 10px;
        border-radius: 6px;
        font-size: 0.85rem;
        font-weight: 700;
        color: #fff;
        min-width: 28px;
        text-align: center;
      }

      .grade-A { background: #1a7a3f; }
      .grade-B { background: #3498db; }
      .grade-C { background: #f39c12; }
      .grade-D { background: #e67e22; }
      .grade-F { background: #c0392b; }

      .remove-btn {
        background: none;
        border: none;
        cursor: pointer;
        font-size: 1.1rem;
        color: #ccc;
      }

      .remove-btn:hover {
        color: #e74c3c;
      }

      /* summary box */
      #summary {
        display: none;
        background: #f5f5f5;
        border-radius: 10px;
        padding: 16px 20px;
      }

      .summary-row {
        display: flex;
        justify-content: space-between;
        padding: 6px 0;
        font-size: 1rem;
        color: #333;
      }

      .summary-row .value {
        font-weight: 700;
        color: #111;
      }
    </style>
  </head>
  <body>
    <h1>Grade Calculator</h1>

    <div class="input-row">
      <input type="text" id="subject-name" placeholder="Subject name" />
      <input type="number" id="subject-score" placeholder="Score" />
      <button id="add-btn">Add</button>
    </div>

    <div id="error"></div>

    <!-- list of subjects with grades -->
    <ul id="subject-list"></ul>

    <!-- overall summary -->
    <div id="summary">
      <div class="summary-row">
        <span>Total</span>
        <span class="value" id="summary-total">0</span>
      </div>
      <div class="summary-row">
        <span>Average</span>
        <span class="value" id="summary-average">0</span>
      </div>
      <div class="summary-row">
        <span>Overall Grade</span>
        <span class="value" id="summary-grade">-</span>
      </div>
    </div>

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

Solution

// Step 1 — select elements
const subjectNameInput = document.querySelector("#subject-name");
const subjectScoreInput = document.querySelector("#subject-score");
const addBtn = document.querySelector("#add-btn");
const error = document.querySelector("#error");
const subjectList = document.querySelector("#subject-list");
const summary = document.querySelector("#summary");

// Step 2 — array to store all subjects
// each subject is { name, score }
let subjects = [];

// Step 3 — listen for add button click
addBtn.addEventListener("click", addSubject);

// also trigger on Enter in either input
[subjectNameInput, subjectScoreInput].forEach((input) => {
  input.addEventListener("keydown", (e) => {
    if (e.key === "Enter") addSubject();
  });
});

function addSubject() {
  const name = subjectNameInput.value.trim();
  const scoreValue = subjectScoreInput.value.trim();

  // Step 4 — validate inputs
  if (!name) {
    showError("Please enter a subject name.");
    return;
  }

  if (!scoreValue) {
    showError("Please enter a score.");
    return;
  }

  const score = Number(scoreValue);

  if (isNaN(score) || score < 0 || score > 100) {
    showError("Score must be a number between 0 and 100.");
    return;
  }

  // Step 5 — add to subjects array
  subjects.push({ name, score });

  // clear inputs
  subjectNameInput.value = "";
  subjectScoreInput.value = "";
  hideError();

  // re-render everything
  render();

  // focus back on subject name for quick entry
  subjectNameInput.focus();
}

// Step 6 — grade calculation function
// returns a letter grade for a given numeric score
function getGrade(score) {
  if (score >= 90) return "A";
  if (score >= 80) return "B";
  if (score >= 70) return "C";
  if (score >= 60) return "D";
  return "F";
}

function render() {
  // clear current list
  subjectList.innerHTML = "";

  if (subjects.length === 0) {
    summary.style.display = "none";
    return;
  }

  // Step 7 — render each subject with its grade
  subjects.forEach((subject, index) => {
    const grade = getGrade(subject.score);

    const li = document.createElement("li");
    li.innerHTML = `
      <span class="subject-name">${subject.name}</span>
      <span class="subject-score">${subject.score}</span>
      <span class="grade-badge grade-${grade}">${grade}</span>
      <button class="remove-btn" data-index="${index}">✕</button>
    `;

    subjectList.appendChild(li);
  });

  // Step 8 — calculate total
  // reduce sums all scores together
  const total = subjects.reduce((sum, subject) => sum + subject.score, 0);

  // Step 9 — calculate average
  const average = total / subjects.length;

  // Step 10 — overall grade is based on the average
  const overallGrade = getGrade(average);

  // Step 11 — display summary
  summary.style.display = "block";
  document.querySelector("#summary-total").textContent = total;
  document.querySelector("#summary-average").textContent = average.toFixed(2);

  const gradeElement = document.querySelector("#summary-grade");
  gradeElement.textContent = overallGrade;
  // reset classes then apply the right grade color
  gradeElement.className = `value grade-${overallGrade}`;
}

// Step 12 — remove subject — event delegation on the list
subjectList.addEventListener("click", (e) => {
  if (!e.target.classList.contains("remove-btn")) return;

  // read the index stored on the button
  const index = Number(e.target.dataset.index);

  // remove that subject from the array
  subjects.splice(index, 1);

  render();
});

function showError(message) {
  error.textContent = message;
  error.style.display = "block";
}

function hideError() {
  error.style.display = "none";
}

Code Execution

Trace through adding "Math" with score 85:

StepCodeResult
Validate name"Math" not emptypass
Validate score85 between 0-100pass
Pushsubjects.push({ name: "Math", score: 85 })subjects.length === 1
Get gradegetGrade(85)85 >= 80"B"
Renderli with "Math", 85, badge "B"shown in list

Trace through summary after adding Math: 85, Science: 72, English: 91:

StepCodeResult
Total85 + 72 + 91248
Average248 / 382.666...
Format.toFixed(2)"82.67"
Overall gradegetGrade(82.666)82.666 >= 80"B"

Trace through removing "Science" (index 1):

StepCodeResult
Click removee.target.dataset.index"1"
Splicesubjects.splice(1, 1)removes Science
Re-rendersubjects = [Math, English]new total = 85 + 91 = 176
New average176 / 288
New overall gradegetGrade(88)"B"

Output

Add Math: 85       → Math 85 — B
Add Science: 72    → Science 72 — C
Add English: 91    → English 91 — A

Summary:
Total: 248
Average: 82.67
Overall Grade: B

Remove Science:
Summary updates to:
Total: 176
Average: 88.00
Overall Grade: B

On this page