Skip to content
This is my space, where experience meets the will to start over. This is my space, where experience meets the will to start over.

The first step is knowing where you want to go.

  • Home
  • Coding Hub
    • Software & Project
      • Small Biz Ops – S.B.O.
        • SmallBizOps – Day 10/90
      • CRM/ERP
      • MyTracker
      • My Budget
    • Form Zero to “WoW”
      • JavaScript from Zero (Completed)
        • 2. Remove and Edit List Items
        • 3. Separate HTML and JavaScript, Use addEventListener and Conditional Logic
        • 4. Add Dynamic CSS Classes
        • 5. Save & Restore Your List with localStorage
        • 6 – Turn Your App into a Full To-Do List
      • Python from Zero (Completed)
        • 2. Lists & Loops
        • 3. Conditional Menus
        • 4. Edit & Remove Tasks (with closing: Python vs PHP and Large Data)
        • 5 – Save to File: Make Your Tasks Survive Restarts
        • 6 — Pythin from zero – Final Project Polishing: Numbering, Formatting, and Preparing for CSV
      • Rust – From Zero to “WoW” (Completed)
        • 1 – Setup and Project Structure in Rust
        • 2 – User input: validation and error handling
        • 3 – Rust from Zero to “WoW – BMI Calculation and Conditional Logic
        • 4 –Rust – Clear, Formatted Output
        • 5 – Rust – Final Thoughts: Precision as a Form of Respect
      • Go from Zero to “WoW” (Completed)
        • 1 – Why Go Is Perfect for a Personal Expense Tracker
        • 2 – Logging Expenses and Console Input
        • 3 – Go from Zero to “WoW” – Smart Filtering & Display Logic
        • 4 – Go – Saving Data to Local Files
        • 5 – Go – Final Project – Expense Tracker in Go
      • C++ from Zero to “WoW” (Completed)
        • 1 – Why C++ for file organization?
        • 2 – C++ – File Type Detection and Classification
        • 3 – C++ – Creating & Managing Subfolders
        • 4 – C++ – Safe File Movement and User Feedback
        • 5 – C++ – Order as Mental Clarity
      • Ubuntu – From Zero to “WoW” (Completed)
        • 2 – Ubuntu – The Desktop Environment and Essential Commands
        • 3 – Ubuntu – Managing Files, Folders, and Permissions
        • 4 – Ubuntu – Installing and Updating Software with APT and Snap
        • 5 – Ubuntu – Customizing the Desktop Environment
        • 6 – Ubuntu – Network and Device Configuration
        • 7 – Ubuntu – User Management & System Security — “The Cathedral of Permissions”
        • 8 – Ubuntu – The Talking Machine: Terminal & Bash Scripting
        • 9 – Ubuntu – Ubuntu as a Server or Development Environment
        • 10 – Ubuntu – Backup, Maintenance & Troubleshooting
    • Git Hub Repository
      • Small Biz Ops – S.B.O.
      • Mini ERP – PHP & MySQL
      • CleverCRM (Java, Spring Boot)
      • FraudWatch (Python, FastAPI + scikit-learn)
      • OnboardIQ – Smart Onboarding Portal (Flask + SQLite Demo)
    • ArchPilot
      • 1-Users & Roles, End-to-End (Architecture, Database, and Cross-Framework Code)
      • 2 – Client Registry (CRM) Across Frameworks
      • 3 – Project & Budget Tracker (ERP)
      • 4 – Approval Workflow Engine Multi-step routing, status tracking, escalation paths
      • 5 – Audit Trail & Versioning
    • Small Biz Ops – S.B.O.
  • Vivere in USA
  • P4Y
  • Testi poetici
    • 1 – Sospeso
    • 2 – Il bicchiere di vetro quieto
    • 3 – Quando l’amore inciampa
    • 4 – Ma chi siete davvero?
    • 5 – Above the Thread of Day
    • 6 – The Truth That Doesn’t Exist
    • 7 – All of You, I Miss
    • 8 – The Captain and the Ocean
    • 9 – Between Light and Mist
    • 10 – Il peso delle scelte
  • Contact
  • Admin
