Student slide deck lessons 5 to 8: HTML forms, localStorage, fetch API, and final project

1/8
HTMLFormsValidation

Getting input from users with HTML forms

Forms are how every website collects data — sign-ups, logins, search bars. Let's build one from scratch!

2/8 Agenda

What we'll cover today

1
Form basics
5 min
2
Input types
6 min
3
JS validation
8 min
4
Error messages
7 min
5
Challenge
4 min
3/8 HTML

The form element and input types

<form id="myForm">
  <label>Name:</label>
  <input type="text" id="name"
    placeholder="Your name">

  <label>Email:</label>
  <input type="email" id="email">

  <label>Password:</label>
  <input type="password" id="pw">

  <button type="button" onclick="submit()">Submit</button>
</form>
Common input types
  • text — plain text
  • email — validates email format
  • password — hides characters
  • number — numbers only
  • checkbox — tick box
  • radio — pick one option
  • date — date picker
4/8 JavaScript

Reading form values with JS

function submitForm() {
  let name = document.getElementById("name").value;
  let email = document.getElementById("email").value;
  let pw = document.getElementById("pw").value;

  // .trim() removes spaces from start/end
  if (name.trim() === "") {
    alert("Please enter your name!");
    return;
  }
  if (pw.length < 6) {
    alert("Password must be 6+ chars");
    return;
  }
  console.log("Form submitted!", name, email);
}
return stops the function
When validation fails, return exits the function immediately — the rest doesn't run
.length counts characters
"hello".length = 5. Use it to check if a password is long enough
5/8 JavaScript

Showing error messages on the page

Instead of alerts, show errors next to the input — much better UX!

<input type="text" id="name">
<p id="nameErr" style="color:red;font-size:12px;"></p>

// JS validation
function validate() {
  let name = document.getElementById("name").value;
  let err = document.getElementById("nameErr");

  if (name.trim() === "") {
    err.innerHTML = "Name is required!";
    return false;
  }
  err.innerHTML = ""; // clear error
  return true;
}
Name
Enter your name...
Name is required!
Email
sarah@gmail.com
Looks good!
6/8 CSS

Styling your form

form { max-width: 400px; margin: 0 auto; }

label { display: block;
  font-size: 14px; margin-bottom: 4px; }

input { display: block; width: 100%;
  padding: 10px; border-radius: 6px;
  border: 1px solid #ddd;
  margin-bottom: 16px; }

input:focus { border-color: #2a5fa5;
  outline: none; }

.error { color: red; font-size: 12px;
  margin-top: -12px; margin-bottom: 10px; }
display: block on inputs
Makes each input take its own full-width line instead of sitting inline
input:focus
The :focus selector applies styles when the user clicks into the input
7/8 HTML

Checkbox and radio inputs

<!-- Checkbox -->
<input type="checkbox" id="agree">
<label for="agree">I agree to terms</label>

<!-- Radio buttons -->
<input type="radio" name="gender" value="male"> Male
<input type="radio" name="gender" value="female"> Female

// Check if checkbox is ticked
let agreed = document.getElementById("agree").checked;
if (!agreed) {
  alert("Please accept the terms!");
}
Key difference
  • Checkbox — can tick many at once
  • Radio — only one can be selected (same name groups them)
  • Use .checked not .value to read checkbox
8/8 Your turn

Challenge — build a sign-up form

Create a registration form with validation for all fields

Required fields
  • Full name (cannot be empty)
  • Email (must contain @)
  • Password (minimum 6 characters)
  • Confirm password (must match)
  • Agree to terms checkbox (must be ticked)
Bonus challenges
  • Show a green tick next to valid fields
  • Show/hide password button
  • After success, show a "Welcome!" message and hide the form
Hint: check if email contains @
email.includes("@") returns true or false
1/8
JavaScriptlocalStorage

Saving data in the browser with localStorage

Ever wondered how a website remembers you? localStorage lets you save data that survives page refresh!

2/8 Agenda

What we'll cover today

1
What is localStorage?
5 min
2
Get & set
7 min
3
Store objects
8 min
4
Persist to-do list
8 min
5
Challenge
2 min
3/8 localStorage

