Tutorial: Membuat Blog Sederhana dengan HTML, CSS, & JSON (Tanpa Backend)
Pernahkah Anda ingin membuat sebuah blog sederhana tanpa perlu repot dengan setup database atau bahasa pemrograman backend? Kabar baiknya, Anda bisa! Dengan HTML, CSS, dan sedikit keajaiban JavaScript, kita bisa membuat blog yang kontennya diambil dari file JSON. Ini adalah cara yang bagus untuk fokus pada keterampilan frontend dan memahami bagaimana data bisa dimuat secara dinamis.
1. Pendahuluan
Tujuan Tutorial
Dalam tutorial ini, kita akan membuat sebuah halaman blog sederhana yang menampilkan daftar postingan. Semua data postingan (judul, tanggal, penulis, konten, gambar) akan disimpan dalam sebuah file posts.json
. Kemudian, kita akan menggunakan JavaScript untuk membaca file JSON tersebut dan menampilkannya secara dinamis di halaman HTML.
Cocok untuk Pemula yang Ingin Memahami:
- Struktur HTML Dasar untuk sebuah blog.
- Styling Dasar dengan CSS untuk membuat tampilan yang menarik.
- Memuat Data Dinamis menggunakan JavaScript
fetch()
API dari file JSON. - Manipulasi DOM untuk membuat elemen HTML dari data.
Tools yang Digunakan:
- Browser Web Modern: Chrome, Firefox, Edge, dll.
- Code Editor: VS Code (direkomendasikan), Sublime Text, Atom, atau bahkan Notepad.
- Tidak ada backend, tidak ada database! Murni HTML, CSS, dan JavaScript.
2. đź—‚ Struktur Proyek
Kita akan menjaga struktur proyek tetap minimalis. Buat sebuah folder utama untuk proyek Anda (misalnya, blog-json-sederhana
), lalu di dalamnya buat file dan folder berikut:
/blog-json-sederhana/
├── index.html // Halaman utama blog kita
├── style.css // Untuk semua gaya CSS
├── script.js // Untuk logika JavaScript
├── posts.json // File yang berisi data postingan blog
└── img/ // Folder untuk menyimpan gambar-gambar postingan
├── html.jpg // Contoh gambar (siapkan gambar Anda sendiri)
└── css.jpg // Contoh gambar
Penjelasan Singkat Isi Masing-masing File:
index.html
: Kerangka utama halaman web yang akan menampilkan postingan.style.css
: Berisi semua aturan CSS untuk mempercantik tampilan blog.script.js
: Tempat kita menulis kode JavaScript untuk mengambil data dariposts.json
dan menampilkannya diindex.html
.posts.json
: File teks berformat JSON yang akan menyimpan semua informasi tentang postingan blog kita (judul, tanggal, konten, dll.).img/
: Folder untuk menyimpan gambar-gambar yang akan digunakan dalam postingan.
3. Membuat Halaman HTML Dasar (index.html
)
Mari kita buat struktur dasar untuk index.html
. Ini akan menjadi “wadah” tempat postingan kita akan dimuat oleh JavaScript.
<!DOCTYPE html>
<html lang="id">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Blog Sederhana Saya</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<header class="site-header">
<div class="container">
<h1>Blog Sederhana Saya</h1>
<p>Kumpulan artikel dan tutorial menarik.</p>
</div>
</header>
<main class="site-main">
<div class="container">
<section id="blog-posts-container">
<!-- Postingan akan dimuat di sini oleh JavaScript -->
<p class="loading-message">Memuat postingan...</p>
</section>
</div>
</main>
<footer class="site-footer">
<div class="container">
<p>© <span id="currentYear"></span> Blog Sederhana. Dibuat dengan HTML, CSS, & JSON.</p>
</div>
</footer>
<script src="script.js"></script>
</body>
</html>
Elemen Penting:
<header class="site-header">
: Bagian atas halaman, berisi judul blog.<main class="site-main">
: Konten utama halaman.<section id="blog-posts-container">
: Ini adalah elemen kunci! JavaScript akan menargetkan ID ini untuk memasukkan kartu-kartu postingan.<p class="loading-message">
: Pesan yang ditampilkan saat data sedang dimuat.
<footer class="site-footer">
: Bagian bawah halaman, berisi informasi hak cipta.- Link ke
style.css
di dalam<head>
danscript.js
sebelum penutup</body>
.
4. Styling Blog dengan CSS (style.css
)
Sekarang, mari kita berikan sedikit gaya pada blog kita agar terlihat lebih menarik.
Buat file style.css
dan tambahkan kode berikut:
/* style.css */
/* Reset Dasar dan Global */
*, *::before, *::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
body {
font-family: 'Arial', sans-serif;
line-height: 1.6;
background-color: #f4f7f6;
color: #333;
padding-top: 80px; /* Beri ruang untuk header fixed jika ada */
}
.container {
width: 90%;
max-width: 960px;
margin: 0 auto;
}
/* Header */
.site-header {
background-color: #2c3e50;
color: #ecf0f1;
padding: 20px 0;
text-align: center;
position: fixed; /* Header tetap di atas */
top: 0;
left: 0;
width: 100%;
z-index: 100;
box-shadow: 0 2px 5px rgba(0,0,0,0.1);
}
.site-header h1 {
margin-bottom: 5px;
font-size: 2.5rem;
}
.site-header p {
font-size: 1.1rem;
color: #bdc3c7;
}
/* Main Content & Postingan */
.site-main {
padding: 30px 0;
}
#blog-posts-container {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); /* Layout grid responsif */
gap: 25px; /* Jarak antar kartu postingan */
}
.loading-message, .error-message {
grid-column: 1 / -1; /* Agar pesan mengambil lebar penuh grid */
text-align: center;
font-size: 1.2rem;
color: #7f8c8d;
padding: 20px;
}
.error-message {
color: #e74c3c;
background-color: #f8d7da;
border: 1px solid #f5c6cb;
border-radius: 5px;
}
.post-card {
background-color: #fff;
border-radius: 8px;
box-shadow: 0 4px 12px rgba(0,0,0,0.1);
overflow: hidden; /* Agar gambar tidak meluber */
display: flex;
flex-direction: column;
transition: transform 0.2s ease-in-out, box-shadow 0.2s ease-in-out;
}
.post-card:hover {
transform: translateY(-5px);
box-shadow: 0 8px 20px rgba(0,0,0,0.15);
}
.post-card-image img {
width: 100%;
height: 200px; /* Tinggi gambar tetap */
object-fit: cover; /* Pastikan gambar mengisi area tanpa distorsi */
display: block;
}
.post-card-content {
padding: 20px;
flex-grow: 1; /* Konten mengisi ruang tersedia */
display: flex;
flex-direction: column;
}
.post-card-content h2 {
font-size: 1.5rem;
color: #34495e;
margin-bottom: 10px;
}
.post-card-content .post-meta {
font-size: 0.85rem;
color: #7f8c8d;
margin-bottom: 15px;
}
.post-card-content .post-meta span {
margin-right: 10px;
}
.post-card-content .post-excerpt {
font-size: 0.95rem;
color: #555;
margin-bottom: auto; /* Mendorong tombol "Baca Selengkapnya" ke bawah */
padding-bottom: 15px; /* Spasi sebelum tombol */
}
.read-more-btn {
display: inline-block;
background-color: #3498db;
color: white;
padding: 10px 18px;
text-decoration: none;
border-radius: 5px;
font-weight: 500;
align-self: flex-start; /* Tombol di kiri bawah konten */
transition: background-color 0.2s ease;
margin-top: 10px; /* Jika excerpt pendek */
}
.read-more-btn:hover {
background-color: #2980b9;
}
/* Footer */
.site-footer {
background-color: #34495e;
color: #ecf0f1;
text-align: center;
padding: 20px 0;
margin-top: 30px;
}
/* Responsif Sederhana */
@media (max-width: 768px) {
.site-header h1 {
font-size: 2rem;
}
#blog-posts-container {
grid-template-columns: 1fr; /* Satu kolom di mobile */
}
}
Penjelasan Styling:
- Header Tetap: Header dibuat
position: fixed
agar selalu terlihat di atas.body
diberipadding-top
agar konten utama tidak tertutup header. - Layout Grid:
#blog-posts-container
menggunakandisplay: grid
dengangrid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
. Ini akan membuat kartu postingan menjadi responsif secara otomatis, menampilkan sebanyak mungkin kolom yang muat (dengan lebar minimal 300px) dan menjadi satu kolom di layar kecil. - Kartu Postingan (
.post-card
): Diberi bayangan, border radius, dan efek hover sederhana.display: flex
denganflex-direction: column
digunakan agar tombol “Baca Selengkapnya” bisa didorong ke bawah jika kontennya bervariasi. - Gambar (
.post-card-image img
):object-fit: cover
penting agar gambar mengisi area yang ditentukan tanpa merusak rasio aspeknya.
5. Menyimpan Konten di JSON (posts.json
)
Sekarang, buat file posts.json
di root proyek Anda. Ini akan menjadi “database” kita. Format JSON menggunakan pasangan kunci-nilai (key-value pairs) dan array.
[
{
"id": 1,
"title": "Belajar HTML Dasar: Fondasi Web",
"date": "2025-06-10",
"author": "Lia Candrasari",
"slug": "belajar-html-dasar",
"excerpt": "HTML adalah tulang punggung setiap halaman web. Mari kita pelajari elemen-elemen dasar dan struktur halaman HTML yang benar.",
"content_full": "Ini adalah isi lengkap artikel tentang HTML dasar. Anda akan belajar tentang tag, atribut, elemen block dan inline, serta cara membuat struktur halaman yang semantik...",
"image": "img/html.jpg",
"tags": ["HTML", "Pemula", "Web Dasar"]
},
{
"id": 2,
"title": "Mengenal CSS: Mempercantik Tampilan Web",
"date": "2025-06-12",
"author": "Budi Santoso",
"slug": "mengenal-css",
"excerpt": "CSS (Cascading Style Sheets) adalah bahasa yang digunakan untuk mendeskripsikan presentasi dokumen HTML. Pelajari selector, properti, dan cara membuat web Anda menarik.",
"content_full": "CSS memberikan kekuatan untuk mengubah warna, font, layout, dan banyak lagi. Artikel ini akan membahas konsep dasar CSS, termasuk box model, flexbox, dan grid...",
"image": "img/css.jpg",
"tags": ["CSS", "Desain Web", "Frontend"]
},
{
"id": 3,
"title": "JavaScript untuk Interaktivitas",
"date": "2025-06-15",
"author": "Rina Wijaya",
"slug": "javascript-interaktivitas",
"excerpt": "Tambahkan kehidupan pada website Anda dengan JavaScript! Pelajari dasar-dasar DOM manipulation, event, dan bagaimana membuat halaman lebih dinamis.",
"content_full": "JavaScript adalah bahasa pemrograman yang berjalan di browser pengguna. Dengan JS, Anda bisa memvalidasi form, membuat animasi, mengambil data dari server (AJAX), dan banyak lagi...",
"image": "img/javascript.jpg",
"tags": ["JavaScript", "Interaktif", "Frontend"]
}
]
Penjelasan posts.json
:
- File dimulai dengan
[
dan diakhiri dengan]
, menandakan sebuah array JSON. - Setiap objek di dalam array (dibatasi
{}
) merepresentasikan satu postingan blog. - Setiap postingan memiliki properti seperti
id
,title
,date
,author
,slug
(untuk URL unik jika kita buat halaman detail),excerpt
(ringkasan),content_full
(konten lengkap),image
(path ke gambar), dantags
. - Pastikan path gambar di
image
(misalnya,img/html.jpg
) sesuai dengan lokasi gambar Anda di folderimg/
. Siapkan beberapa gambar contoh di folderimg/
Anda.
6. Menampilkan Postingan dengan JavaScript (script.js
)
Ini adalah bagian di mana keajaiban terjadi! Kita akan menggunakan JavaScript untuk:
- Mengambil (fetch) data dari
posts.json
. - Memproses data JSON.
- Membuat elemen HTML secara dinamis untuk setiap postingan.
- Memasukkan elemen-elemen tersebut ke dalam
#blog-posts-container
diindex.html
.
Buka file script.js
dan tambahkan kode berikut:
// script.js
document.addEventListener('DOMContentLoaded', () => {
const blogPostsContainer = document.getElementById('blog-posts-container');
const loadingMessage = document.querySelector('.loading-message');
// Update tahun di footer
const currentYearElement = document.getElementById('currentYear');
if (currentYearElement) {
currentYearElement.textContent = new Date().getFullYear();
}
// Fungsi untuk membuat satu kartu postingan HTML
function createPostCard(post) {
const card = document.createElement('article');
card.classList.add('post-card');
// Gambar postingan (jika ada)
if (post.image) {
const imageContainer = document.createElement('div');
imageContainer.classList.add('post-card-image');
const img = document.createElement('img');
img.src = post.image;
img.alt = post.title; // Teks alt penting untuk aksesibilitas
imageContainer.appendChild(img);
card.appendChild(imageContainer);
}
// Konten postingan
const contentDiv = document.createElement('div');
contentDiv.classList.add('post-card-content');
const title = document.createElement('h2');
// Jika kita membuat halaman detail, link akan ke sana
// Untuk sekarang, judul tidak bisa diklik
title.textContent = post.title;
contentDiv.appendChild(title);
const meta = document.createElement('p');
meta.classList.add('post-meta');
const dateSpan = document.createElement('span');
// Format tanggal agar lebih mudah dibaca
const postDate = new Date(post.date);
const options = { year: 'numeric', month: 'long', day: 'numeric' };
dateSpan.textContent = `Tanggal: ${postDate.toLocaleDateString('id-ID', options)}`;
meta.appendChild(dateSpan);
if (post.author) {
const authorSpan = document.createElement('span');
authorSpan.textContent = `| Penulis: ${post.author}`;
meta.appendChild(authorSpan);
}
contentDiv.appendChild(meta);
if (post.excerpt) {
const excerpt = document.createElement('p');
excerpt.classList.add('post-excerpt');
excerpt.textContent = post.excerpt;
contentDiv.appendChild(excerpt);
}
// Tombol "Baca Selengkapnya" (opsional, untuk versi ini belum ada halaman detail)
const readMoreBtn = document.createElement('a');
readMoreBtn.classList.add('read-more-btn');
readMoreBtn.textContent = 'Baca Selengkapnya';
// Jika ada halaman detail: readMoreBtn.href = `post.html?slug=${post.slug}`;
readMoreBtn.href = '#'; // Placeholder
contentDiv.appendChild(readMoreBtn);
card.appendChild(contentDiv);
return card;
}
// Mengambil dan menampilkan postingan
async function fetchPosts() {
try {
const response = await fetch('posts.json');
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const posts = await response.json();
// Hapus pesan loading
if (loadingMessage) {
loadingMessage.remove();
}
if (posts.length === 0) {
blogPostsContainer.innerHTML = '<p class="info-message">Tidak ada postingan saat ini.</p>';
return;
}
// Tampilkan setiap postingan
posts.forEach(post => {
const postCardElement = createPostCard(post);
blogPostsContainer.appendChild(postCardElement);
});
} catch (error) {
console.error("Gagal memuat postingan:", error);
if (loadingMessage) {
loadingMessage.remove();
}
blogPostsContainer.innerHTML = `<p class="error-message">Oops! Terjadi kesalahan saat memuat postingan. Silakan coba lagi nanti. Detail: ${error.message}</p>`;
}
}
// Panggil fungsi untuk memuat postingan
fetchPosts();
});
Penjelasan JavaScript:
DOMContentLoaded
: Memastikan skrip berjalan setelah HTML siap.- Seleksi Elemen: Mengambil
#blog-posts-container
dan pesan loading. createPostCard(post)
: Fungsi ini bertanggung jawab untuk membuat struktur HTML satu kartu postingan berdasarkan objekpost
dari JSON.- Menggunakan
document.createElement()
untuk membuat setiap elemen HTML (article, div, img, h2, p, a). - Mengatur atribut (seperti
src
danalt
untuk gambar) dan konten teks. - Menambahkan kelas CSS yang sesuai.
- Menggabungkan elemen-elemen tersebut menjadi struktur kartu.
- Format Tanggal:
new Date(post.date).toLocaleDateString('id-ID', options)
digunakan untuk memformat tanggal agar lebih mudah dibaca dalam bahasa Indonesia.
- Menggunakan
fetchPosts()
: Fungsi utama yang melakukan pekerjaan.- Menggunakan
async/await
untuk penanganan Promise yang lebih bersih denganfetch()
. fetch('posts.json')
: Mengambil data dari fileposts.json
. Penting: Ini akan bekerja dengan baik jika Anda menjalankan halaman HTML melalui server web lokal. Jika Anda membukaindex.html
langsung dari file system (file:///...
),fetch()
mungkin gagal karena kebijakan keamanan browser (CORS). Lihat Langkah 8.response.json()
: Mengubah respons menjadi objek JavaScript.- Menghapus Pesan Loading: Setelah data diterima (atau error), pesan “Memuat postingan…” dihapus.
- Looping Melalui Postingan:
posts.forEach(post => ...)
untuk setiap objek postingan di array JSON. blogPostsContainer.appendChild(postCardElement)
: Menambahkan kartu postingan yang sudah jadi ke dalam kontainer di HTML.- Penanganan Error: Blok
try...catch
menangani potensi error saatfetch
atau parsing JSON, dan menampilkan pesan error yang sesuai.
- Menggunakan
- Memanggil
fetchPosts()
: Fungsi ini dipanggil untuk memulai proses pengambilan dan penampilan data.
7. Tambahan untuk UX (Pengalaman Pengguna)
- Loading State: Kita sudah menambahkan pesan “Memuat postingan…” di HTML dan menghapusnya di JS setelah data dimuat atau gagal.
- Pesan Error: Jika
fetch()
gagal atau fileposts.json
tidak ditemukan/error, pesan error akan ditampilkan kepada pengguna. - Tombol “Baca Selengkapnya”: Saat ini tombol tersebut adalah placeholder (
href="#"
). Jika Anda ingin membuat halaman detail untuk setiap postingan (misalnya,post.html
), Anda bisa memodifikasi link ini menjadi:
Ini memerlukan halaman// Di dalam createPostCard: readMoreBtn.href = `post.html?slug=${post.slug}`; // Atau ?id=${post.id}
post.html
baru dan logika JavaScript di sana untuk mengambil data postingan spesifik berdasarkan parameter URL (misal, slug atau id). Ini adalah pengembangan yang bagus untuk eksplorasi lebih lanjut.
8. Menjalankan di Browser
Anda mungkin tergoda untuk membuka file index.html
langsung di browser Anda (misalnya, dengan double-click). Namun, karena kita menggunakan fetch()
untuk memuat file JSON lokal, ini seringkali tidak akan bekerja karena kebijakan keamanan browser terkait permintaan dari protokol file:///
(CORS - Cross-Origin Resource Sharing).
Solusi: Jalankan Server Lokal Sederhana Cara termudah untuk mengatasi ini adalah dengan menjalankan server web lokal di direktori proyek Anda. Ada banyak cara untuk melakukannya:
-
Menggunakan
npx serve
(jika Anda memiliki Node.js/npm terinstal):- Buka terminal atau command prompt Anda.
- Arahkan (navigate) ke direktori proyek
blog-json-sederhana/
. - Jalankan perintah:
npx serve .
- Terminal akan menampilkan URL lokal (biasanya seperti
http://localhost:3000
atauhttp://localhost:5000
). Buka URL tersebut di browser Anda.
-
Menggunakan Modul
http.server
Python (jika Anda memiliki Python terinstal):- Buka terminal atau command prompt.
- Arahkan ke direktori proyek
blog-json-sederhana/
. - Jalankan perintah (untuk Python 3):
Atau untuk Python 2:python -m http.server
python -m SimpleHTTPServer
- Terminal akan menampilkan URL lokal (biasanya
http://localhost:8000
). Buka URL tersebut.
-
Menggunakan Ekstensi Live Server di VS Code: Jika Anda menggunakan VS Code, ekstensi “Live Server” oleh Ritwick Dey sangat populer. Setelah diinstal, Anda bisa klik kanan pada file
index.html
dan pilih “Open with Live Server”.
Dengan menjalankan server lokal, fetch('posts.json')
akan bekerja dengan benar karena permintaan dibuat melalui protokol http://
.
9. Keterbatasan (dan Kenapa Ini Bukan untuk Situs Produksi Besar)
Meskipun metode ini bagus untuk belajar dan untuk situs statis kecil, penting untuk memahami keterbatasannya:
- Konten Statis: File
posts.json
adalah statis. Untuk mengubah atau menambah postingan, Anda harus mengedit file JSON secara manual dan mengunggah ulang semua file ke server (jika di-hosting). Tidak ada antarmuka admin untuk mengelola konten. - Tidak Ada Fitur Dinamis Backend: Karena tidak ada backend, fitur seperti komentar pengguna, formulir kontak yang mengirim email, pencarian sisi server, atau sistem login pengguna tidak mungkin dilakukan hanya dengan pendekatan ini.
- Skalabilitas: Untuk blog dengan ratusan atau ribuan postingan, memuat dan memproses file JSON besar di sisi klien bisa menjadi tidak efisien dan lambat.
- SEO (Search Engine Optimization): Konten yang dimuat oleh JavaScript mungkin tidak selalu diindeks sebaik konten yang sudah ada di HTML awal oleh semua mesin pencari (meskipun Google sudah cukup baik dalam hal ini).
Tujuan Proyek Ini: Proyek ini dirancang sebagai latihan untuk memahami bagaimana data (dalam format JSON) bisa diambil dan ditampilkan secara dinamis di halaman web menggunakan JavaScript, memberikan dasar untuk memahami aplikasi web yang lebih kompleks.
10. Penutup
Selamat! Anda telah berhasil membuat blog sederhana yang mengambil kontennya dari file JSON. Ini adalah langkah awal yang bagus untuk memahami bagaimana frontend bisa berinteraksi dengan sumber data.
Apa yang Sudah Dipelajari:
- Membuat struktur HTML dasar untuk blog.
- Memberikan styling dengan CSS, termasuk layout grid responsif.
- Membuat dan memahami struktur data JSON untuk menyimpan konten.
- Menggunakan JavaScript
fetch()
API untuk mengambil data dari file JSON. - Manipulasi DOM dengan JavaScript untuk membuat elemen HTML secara dinamis dan menampilkannya di halaman.
- Menangani loading state dan pesan error dasar.
- Pentingnya menjalankan server lokal untuk fitur seperti
fetch()
.
Ajakan untuk Eksplorasi Lebih Lanjut: Proyek ini bisa menjadi dasar untuk banyak pengembangan menarik:
- Membuat Halaman Detail Postingan: Buat halaman
post.html
yang menampilkan konten lengkap satu artikel berdasarkanid
atauslug
dari parameter URL (misalnya,post.html?slug=belajar-html-dasar
). Anda perlu logika JS dipost.html
untuk mengambil data postingan spesifik. - Pagination Manual: Jika Anda memiliki banyak postingan di
posts.json
, implementasikan pagination sederhana dengan JavaScript untuk membagi daftar menjadi beberapa halaman. - Filtering atau Sorting: Tambahkan tombol atau dropdown untuk memfilter postingan berdasarkan tag (jika Anda menambahkannya di JSON) atau mengurutkan berdasarkan tanggal atau judul.
- Styling Lebih Lanjut: Percantik tampilan blog Anda lebih jauh dengan CSS.
- Menggunakan Template Literal JavaScript: Untuk membuat string HTML yang lebih kompleks dengan cara yang lebih mudah dibaca daripada
document.createElement()
berulang kali.
Teruslah bereksperimen dan membangun. Semoga tutorial ini memberikan Anda pemahaman yang lebih baik tentang bagaimana data dan presentasi bisa dipisahkan di frontend!