This is my space, where experience meets the will to start over.
This is my space, where experience meets the will to start over.

The first step is knowing where you want to go.

Coding – Step 9.5 – JavaScript from Zero – Save & Restore Your List with localStorage

Posted on 17 Agosto 202517 Agosto 2025 By Francesco

🧠 Why this lesson matters

So far (Step 9.4) we made our list interactive: clicking an item toggles a “completed” look. But if you refresh the page or close the tab, the list resets.
In this step we introduce persistence with the browser’s localStorage, so your list survives reloads without any backend.


📖 Key concepts (plain English)

  • DOM (Document Object Model)
    The DOM is the browser’s in-memory representation of your HTML. Every <ul>, <li>, etc. becomes an object that JavaScript can modify (add/remove classes, change text, delete nodes). Think of it as the “bridge” between your HTML and your JS.
  • localStorage
    A tiny key–value store built into the browser for each site. It stores strings and keeps them persistently (they remain after reloads).
    Since we want to save an array of tasks, we’ll convert it to a string with JSON.stringify() and back with JSON.parse().

🎯 Goal

  1. Load the saved list from localStorage when the page opens.
  2. Save the list every time the user adds, completes, or deletes a task.
  3. Keep the interface accessible with ARIA roles and keyboard support (Space/Enter).

🧩 Starter HTML (compatible with Step 9.4)

<section class="todo">
  <h3>My Tasks</h3>

  <form id="add-form" autocomplete="off">
    <input id="new-item" type="text" placeholder="Add a task…" />
    <button type="submit">Add</button>
    <button type="button" id="clear-done">Clear completed</button>
  </form>

  <ul id="todo-list">
    <li class="todo-item" role="checkbox" aria-checked="false" tabindex="0">Buy coffee beans</li>
    <li class="todo-item" role="checkbox" aria-checked="false" tabindex="0">Email the client</li>
    <li class="todo-item" role="checkbox" aria-checked="false" tabindex="0">Book dentist appointment</li>
  </ul>

  <p id="counter" aria-live="polite"></p>
</section>

🎨 CSS (same as 9.4 + minor tweaks)