What is localStorage?

Think of it as a tiny notebook your browser keeps — it remembers data even after you close the tab

// Save a value
localStorage.setItem("username", "Sarah");

// Read a value
let name = localStorage.getItem("username");
console.log(name); // "Sarah"

// Delete one item
localStorage.removeItem("username");

// Delete everything
localStorage.clear();
3 simple rules
  • Keys and values are always strings
  • Data stays until you delete it or the user clears browser data
  • Each website has its own separate storage
Check in DevTools
F12 → Application → Local Storage — you can see and edit your saved data!
4/8 localStorage

Storing objects and arrays using JSON

localStorage only stores text — use JSON.stringify() to save objects and JSON.parse() to read them back

let user = { name: "Sarah", age: 17 };

// Convert object → text to save
localStorage.setItem("user", JSON.stringify(user));

// Convert text → object to read
let saved = JSON.parse(localStorage.getItem("user"));
console.log(saved.name); // "Sarah"

// Same with arrays
let tasks = ["Study", "Code", "Sleep"];
localStorage.setItem("tasks", JSON.stringify(tasks));
let myTasks = JSON.parse(localStorage.getItem("tasks"));
JSON.stringify() → save
Converts objects/arrays to a text string so localStorage can store them
JSON.parse() → read
Converts the text string back into a real object or array you can use
5/8 localStorage

Persisting the to-do list

Upgrade your to-do app — tasks now survive page refresh!

// Load tasks when page opens
let tasks = JSON.parse(localStorage.getItem("tasks")) || [];

// Save tasks every time they change
function saveTasks() {
  localStorage.setItem("tasks", JSON.stringify(tasks));
}

// Call saveTasks() after every add or delete
function addTask() {
  let text = document.getElementById("taskInput").value;
  if (text) {
    tasks.push({ text: text, done: false });
    saveTasks(); // save immediately!
    render();
  }
}

// Initial render when page loads
render();
6/8 localStorage

Handling missing data safely

// getItem returns null if key doesn't exist
let raw = localStorage.getItem("tasks");

// Safe pattern: use || [] as fallback
let tasks = raw ? JSON.parse(raw) : [];

// Or using the OR shortcut
let tasks2 = JSON.parse(
  localStorage.getItem("tasks")
) || [];

// Save user preferences
let theme = localStorage.getItem("theme") || "light";
let username = localStorage.getItem("username") || "Guest";
Practical uses
  • Remember dark/light mode preference
  • Save a user's name after they sign in
  • Keep a shopping cart between page visits
  • Store game scores
7/8 localStorage

Dark mode with localStorage

<button onclick="toggleTheme()">Toggle theme</button>

// Load saved theme on page open
let theme = localStorage.getItem("theme") || "light";
applyTheme(theme);

function toggleTheme() {
  theme = theme === "light" ? "dark" : "light";
  localStorage.setItem("theme", theme);
  applyTheme(theme);
}

function applyTheme(t) {
  document.body.style.background = t === "dark" ? "#111" : "#fff";
  document.body.style.color = t === "dark" ? "#fff" : "#111";
}
Ternary operator
condition ? valueIfTrue : valueIfFalse — a compact if/else on one line
Light mode
Dark mode
8/8 Your turn

Challenge — persistent sign-up form

Combine forms (Lesson 5) with localStorage to remember the user

What to build
  • After form is submitted successfully, save the name to localStorage
  • When page loads, check if a name is saved — if yes, show "Welcome back, [name]!" instead of the form
  • Add a "Log out" button that clears localStorage and shows the form again
// On page load
let saved = localStorage.getItem("username");
if (saved) {
  document.getElementById("welcome").innerHTML =
    "Welcome back, " + saved + "!";
  document.getElementById("form").style.display = "none";
}
1/8
JavaScriptFetch APIJSON

Talking to the internet with fetch

APIs power every app you use. Learn how to fetch real live data from the web inside your own webpage!

2/8 Agenda

What we'll cover today

