Bài 13: JavaScript Array & Object – Quản lý danh sách (thêm/xóa/tìm kiếm)
🎯 Mục tiêu
- Hiểu Array (mảng) và Object (đối tượng) trong JavaScript
- Biết dùng các hàm mảng:
push,filter,map,find,splice - Biết render (vẽ) danh sách ra HTML bằng DOM
- Tạo mini CRUD: Thêm / Xóa / Tìm kiếm
- (Nâng cao) Lưu dữ liệu bằng
localStorage
📘 Lý thuyết ngắn
1) Array (Mảng): danh sách nhiều phần tử.
const nums = [1, 2, 3];
nums.push(4); // thêm cuối
2) Object (Đối tượng): lưu dữ liệu theo cặp key–value.
const product = { id: 1, name: "Nước suối", price: 10000 };
console.log(product.name); // "Nước suối"
3) Mảng object (rất hay gặp trong web)
const products = [
{ id: 1, name: "Cà phê", price: 25000 },
{ id: 2, name: "Trà sữa", price: 35000 }
];
4) Các hàm mảng quan trọng
map(): biến đổi mảng → mảng mớifilter(): lọc theo điều kiệnfind(): tìm 1 phần tửsplice(): xóa/sửa tại vị trí index
💻 Code mẫu (Mini app quản lý sản phẩm)
<!doctype html>
<html lang="vi">
<head>
<meta charset="utf-8">
<title>Bài 13 - Array & Object CRUD</title>
<style>
body{
font-family: Arial;
background:#f7f8fa;
padding: 24px;
line-height: 1.7;
}
.app{
max-width: 860px;
margin: 0 auto;
background:#fff;
border:1px solid #e5e5e5;
border-radius: 14px;
padding: 18px;
}
h1{ margin-top:0; text-align:center; }
.grid{
display:grid;
grid-template-columns: 1fr 1fr 1fr auto;
gap: 10px;
margin: 12px 0;
}
@media (max-width: 800px){
.grid{ grid-template-columns: 1fr 1fr; }
}
input{
padding:10px 12px;
border:1px solid #ddd;
border-radius: 10px;
width:100%;
box-sizing:border-box;
}
button{
padding:10px 14px;
border:0;
border-radius: 10px;
cursor:pointer;
background:#2563eb;
color:#fff;
font-weight:700;
}
button.gray{ background:#111; }
button.red{ background:#ef4444; }
.toolbar{
display:flex;
gap:10px;
flex-wrap:wrap;
align-items:center;
justify-content: space-between;
margin-top: 8px;
}
.table{
width: 100%;
border-collapse: collapse;
margin-top: 12px;
}
.table th, .table td{
border: 1px solid #ddd;
padding: 10px;
text-align:left;
vertical-align: top;
}
.table th{
background:#111;
color:#fff;
}
.badge{
display:inline-block;
padding:2px 10px;
border-radius: 999px;
background:#eef2ff;
border:1px solid #dbeafe;
font-size: 12px;
margin-right: 6px;
}
.msg{
margin-top: 10px;
padding: 10px 12px;
border-radius: 12px;
background:#f3f4f6;
border:1px solid #e5e7eb;
color:#111;
}
</style>
</head>
<body>
<div class="app">
<h1>🧾 Quản lý sản phẩm (Array + Object)</h1>
<div class="grid">
<input id="name" type="text" placeholder="Tên sản phẩm (vd: Trà sữa)">
<input id="price" type="number" placeholder="Giá (vd: 35000)">
<input id="category" type="text" placeholder="Danh mục (vd: Đồ uống)">
<button id="btnAdd">Thêm</button>
</div>
<div class="toolbar">
<div>
<span class="badge">Thêm</span>
<span class="badge">Xóa</span>
<span class="badge">Tìm kiếm</span>
</div>
<div style="display:flex; gap:10px; flex-wrap:wrap;">
<input id="search" type="text" placeholder="Tìm theo tên..." style="min-width:220px">
<button class="gray" id="btnClear">Xóa hết</button>
</div>
</div>
<table class="table">
<thead>
<tr>
<th>ID</th>
<th>Tên</th>
<th>Danh mục</th>
<th>Giá</th>
<th>Thao tác</th>
</tr>
</thead>
<tbody id="tbody"></tbody>
</table>
<div class="msg" id="msg">Sẵn sàng.</div>
</div>
<script>
// ====== 1) Dữ liệu ban đầu (mảng object) ======
let products = [
{ id: 1, name: "Cà phê", category: "Đồ uống", price: 25000 },
{ id: 2, name: "Trà sữa", category: "Đồ uống", price: 35000 },
{ id: 3, name: "Snack", category: "Ăn vặt", price: 15000 }
];
// ====== 2) Lấy element ======
const elName = document.getElementById("name");
const elPrice = document.getElementById("price");
const elCategory = document.getElementById("category");
const btnAdd = document.getElementById("btnAdd");
const elSearch = document.getElementById("search");
const btnClear = document.getElementById("btnClear");
const tbody = document.getElementById("tbody");
const msg = document.getElementById("msg");
// ====== 3) Helpers ======
function formatMoney(n){
return Number(n).toLocaleString("vi-VN") + " đ";
}
function setMsg(text){
msg.textContent = text;
}
function nextId(){
// lấy id lớn nhất + 1
const maxId = products.length ? Math.max(...products.map(p => p.id)) : 0;
return maxId + 1;
}
// ====== 4) Render ======
function render(list){
tbody.innerHTML = "";
if(list.length === 0){
tbody.innerHTML = '<tr><td colspan="5">Không có dữ liệu.</td></tr>';
return;
}
list.forEach(p => {
const tr = document.createElement("tr");
tr.innerHTML = `
<td>${p.id}</td>
<td>${p.name}</td>
<td>${p.category}</td>
<td>${formatMoney(p.price)}</td>
<td><button class="red" data-id="${p.id}">Xóa</button></td>
`;
tbody.appendChild(tr);
});
// gắn sự kiện xóa cho các nút vừa render
tbody.querySelectorAll("button[data-id]").forEach(btn => {
btn.addEventListener("click", () => {
const id = Number(btn.dataset.id);
removeById(id);
});
});
}
// ====== 5) Thêm ======
function addProduct(){
const name = elName.value.trim();
const category = elCategory.value.trim();
const price = Number(elPrice.value);
if(name.length < 2){
setMsg("❌ Tên sản phẩm quá ngắn.");
return;
}
if(!category){
setMsg("❌ Vui lòng nhập danh mục.");
return;
}
if(!price || price <= 0){
setMsg("❌ Giá không hợp lệ.");
return;
}
const p = { id: nextId(), name, category, price };
products.push(p); // push vào mảng
elName.value = "";
elCategory.value = "";
elPrice.value = "";
setMsg("✅ Đã thêm: " + p.name);
applySearch(); // render theo filter hiện tại
}
// ====== 6) Xóa ======
function removeById(id){
const idx = products.findIndex(p => p.id === id);
if(idx === -1) return;
const removed = products[idx];
products.splice(idx, 1); // xóa 1 phần tử tại idx
setMsg("🗑️ Đã xóa: " + removed.name);
applySearch();
}
// ====== 7) Tìm kiếm ======
function applySearch(){
const key = elSearch.value.trim().toLowerCase();
if(!key){
render(products);
return;
}
const filtered = products.filter(p => p.name.toLowerCase().includes(key));
render(filtered);
}
// ====== 8) Xóa hết ======
function clearAll(){
if(!confirm("Bạn chắc chắn muốn xóa hết dữ liệu?")) return;
products = [];
setMsg("Đã xóa hết dữ liệu.");
applySearch();
}
// ====== 9) Gắn sự kiện ======
btnAdd.addEventListener("click", addProduct);
elSearch.addEventListener("input", applySearch);
btnClear.addEventListener("click", clearAll);
// ====== 10) Render lần đầu ======
render(products);
</script>
</body>
</html>
👉 Copy code → lưu thành bai13.html → mở bằng Chrome.
Thử thêm sản phẩm, gõ tìm kiếm, bấm xóa từng dòng.
✍️ Bài tập
- Thêm cột “Ngày tạo” (tự lấy ngày hiện tại bằng JS).
- Thêm chức năng “Sắp xếp theo giá tăng dần/giảm dần”.
- (Nâng cao) Lưu mảng
productsvàolocalStoragevà tải lại vẫn còn. - (Nâng cao) Thêm nút “Sửa” (edit) cho mỗi dòng.
✅ Đáp án gợi ý
1) Lưu localStorage (gợi ý)
// Lưu
localStorage.setItem("products", JSON.stringify(products));
// Tải
products = JSON.parse(localStorage.getItem("products") || "[]");
2) Sắp xếp tăng dần
products.sort((a,b) => a.price - b.price);
render(products);
📌 Danh sách bình luận