.todo-item {
  cursor: pointer;
  padding: 8px 10px;
  border-bottom: 1px solid #eee;
  transition: background .15s;
}
.todo-item:hover,
.todo-item:focus { background: #f7faff; outline: none; }
.todo-item.done { text-decoration: line-through; color: #6b7280; opacity: .85; }

#add-form { display: flex; gap: 8px; margin: 10px 0; }
#add-form input { flex: 1; padding: 8px 10px; border: 1px solid #ddd; border-radius: 8px; }
#add-form button { padding: 8px 12px; border: 0; border-radius: 8px; cursor: pointer; }
#add-form button[type="submit"] { background: #1a73e8; color: #fff; }
#clear-done { background: #eee; color: #222; }
.remove-btn { margin-left: 8px; background: #ffe3e3; border: 0; border-radius: 8px; cursor: pointer; }

🗂️ Data model

Each task is an object:

// { id: string, text: string, done: boolean }

We’ll keep an array of these objects as our single source of truth and render the DOM from it.


⚙️ JavaScript — full persistence

Paste this after your Step 9.4 script or replace it entirely. It keeps event delegation and ARIA updates, and adds save/load.

// ----- SELECTORS -----
const list = document.getElementById('todo-list');
const form = document.getElementById('add-form');
const input = document.getElementById('new-item');
const clearDoneBtn = document.getElementById('clear-done');
const counterEl = document.getElementById('counter');

// ----- STORAGE -----
const STORAGE_KEY = 'todo-list:v1';

function loadFromStorage() {
  try {
    const raw = localStorage.getItem(STORAGE_KEY);
    return raw ? JSON.parse(raw) : null;
  } catch (e) {
    console.warn('localStorage not available or corrupt data', e);
    return null;
  }
}

function saveToStorage(data) {
  try {
    localStorage.setItem(STORAGE_KEY, JSON.stringify(data));
  } catch (e) {
    console.warn('Failed to save to localStorage', e);
  }
}

// ----- STATE -----
let todos = []; // [{id, text, done}]

// Initialize from storage or from existing <li> elements (Step 9.4)
function bootstrapFromDOM() {
  return [...list.querySelectorAll('.todo-item')].map(li => ({
    id: crypto.randomUUID(),
    text: li.textContent.trim(),
    done: li.classList.contains('done')
  }));
}

{
  const saved = loadFromStorage();
  todos = saved ?? bootstrapFromDOM();
  saveToStorage(todos); // ensure a persisted initial state
}

// ----- RENDER -----
function updateCounter() {
  const total = todos.length;
  const done = todos.filter(t => t.done).length;
  counterEl.textContent = `${done} completed out of ${total}`;
}

function render() {
  list.innerHTML = '';
  for (const t of todos) {
    const li = document.createElement('li');
    li.className = 'todo-item';
    if (t.done) li.classList.add('done');
    li.setAttribute('role', 'checkbox');
    li.setAttribute('aria-checked', String(t.done));
    li.tabIndex = 0;
    li.dataset.id = t.id;

    const name = document.createElement('span');
    name.textContent = t.text;

    const del = document.createElement('button');
    del.className = 'remove-btn';
    del.setAttribute('aria-label', 'Delete item');
    del.textContent = '🗑';

    li.append(name, del);
    list.appendChild(li);
  }
  updateCounter();
}

// ----- ACTIONS -----
function addTodo(text) {
  todos.push({ id: crypto.randomUUID(), text, done: false });
  saveToStorage(todos);
  render();
}

function toggleTodo(id) {
  const t = todos.find(x => x.id === id);
  if (!t) return;
  t.done = !t.done;
  saveToStorage(todos);
  render();
}

function removeTodo(id) {
  todos = todos.filter(x => x.id !== id);
  saveToStorage(todos);
  render();
}

function clearDone() {
  if (!todos.some(t => t.done)) return;
  if (!confirm('Delete all completed items?')) return;
  todos = todos.filter(t => !t.done);
  saveToStorage(todos);
  render();
}

// ----- EVENTS (delegation + keyboard) -----
list.addEventListener('click', (e) => {
  const delBtn = e.target.closest('.remove-btn');
  if (delBtn) {
    const id = delBtn.closest('.todo-item')?.dataset.id;
    if (id) removeTodo(id);
    return;
  }
  const item = e.target.closest('.todo-item');
  if (!item || !list.contains(item)) return;
  toggleTodo(item.dataset.id);
});

list.addEventListener('keydown', (e) => {
  const item = e.target.closest('.todo-item');
  if (!item) return;
  const k = e.key.toLowerCase();
  if (k === ' ' || k === 'enter') {
    e.preventDefault();
    toggleTodo(item.dataset.id);
  } else if (k === 'delete' || k === 'backspace') {
    e.preventDefault();
    removeTodo(item.dataset.id);
  }
});

form.addEventListener('submit', (e) => {
  e.preventDefault();
  const value = input.value.trim();
  if (!value) return;
  addTodo(value);
  input.value = '';
  input.focus();
});

clearDoneBtn.addEventListener('click', clearDone);

// Initial render
render();

💾 How localStorage works (quick recap)

  • Per-site key–value store, string-only; we use JSON.stringify()/JSON.parse() for arrays/objects.
  • Persistent: data survives reloads and browser restarts (until cleared).
  • Synchronous: keep payloads small and focused (a to-do list is perfect).

🚫 Common mistakes

  • Forgetting to call saveToStorage() after changes → nothing persists.
  • Overwriting className instead of using classList.toggle() → you might remove other classes by accident.
  • Not updating aria-checked when toggling .done → visually OK but not accessible.
  • Attaching listeners to each <li> instead of using event delegation on the <ul>.

🧪 Exercises

  1. Export/Import JSON: add buttons to download the list as .json and re-upload it later.
  2. Filter: “All / Active / Completed” using CSS classes (don’t remove from the data model).
  3. Inline edit: double-click a task to edit its text; save on Enter/blur.

📌 Key takeaways

  • The DOM is the live structure your JS manipulates.
  • localStorage gives you persistence without a backend.
  • Keep a single source of truth (the todos array), then render the DOM from it.
  • Accessibility + persistence = a simple, real-world-ready app.
Post Views: 478

Condividi:

  • Condividi su Facebook (Si apre in una nuova finestra) Facebook
  • Condividi su X (Si apre in una nuova finestra) X
Coding Javascript AccessibilityARIADOMEvent DelegationlocalStorage

Navigazione articoli

Previous post
Next post

Francesco

My name is Francesco Boschi, originally from Italy and currently based in the United States. For over twenty years, I’ve worked as a manager and consultant across diverse sectors — from education and cultural institutions to the food industry — developing skills in operational management, strategic consulting, and complex problem-solving. In recent years, I’ve combined this experience with a strong passion for software development, creating custom tools designed to simplify workflows and meet real business needs.

Relocating to the U.S. marks the beginning of a new chapter: a personal and professional decision driven by the desire to be close to my son and to embrace new challenges in a different environment. Today, my goal is to turn my experience into meaningful solutions, blending strategic vision with technical expertise to help people and organizations work more effectively.

I enjoy moving between different worlds, adapting tools and approaches to people and contexts. I bring leadership, flexibility, attention to detail, analytical thinking, and a strong problem-solving mindset — along with a deep curiosity to learn and grow. Above all, I believe in sharing: I’m always eager to offer my experience to support the growth of others.

Related Posts

Coding

Coding – Step 11.3 – Rust from Zero to “WoW – BMI Calculation and Conditional Logic

Posted on 7 Ottobre 20257 Ottobre 2025

Learn how to calculate and interpret the Body Mass Index (BMI) using Rust. In this lesson, you’ll master input validation, conditional logic (if/else, match), and error handling with Result, while building a practical CLI program step by step.

Condividi:

  • Condividi su Facebook (Si apre in una nuova finestra) Facebook
  • Condividi su X (Si apre in una nuova finestra) X
Read More
Coding

Coding – MyBudget: Building a Modular Budget System for Families

Posted on 20 Ottobre 202520 Ottobre 2025

A Laravel-based budgeting app designed for families, with multi-timeframe tracking, modular categories, secure onboarding, and automated email reports. Built from real-world enterprise experience, adapted for everyday life.”
tech=”Laravel, PHP, MySQL, HTML5

Condividi:

  • Condividi su Facebook (Si apre in una nuova finestra) Facebook
  • Condividi su X (Si apre in una nuova finestra) X
Read More
Javascript

Coding – Step 9.4 – JavaScript from Zero – Add Dynamic CSS Classes

Posted on 9 Agosto 202517 Agosto 2025

Mark items as complete by toggling CSS classes (and keep the DOM accessible & tidy) 🧠 Why this lesson matters After building the basic To-Do List with menu and core actions, it’s time to make it interactive in the browser. With just a few lines of JavaScript, we can: 🎯…

Condividi:

  • Condividi su Facebook (Si apre in una nuova finestra) Facebook
  • Condividi su X (Si apre in una nuova finestra) X
Read More

Iscriviti alla nostra Newsletter

🤞 Let's keep in touch

We do not send spam! Read our Privacy policy for more information.

Controlla la tua casella di posta o la cartella spam per confermare la tua iscrizione

Cerca nel sito

©2026 This is my space, where experience meets the will to start over. | WordPress Theme by SuperbThemes