1
What is an API?
5 min
2
fetch basics
7 min
3
Read JSON
7 min
4
Display data
8 min
5
Challenge
3 min
3/8 API

What is an API? The waiter analogy

Your app
API (waiter)
Server
  • You ask: "Give me today's weather in Singapore"
  • API fetches it from the server
  • Data comes back as JSON text
  • You display it on your page
Free APIs to try
  • Official Joke API — random jokes
  • JSONPlaceholder — fake blog posts
  • PokeAPI — Pokemon data
  • Open-Meteo — free weather data
4/8 fetch

Your first fetch call

fetch() sends a request to a URL and returns a promise — then() runs when data arrives

// Fetch a random joke
fetch("https://official-joke-api.appspot.com/random_joke")
  .then(function(response) {
    return response.json(); // parse JSON
  })
  .then(function(data) {
    // data is now a JS object!
    console.log(data.setup);
    console.log(data.punchline);
  })
  .catch(function(error) {
    console.log("Error: " + error);
  });
.then() — runs when data arrives
fetch is asynchronous — it doesn't block your page while waiting
.catch() — handles errors
Always add .catch() — the API might be offline or return an error
5/8 fetch

Building a joke generator

<div id="setup">Click for a joke!</div>
<div id="punchline"></div>
<button onclick="getJoke()">New joke</button>

function getJoke() {
  document.getElementById("setup").innerHTML = "Loading...";
  document.getElementById("punchline").innerHTML = "";

  fetch("https://official-joke-api.appspot.com/random_joke")
    .then(r => r.json())
    .then(data => {
      document.getElementById("setup").innerHTML = data.setup;
      document.getElementById("punchline").innerHTML = data.punchline;
    })
    .catch(() => { document.getElementById("setup").innerHTML = "Error loading joke!"; });
}
Why don't scientists trust atoms?
Because they make up everything!
New joke
Arrow functions — r => r.json()
A shorter way to write function(r) { return r.json() }
6/8 fetch

Fetching a list and displaying it

APIs often return arrays of objects — loop through them to display each one

// Fetch 5 fake blog posts from JSONPlaceholder
fetch("https://jsonplaceholder.typicode.com/posts?_limit=5")
  .then(r => r.json())
  .then(posts => {
    let list = document.getElementById("postList");
    list.innerHTML = "";

    posts.forEach(post => {
      list.innerHTML += `<div class="post">
        <h3>${post.title}</h3>
        <p>${post.body}</p>
      </div>`;
    });
  });
Template literals — backtick strings
Use backticks (`) and ${variable} to embed variables directly in strings — much cleaner than "+" concatenation
7/8 fetch

Loading states and error handling

function fetchData() {
  let output = document.getElementById("output");

  // Show loading state
  output.innerHTML = "Loading...";
  output.style.color = "gray";

  fetch("https://api.example.com/data")
    .then(response => {
      if (!response.ok) throw new Error("Bad response");
      return response.json();
    })
    .then(data => {
      output.innerHTML = data.result;
      output.style.color = "green";
    })
    .catch(err => {
      output.innerHTML = "Failed to load data.";
      output.style.color = "red";
    });
}
3-state pattern
  • Loading — show "Loading..." while waiting
  • Success — display the data
  • Error — show a friendly message
Always handle all 3 states
Good UX means the user always knows what's happening — never leave them staring at a blank page
8/8 Your turn

Challenge — build a Pokemon lookup app

Use the free PokeAPI to search for any Pokemon by name

<input id="pokeName" placeholder="e.g. pikachu">
<button onclick="searchPoke()">Search</button>
<div id="result"></div>

function searchPoke() {
  let name = document.getElementById("pokeName").value.toLowerCase();
  fetch(`https://pokeapi.co/api/v2/pokemon/${name}`)
    .then(r => r.json())
    .then(p => {
      document.getElementById("result").innerHTML =
        `<img src="${p.sprites.front_default}"><p>${p.name}</p>`;
    });
}
?
pikachu
HP: 35 · Speed: 90
Bonus
  • Show the Pokemon's type (fire, water, etc.)
  • Display HP and speed stats
  • Show error if Pokemon not found
