🧭 Day 21 — Mini Project: Notes App with LocalStorage
Build a small but practical Notes app that stores user notes locally. This project covers UI, event handling, storage, editing, deleting, and persistence.
📋 Project overview
Requirements:
- Create, read, update, delete notes (CRUD).
- Persist notes in localStorage so they survive refresh.
- Support edit and delete actions, and simple search.
- Minimal, responsive UI using vanilla JS and CSS.
🧱 HTML structure (starter)
<div class="notes-app">
<input id="noteTitle" placeholder="Title">
<textarea id="noteBody" placeholder="Write your note..."></textarea>
<button id="saveBtn">Save</button>
<input id="search" placeholder="Search notes...">
<div id="notesList"></div>
</div>
⚙️ Core JavaScript (save, render, edit, delete)
const KEY = 'notes:v1';
function loadNotes() {
return JSON.parse(localStorage.getItem(KEY) || '[]');
}
function saveNotes(notes) {
localStorage.setItem(KEY, JSON.stringify(notes));
}
function renderNotes(filter = '') {
const list = document.getElementById('notesList');
list.innerHTML = '';
const notes = loadNotes().filter(n => n.title.includes(filter) || n.body.includes(filter));
notes.forEach(note => {
const el = document.createElement('div');
el.className = 'note';
el.innerHTML = `
<h3>${note.title}</h3>
<p>${note.body}</p>
<button data-id="${note.id}" class="edit">Edit</button>
<button data-id="${note.id}" class="delete">Delete</button>
`;
list.appendChild(el);
});
}
document.getElementById('saveBtn').addEventListener('click', () => {
const title = document.getElementById('noteTitle').value.trim();
const body = document.getElementById('noteBody').value.trim();
if (!title && !body) return;
const notes = loadNotes();
const id = Date.now();
notes.unshift({ id, title, body, updated: id });
saveNotes(notes);
renderNotes();
document.getElementById('noteTitle').value = '';
document.getElementById('noteBody').value = '';
});
// delegate edit/delete
document.getElementById('notesList').addEventListener('click', (e) => {
const id = Number(e.target.dataset.id);
if (e.target.classList.contains('delete')) {
let notes = loadNotes().filter(n => n.id !== id);
saveNotes(notes);
renderNotes();
} else if (e.target.classList.contains('edit')) {
const notes = loadNotes();
const note = notes.find(n => n.id === id);
document.getElementById('noteTitle').value = note.title;
document.getElementById('noteBody').value = note.body;
// remove old one; save will add new version
saveNotes(notes.filter(n => n.id !== id));
renderNotes();
}
});
document.getElementById('search').addEventListener('input', (e) => {
renderNotes(e.target.value.trim());
});
renderNotes();
📐 UI & UX Tips
- Show updated timestamp on each note for clarity.
- Confirm delete with a toast or confirm dialog.
- Keep the editable state minimal: edit in-place or open a modal for rich editing.
- Debounce the search input for larger note sets.
🛡 Edge cases & robustness
- Handle storage full errors—catch
QuotaExceededError.
- Sanitize UI output to avoid accidental HTML injection (use
textContent when inserting user text into the DOM).
- Provide export/import: allow user to download notes JSON and re-upload later.
✨ Enhancements & upgrades
- Offline sync: Keep local notes and sync with server when online.
- Attachments: Use IndexedDB for storing images or larger blobs.
- Tagging & filtering: Add tags and filter by tag or date.
- Encryption: Optionally encrypt notes before saving to localStorage when storing sensitive info (be aware of key storage problem).
📝 Practice tasks
- Add a "favorite" flag and sort favorites up top.
- Implement autosave: every few seconds, save the current draft to localStorage.
- Create import/export JSON UI so users can backup notes.
This Notes app is a great portfolio mini-project: it demonstrates DOM manipulation, state management, and persistence without any backend.
Comments
Post a Comment