JavaScript
3 Ways to Store Data in the Browser
script.js
// AZUL CODING ---------------------------------------
// JavaScript - 3 Ways to Store Data in the Browser
// https://youtu.be/tkMD51-KMfk
let db;
let editingUserId = null;
const DB_NAME = "UserDatabase";
const DB_VERSION = 1;
const STORE_NAME = "users";
const SAMPLE_USERS = "/users.json";
const userList = document.getElementById("user-list");
const clearDbBtn = document.getElementById("clear-database-btn");
const loadSampleBtn = document.getElementById("load-sample-btn");
const addUserBtn = document.getElementById("add-user-btn");
const toggleThemeBtn = document.getElementById("theme-btn");
const statusEl = document.getElementById("status");
const dbInfoEl = document.getElementById("entry-count");
const newNameInput = document.getElementById("new-name");
const newEmailInput = document.getElementById("new-email");
const newAgeInput = document.getElementById("new-age");
function initDatabase() {
return new Promise((resolve, reject) => {
const request = indexedDB.open(DB_NAME, DB_VERSION);
request.onerror = (event) => {
showStatus("Failed to open database");
reject(event.target.errorCode);
};
request.onsuccess = (event) => {
db = event.target.result;
showStatus("Database opened successfully");
updateDbInfo();
loadUsers();
resolve(db);
};
request.onupgradeneeded = (event) => {
const db = event.target.result;
const objectStore = db.createObjectStore(STORE_NAME, {
keyPath: "id",
autoIncrement: true
});
objectStore.createIndex("name", "name", { unique: false });
objectStore.createIndex("email", "email", { unique: true });
objectStore.createIndex("age", "age", { unique: false });
showStatus("Database setup complete");
};
});
}
function loadUsers() {
userList.innerHTML = "";
const transaction = db.transaction([STORE_NAME], "readonly");
const objectStore = transaction.objectStore(STORE_NAME);
const request = objectStore.openCursor();
request.onerror = () => {
showStatus("Error fetching users");
};
request.onsuccess = (event) => {
const cursor = event.target.result;
if (cursor) {
const user = cursor.value;
const row = document.createElement("tr");
row.setAttribute("data-id", user.id);
row.innerHTML = `
<td>${user.name}</td>
<td>${user.email}</td>
<td>${user.age || "N/A"}</td>
<td>
<button type="button" class="edit">Edit</button>
<button type="button" class="delete">Delete</button>
</td>
`;
row.querySelector("button.edit").addEventListener("click", () => {
startEditing(row, user);
});
row.querySelector("button.delete").addEventListener("click", () => {
deleteUser(user.id);
});
userList.appendChild(row);
cursor.continue();
}
};
updateDbInfo();
}
/* editing users */
function startEditing(row, user) {
if (editingUserId !== null && editingUserId !== user.id) {
cancelEditing();
}
editingUserId = user.id;
row.innerHTML = `
<td><input type="text" class="edit-name" value="${user.name}" required></td>
<td><input type="email" class="edit-email" value="${user.email}" required></td>
<td><input type="number" class="edit-age" value="${user.age || ""}" min="0" max="120"></td>
<td>
<button class="edit">Save</button>
<button class="cancel">Cancel</button>
</td>
`;
row.querySelector("button.edit").addEventListener("click", () => {
saveEdits(row, user.id);
});
row.querySelector("button.cancel").addEventListener("click", () => {
cancelEditing();
});
}
function saveEdits(row, userId) {
const nameInput = row.querySelector(".edit-name");
const emailInput = row.querySelector(".edit-email");
const ageInput = row.querySelector(".edit-age");
const userData = {
id: userId,
name: nameInput.value,
email: emailInput.value,
age: ageInput.value ? parseInt(ageInput.value) : null
};
saveUser(userData)
.then(() => {
showStatus("User updated successfully");
loadUsers();
editingUserId = null;
})
.catch(error => {
showStatus(error);
});
}
function cancelEditing() {
loadUsers();
editingUserId = null;
}
function addUser() {
const userData = {
name: newNameInput.value,
email: newEmailInput.value,
age: newAgeInput.value ? parseInt(newAgeInput.value) : null
};
if (!userData.name || !userData.email) {
showStatus("Name and email are required fields");
return;
}
saveUser(userData)
.then(() => {
showStatus("User added successfully");
loadUsers();
newNameInput.value = "";
newEmailInput.value = "";
newAgeInput.value = "";
})
.catch(error => {
showStatus(error);
});
}
function saveUser(userData) {
return new Promise((resolve, reject) => {
const transaction = db.transaction([STORE_NAME], "readwrite");
const objectStore = transaction.objectStore(STORE_NAME);
let request;
if (userData.id) {
// Update existing user
request = objectStore.put(userData);
} else {
// Add new user - remove empty id to allow auto-increment
delete userData.id;
request = objectStore.add(userData);
}
request.onerror = (event) => {
const error = event.target.error;
if (error.name === "ConstraintError") {
reject("Email already exists");
} else {
reject("Error saving user: " + error);
}
};
request.onsuccess = (event) => {
resolve(event.target.result);
};
});
}
function deleteUser(id) {
if (!confirm("Are you sure you want to delete this user?")) return;
const transaction = db.transaction([STORE_NAME], "readwrite");
const objectStore = transaction.objectStore(STORE_NAME);
const request = objectStore.delete(id);
request.onerror = () => {
showStatus("Error deleting user");
};
request.onsuccess = () => {
showStatus("User deleted successfully");
loadUsers();
};
}
/* database management */
function clearDatabase() {
if (!confirm("Are you sure you want to delete all users?")) return;
const transaction = db.transaction([STORE_NAME], "readwrite");
const objectStore = transaction.objectStore(STORE_NAME);
const request = objectStore.clear();
request.onerror = () => {
showStatus("Error clearing database");
};
request.onsuccess = () => {
showStatus("Database cleared successfully");
loadUsers();
};
}
function getSampleData() {
return new Promise((resolve) => {
caches.match(SAMPLE_USERS).then(async (response) => {
if (response) {
resolve(await response.json());
return;
}
fetch(SAMPLE_USERS).then(response => {
if (!response.ok) {
showStatus("Failed to fetch sample data");
return;
}
caches.open(STORE_NAME).then(async (cache) => {
cache.put(SAMPLE_USERS, response.clone());
resolve(await response.json());
});
});
});
});
}
async function loadSampleData() {
const sampleUsers = await getSampleData();
const transaction = db.transaction([STORE_NAME], "readwrite");
const objectStore = transaction.objectStore(STORE_NAME);
let successCount = 0;
sampleUsers.forEach(user => {
const request = objectStore.put(user);
request.onsuccess = () => {
successCount++;
if (successCount === sampleUsers.length) {
showStatus("Sample data loaded successfully");
loadUsers();
}
};
request.onerror = () => {
showStatus("Sample data already loaded");
};
});
}
/* utility functions */
function updateDbInfo() {
const transaction = db.transaction([STORE_NAME], "readonly");
const objectStore = transaction.objectStore(STORE_NAME);
const countRequest = objectStore.count();
countRequest.onsuccess = () => {
dbInfoEl.textContent = `${countRequest.result} entries`;
};
}
function showStatus(message) {
statusEl.textContent = message;
}
function toggleTheme() {
const lightMode = document.documentElement.classList.toggle("light");
// Watch this video to understand the differences between localStorage and sessionStorage:
// https://youtu.be/0taLxS1xadM
localStorage.setItem("theme", lightMode ? "light" : "dark");
// sessionStorage.setItem("theme", lightMode ? "light" : "dark");
}
const savedTheme = localStorage.getItem("theme");
if (savedTheme === "light") {
document.documentElement.classList.add("light");
}
addUserBtn.addEventListener("click", addUser);
clearDbBtn.addEventListener("click", clearDatabase);
loadSampleBtn.addEventListener("click", loadSampleData);
toggleThemeBtn.addEventListener("click", toggleTheme);
initDatabase().catch(error => {
console.error("Database initialization failed:", error);
});
users.json
// AZUL CODING ---------------------------------------
// JavaScript - 3 Ways to Store Data in the Browser
// https://youtu.be/tkMD51-KMfk
[
{ "name": "John Doe", "email": "john@example.com", "age": 32 },
{ "name": "Jane Smith", "email": "jane@example.com", "age": 28 },
{ "name": "Bob Johnson", "email": "bob@example.com", "age": 45 },
{ "name": "Alice Brown", "email": "alice@example.com", "age": 24 }
]
index.html
<!-- AZUL CODING --------------------------------------- -->
<!-- JavaScript - 3 Ways to Store Data in the Browser -->
<!-- https://youtu.be/tkMD51-KMfk -->
<!DOCTYPE html>
<html lang="en">
<head>
<title>Azul Coding</title>
<style>
:root {
--back-colour: #03506E;
--text-colour: white;
}
.light {
--back-colour: white;
--text-colour: #03506E;
}
body {
margin: 30px;
background-color: var(--back-colour);
color: var(--text-colour);
}
* {
font-family: 'Golos Text', system-ui, sans-serif;
font-weight: 500;
font-size: 18px;
}
h1 {
font-size: 32px;
font-weight: 700;
margin-bottom: 0;
}
#status {
border: 2px solid #49c8fc;
padding: 8px 10px;
}
input {
padding: 5px 8px;
width: 100%;
box-sizing: border-box;
}
button {
background-color: var(--text-colour);
color: var(--back-colour);
border: none;
padding: 6px 12px;
cursor: pointer;
display: inline;
}
button:hover {
background-color: #49c8fc;
}
.actions {
display: flex;
gap: 10px;
margin: 40px 0;
padding-top: 15px;
border-top: 2px solid #49c8fc;
}
table {
border: none;
width: 100%;
margin: 20px auto;
color: var(--text-colour);
border-collapse: collapse;
}
th {
font-weight: 700;
text-align: left;
font-size: 20px;
}
thead th, tbody td {
border-bottom: 1px solid #49c8fc;
}
th, td {
padding: 8px;
}
</style>
<script defer src="/script.js"></script>
</head>
<body>
<h1>User Database</h1>
<p id="entry-count">0 entries</p>
<div id="status" role="status">Loading data...</div>
<table id="user-table">
<thead>
<tr>
<th>Name</th>
<th>Email</th>
<th>Age</th>
<th>Actions</th>
</tr>
</thead>
<tbody id="user-list"></tbody>
<tfoot>
<tr>
<td><input type="text" id="new-name" placeholder="Name" required></td>
<td><input type="email" id="new-email" placeholder="Email" required></td>
<td><input type="number" id="new-age" placeholder="Age" min="0" max="120"></td>
<td><button type="button" id="add-user-btn">Add user</button></td>
</tr>
</tfoot>
</table>
<div class="actions">
<button type="button" id="clear-database-btn">Clear database</button>
<button type="button" id="load-sample-btn">Load sample data</button>
<button type="button" id="theme-btn">Toggle theme</button>
</div>
</body>
</html>