1/8
HTMLCSSJavaScript

Final project — build your own portfolio website

Put everything together — HTML, CSS, JS, forms, localStorage and an API — in one polished project you can show anyone!

2/8 Agenda

What we'll build today

1
Plan & structure
5 min
2
Navbar + hero
7 min
3
Projects section
7 min
4
Contact form
7 min
5
Polish & ship
4 min
3/8 HTML

Page structure — plan before you code

<!DOCTYPE html><html><head>
  <title>Sarah's Portfolio</title>
  <link rel="stylesheet" href="style.css">
</head><body>

  <nav class="navbar">...</nav>
  <section id="hero">...</section>
  <section id="about">...</section>
  <section id="projects">...</section>
  <section id="contact">...</section>
  <footer>...</footer>
  <script src="app.js"></script>
</body></html>
3 separate files
  • index.html — the structure
  • style.css — all the styling
  • app.js — all the JavaScript
<section> organises your page
Use <section> for major page areas — better than <div> for structure and readability
4/8 CSS

Hero section — make a great first impression

<section id="hero">
  <div class="avatar">S</div>
  <h1>Hi, I'm Sarah Tan</h1>
  <p>IT Student · Web Developer in training</p>
  <a href="#projects">See my work</a>
</section>

#hero { text-align: center; padding: 80px 20px; }
.avatar { width: 100px; height: 100px;
  border-radius: 50%; background: #2a5fa5;
  margin: 0 auto 20px;
  font-size: 40px; line-height: 100px; color: white; }
S
Hi, I'm Sarah Tan
IT Student · Web Developer in training
See my work
5/8 HTML

Projects section — show what you've built

<section id="projects">
  <h2>My Projects</h2>
  <div class="card-row">
    <div class="project-card">
      <h3>About Me Page</h3>
      <p>My first HTML & CSS page</p>
      <span class="tag">HTML</span>
      <span class="tag">CSS</span>
    </div>
    <div class="project-card">...</div>
  </div>
</section>
About Me Page
My first HTML & CSS page
HTML CSS
To-Do App
Task manager with localStorage
JavaScript
Add all your lesson projects here!
About Me, JS Playground, To-Do App, Joke Generator — you've already built them!
6/8 localStorage

Dark mode toggle for your portfolio

<button id="themeBtn" onclick="toggleTheme()">Dark mode</button>

/* In style.css */
body.dark { background: #1a1a2e; color: #eee; }
body.dark .navbar { background: #16213e; }
body.dark .project-card { background: #222; border-color: #444; }

// In app.js
let dark = localStorage.getItem("dark") === "true";
if (dark) document.body.classList.add("dark");

function toggleTheme() {
  dark = !dark;
  document.body.classList.toggle("dark");
  localStorage.setItem("dark", dark);
}
classList.toggle()
Adds the class if it's not there, removes it if it is — perfect for toggling modes
body.dark .card — CSS nesting
This selects .card elements only when body has the dark class — no JavaScript needed!
7/8 HTML

Contact section with form + API quote

<section id="contact">
  <h2>Get in touch</h2>
  <input type="text" id="visitorName" placeholder="Your name">
  <button onclick="greetVisitor()">Say hi!</button>
  <div id="greeting"></div>
  <div id="quote"></div>
</section>

function greetVisitor() {
  let name = document.getElementById("visitorName").value;
  document.getElementById("greeting").innerHTML = `Thanks for visiting, ${name}!`;
  localStorage.setItem("lastVisitor", name);
}
Get in touch
Your name
Say hi!
Thanks for visiting, Sarah!
8/8 You built it!

What you've achieved across 8 lessons

Skills you now have
  • HTML — structure any webpage
  • CSS & Flexbox — make it look great
  • JavaScript — add interactivity
  • Forms — collect user input
  • localStorage — save data in the browser
  • fetch API — pull live data from the web
What to learn next
  • React — build bigger apps with components
  • Node.js — run JavaScript on the server
  • Git & GitHub — save and share your code
  • Netlify — host your portfolio online for free
Push your portfolio to GitHub today!
It's your developer identity — every commit shows your progress