Bài 14: JavaScript Fetch API – Tải dữ liệu và hiển thị ra giao diện
🎯 Mục tiêu
- Hiểu “API” là gì và vì sao cần gọi API trong web
- Biết dùng
fetch()để lấy dữ liệu dạng JSON - Biết xử lý
then()hoặcasync/await(mình dùng async/await cho dễ học) - Biết render danh sách dữ liệu ra HTML bằng DOM
- Biết xử lý trạng thái: đang tải / lỗi / thành công
📘 Lý thuyết ngắn
1) API là gì?
API là nơi cung cấp dữ liệu cho ứng dụng. Web của bạn có thể “xin dữ liệu” từ API rồi hiển thị ra giao diện. Ví dụ: danh sách bài viết, sản phẩm, đơn hàng...
2) Fetch API
fetch("https://example.com/api")
.then(res => res.json())
.then(data => console.log(data));
3) Async/Await (dễ đọc hơn)
async function load(){
const res = await fetch(url);
const data = await res.json();
}
Lưu ý: Nếu mở file .html offline, một số trình duyệt vẫn cho gọi API HTTPS bình thường.
Nếu máy bạn chặn, hãy thử chạy bằng Live Server hoặc đưa lên Blogspot.
💻 Code mẫu (Tải bài viết từ API và hiển thị)
<!doctype html>
<html lang="vi">
<head>
<meta charset="utf-8">
<title>Bài 14 - Fetch API</title>
<style>
body{
font-family: Arial;
background:#f7f8fa;
padding: 24px;
line-height: 1.7;
}
.app{
max-width: 900px;
margin: 0 auto;
background:#fff;
border:1px solid #e5e5e5;
border-radius: 14px;
padding: 18px;
}
h1{ margin-top:0; text-align:center; }
.toolbar{
display:flex;
gap:10px;
flex-wrap:wrap;
justify-content: center;
margin: 12px 0 16px;
}
button{
padding:10px 14px;
border:0;
border-radius: 10px;
cursor:pointer;
background:#2563eb;
color:#fff;
font-weight:700;
}
button.gray{ background:#111; }
input{
padding:10px 12px;
border:1px solid #ddd;
border-radius: 10px;
min-width: 240px;
}
.status{
padding: 10px 12px;
border-radius: 12px;
background:#f3f4f6;
border:1px solid #e5e7eb;
margin-bottom: 12px;
}
.list{
display:grid;
grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
gap: 14px;
}
.card{
border:1px solid #e5e5e5;
border-radius: 12px;
padding: 14px;
background:#fff;
}
.card h3{
margin: 0 0 8px;
font-size: 16px;
}
.card p{
margin: 0;
color:#333;
font-size: 14px;
}
.muted{ color:#555; }
</style>
</head>
<body>
<div class="app">
<h1>🌐 Fetch API - Danh sách bài viết</h1>
<div class="toolbar">
<button id="btnLoad">Tải dữ liệu</button>
<button class="gray" id="btnClear">Xóa hiển thị</button>
<input id="search" type="text" placeholder="Tìm theo tiêu đề...">
</div>
<div class="status" id="status">
<span class="muted">Chưa tải dữ liệu.</span>
</div>
<div class="list" id="list"></div>
</div>
<script>
const API_URL = "https://jsonplaceholder.typicode.com/posts";
const btnLoad = document.getElementById("btnLoad");
const btnClear = document.getElementById("btnClear");
const search = document.getElementById("search");
const statusEl = document.getElementById("status");
const listEl = document.getElementById("list");
let posts = [];
function setStatus(text){
statusEl.innerHTML = text;
}
function render(list){
listEl.innerHTML = "";
if(list.length === 0){
listEl.innerHTML = "<p class='muted'>Không có dữ liệu để hiển thị.</p>";
return;
}
list.forEach(p => {
const div = document.createElement("div");
div.className = "card";
div.innerHTML = `
<h3>${p.id}. ${p.title}</h3>
<p>${p.body}</p>
`;
listEl.appendChild(div);
});
}
async function loadPosts(){
try{
setStatus("⏳ Đang tải dữ liệu...");
const res = await fetch(API_URL);
if(!res.ok){
throw new Error("HTTP " + res.status);
}
const data = await res.json();
// lấy 12 bài cho gọn
posts = data.slice(0, 12);
setStatus("✅ Tải thành công: <b>" + posts.length + "</b> bài viết.");
render(posts);
} catch(err){
setStatus("❌ Lỗi khi tải dữ liệu: <b>" + err.message + "</b>");
}
}
function applySearch(){
const key = search.value.trim().toLowerCase();
if(!key){
render(posts);
return;
}
const filtered = posts.filter(p => p.title.toLowerCase().includes(key));
render(filtered);
}
btnLoad.addEventListener("click", loadPosts);
btnClear.addEventListener("click", () => {
posts = [];
listEl.innerHTML = "";
search.value = "";
setStatus("<span class='muted'>Đã xóa hiển thị.</span>");
});
search.addEventListener("input", applySearch);
</script>
</body>
</html>
👉 Copy code → lưu thành bai14.html → mở bằng Chrome.
Bấm “Tải dữ liệu”, sau đó gõ tìm kiếm theo tiêu đề.
✍️ Bài tập
- Đổi API sang
https://jsonplaceholder.typicode.com/usersvà hiển thị tên + email. - Thêm dropdown chọn số lượng hiển thị (5/10/20).
- (Nâng cao) Thêm nút “Tải lại” và hiển thị thời gian tải (giờ:phút:giây).
- (Nâng cao) Tạo modal xem chi tiết bài viết khi click card.
✅ Đáp án gợi ý
1) Đổi sang users
const API_URL = "https://jsonplaceholder.typicode.com/users";
...
div.innerHTML = `
<h3>${u.name}</h3>
<p>${u.email}</p>
`;
2) Lấy thời gian tải (gợi ý)
const t = new Date();
const timeText = t.toLocaleTimeString("vi-VN");
setStatus("✅ Tải xong lúc: <b>" + timeText + "</b>");
📌 Danh sách bình luận