Tự làm một ứng dụng web (Web App) quản lý bảng dữ liệu bằng HTMLIndexedDB là một cách tuyệt vời để hiểu cách lưu trữ dữ liệu offline ngay trên trình duyệt mà không cần server.

Dưới đây là hướng dẫn từng bước từ giao diện (HTML/CSS) đến xử lý logic dữ liệu (JavaScript + IndexedDB) để bạn có thể hoàn thành một ứng dụng Data Table hoàn chỉnh.

1. Ý tưởng và Cấu trúc dự án

Chúng ta sẽ tạo một ứng dụng quản lý danh sách nhân viên (hoặc học sinh) gồm các chức năng: Xem, Thêm, Xóa.

Dự án chỉ cần duy nhất 1 file index.html chứa cả HTML, CSS và JavaScript để bạn dễ quản lý và chạy ngay lập tức.

2. Mã nguồn hoàn chỉnh (index.html)

Bạn hãy tạo một file tên là index.html, copy toàn bộ đoạn code dưới đây và lưu lại:

HTML
<!DOCTYPE html>
<html lang="vi">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Web App Data Table với IndexedDB</title>
    <style>
        body {
            font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
            margin: 20px;
            background-color: #f4f7f6;
            color: #333;
        }
        h2 { color: #2c3e50; }
        
        /* Form styles */
        .form-container {
            background: white;
            padding: 20px;
            border-radius: 8px;
            box-shadow: 0 4px 6px rgba(0,0,0,0.1);
            margin-bottom: 20px;
            display: flex;
            gap: 10px;
            flex-wrap: wrap;
            align-items: center;
        }
        .form-container input {
            padding: 10px;
            border: 1px solid #ccc;
            border-radius: 4px;
            font-size: 14px;
            flex: 1;
            min-width: 150px;
        }
        .form-container button {
            padding: 10px 20px;
            background-color: #2ecc71;
            color: white;
            border: none;
            border-radius: 4px;
            cursor: pointer;
            font-weight: bold;
            transition: background 0.2s;
        }
        .form-container button:hover { background-color: #27ae60; }

        /* Table styles */
        table {
            width: 100%;
            border-collapse: collapse;
            background: white;
            border-radius: 8px;
            overflow: hidden;
            box-shadow: 0 4px 6px rgba(0,0,0,0.1);
        }
        th, td {
            padding: 12px 15px;
            text-align: left;
            border-bottom: 1px solid #ddd;
        }
        th {
            background-color: #34495e;
            color: white;
        }
        tr:hover { background-color: #f1f1f1; }
        
        /* Action buttons */
        .btn-delete {
            padding: 6px 12px;
            background-color: #e74c3c;
            color: white;
            border: none;
            border-radius: 4px;
            cursor: pointer;
        }
        .btn-delete:hover { background-color: #c0392b; }
    </style>
</head>
<body>

    <h2>Quản lý Danh sách Nhân viên (IndexedDB)</h2>

    <div class="form-container">
        <input type="text" id="empName" placeholder="Họ và tên" required>
        <input type="text" id="empRole" placeholder="Chức vụ" required>
        <input type="email" id="empEmail" placeholder="Email" required>
        <button onclick="addEmployee()">Thêm mới</button>
    </div>

    <table>
        <thead>
            <tr>
                <th>ID</th>
                <th>Họ và tên</th>
                <th>Chức vụ</th>
                <th>Email</th>
                <th>Hành động</th>
            </tr>
        </thead>
        <tbody id="tableBody">
            </tbody>
    </table>

    <script>
        // 1. KHỞI TẠO CƠ SỞ DỮ LIỆU INDEXEDDB
        let db;
        const request = indexedDB.open("EmployeeDB", 1);

        // Tạo cấu trúc database nếu chạy lần đầu hoặc nâng cấp version
        request.onupgradeneeded = function(event) {
            db = event.target.result;
            // Tạo một 'Object Store' tương tự như bảng trong SQL. 
            // keyPath: "id" tự động tăng (autoIncrement) làm khóa chính.
            const objectStore = db.createObjectStore("employees", { keyPath: "id", autoIncrement: true });
            
            // Tạo các trường chỉ mục để tìm kiếm nếu cần (tùy chọn)
            objectStore.createIndex("name", "name", { unique: false });
        };

        request.onsuccess = function(event) {
            db = event.target.result;
            console.log("Kết nối IndexedDB thành công!");
            displayData(); // Tải dữ liệu lên bảng khi mở app
        };

        request.onerror = function(event) {
            console.error("Lỗi kết nối IndexedDB: ", event.target.errorCode);
        };

        // 2. CHỨC NĂNG THÊM DỮ LIỆU
        function addEmployee() {
            const name = document.getElementById("empName").value.trim();
            const role = document.getElementById("empRole").value.trim();
            const email = document.getElementById("empEmail").value.trim();

            if (!name || !role || !email) {
                alert("Vui lòng điền đầy đủ thông tin!");
                return;
            }

            // Tạo một Transaction (giao dịch) có quyền ghi (readwrite)
            const transaction = db.transaction(["employees"], "readwrite");
            const objectStore = transaction.objectStore("employees");

            const newEmployee = { name, role, email };
            const requestAdd = objectStore.add(newEmployee);

            requestAdd.onsuccess = function() {
                // Xóa trống form sau khi thêm thành công
                document.getElementById("empName").value = "";
                document.getElementById("empRole").value = "";
                document.getElementById("empEmail").value = "";
                
                // Cập nhật lại bảng hiển thị
                displayData();
            };

            transaction.oncomplete = function() {
                console.log("Đã thêm nhân viên thành công.");
            };
        }

        // 3. CHỨC NĂNG HIỂN THỊ DỮ LIỆU LÊN BẢNG
        function displayData() {
            const tableBody = document.getElementById("tableBody");
            tableBody.innerHTML = ""; // Xóa dữ liệu cũ trên giao diện

            const transaction = db.transaction(["employees"], "readonly");
            const objectStore = transaction.objectStore("employees");

            // Sử dụng Cursor (con trỏ) để duyệt qua từng dòng dữ liệu
            objectStore.openCursor().onsuccess = function(event) {
                const cursor = event.target.result;
                if (cursor) {
                    const row = document.createElement("tr");
                    row.innerHTML = `
                        <td>${cursor.value.id}</td>
                        <td>${cursor.value.name}</td>
                        <td>${cursor.value.role}</td>
                        <td>${cursor.value.email}</td>
                        <td><button class="btn-delete" onclick="deleteEmployee(${cursor.value.id})">Xóa</button></td>
                    `;
                    tableBody.appendChild(row);
                    cursor.continue(); // Đi tới dòng tiếp theo
                } else {
                    console.log("Đã hiển thị toàn bộ dữ liệu.");
                }
            };
        }

        // 4. CHỨC NĂNG XÓA DỮ LIỆU
        function deleteEmployee(id) {
            if (confirm("Bạn có chắc chắn muốn xóa nhân viên này không?")) {
                const transaction = db.transaction(["employees"], "readwrite");
                const objectStore = transaction.objectStore("employees");
                
                const requestDelete = objectStore.delete(id);

                requestDelete.onsuccess = function() {
                    console.log("Xóa thành công ID: " + id);
                    displayData(); // Cập nhật lại bảng
                };
            }
        }
    </script>
</body>
</html>

3. Cách chạy ứng dụng

  1. Click đúp chuột vào file index.html bạn vừa lưu để mở trực tiếp bằng trình duyệt (Chrome, Edge, Firefox...).

  2. Nhập thông tin vào các ô: Họ và tên, Chức vụ, Email rồi ấn Thêm mới.

  3. Thử F5 (Refresh) lại trang web. Bạn sẽ thấy dữ liệu không bị mất đi vì đã được lưu vào IndexedDB của trình duyệt.

4. Cách kiểm tra Database "ẩn" trong trình duyệt

Để xem dữ liệu thực tế đang nằm ở đâu trong trình duyệt của bạn:

  1. Nhấn nút F12 (hoặc chuột phải chọn Inspect / Kiểm tra).

  2. Chuyển sang tab Application (Ứng dụng).

  3. Ở menu bên trái, tìm mục Storage -> IndexedDB -> EmployeeDB -> employees.

  4. Bạn sẽ thấy danh sách các Object kèm theo ID tự động tăng mà bạn vừa thêm vào. Bạn cũng có thể xóa trực tiếp dữ liệu tại đây.

Web App Data Table với IndexedDB

Quản lý Danh sách Nhân viên (IndexedDB)

ID Họ và tên Chức vụ Email Hành động