Membangun Aplikasi Web Interaktif: Kalkulator dan Page Builder Andal dengan HTML, CSS, dan JavaScript

ikramlink April 15, 2026
Membangun Aplikasi Web Interaktif: Kalkulator dan Page Builder Andal dengan HTML, CSS, dan JavaScript
Oh, betapa kesalnya diriku kemarin! Tepat saat deadline sebuah proyek pribadi, tiba-tiba saja elemen tombol pada kalkulator web yang sedang kubuat tak mau merespon sama sekali. Sudah kuperiksa log console, semua sintaks JavaScript terlihat benar, tapi tetap saja macet. Ternyata, ada satu variabel kecil yang lupa kudeclare di dalam sebuah fungsi callback, bikin seluruh alur eksekusi jadi kacau balau. Rasanya seperti sedang merakit mesin mobil canggih, semua komponen sudah terpasang rapi, tapi lupa memasang satu baut kecil di bagian krusial, alhasil mesinnya tidak mau menyala. Nah, pengalaman seperti inilah yang sering dihadapi para developer, tapi justru dari situlah kita belajar dan semakin kuat, bukan? Hari ini, mari kita selami dunia pengembangan web yang seru dengan membangun dua aplikasi yang seringkali menjadi batu loncatan bagi para pemula (dan bahkan programmer berpengalaman untuk prototyping cepat): aplikasi web kalkulator sederhana dan, yang sedikit lebih ambisius, page builder visual berbasis web. Kita akan mengandalkan tiga pilar utama: HTML untuk struktur, CSS untuk estetika, dan JavaScript untuk logika interaktifnya. Bayangkan ini seperti kita sedang mendirikan sebuah kedai kopi. HTML adalah fondasi bangunannya, tata letaknya, ruangan-ruangannya. CSS adalah cat dindingnya, pemilihan furnitur, pencahayaannya, agar kedai itu terlihat menarik dan nyaman. Sementara JavaScript adalah pelayan yang sigap mengambil pesanan, meracik kopi sesuai permintaan, dan mengantarkannya ke meja pelanggan. Semuanya bekerja sama menciptakan pengalaman yang memuaskan.

Fondasi yang Kokoh: HTML untuk Struktur

Sebelum kita mulai bermain dengan warna dan gaya, mari kita siapkan "kerangka" dari kedua aplikasi kita. HTML (HyperText Markup Language) adalah tulang punggung dari setiap halaman web. Tanpanya, kita hanya akan melihat teks polos tanpa bentuk.

Kalkulator Sederhana: Membangun Antarmuka Numerik

Untuk kalkulator, kita membutuhkan area tampilan untuk angka dan hasil, serta tombol-tombol angka (0-9), operator (+, -, *, /), tombol sama dengan (=), dan tombol hapus (C). ```html
``` Perhatikan penggunaan `div` untuk mengelompokkan elemen, `input type="text"` untuk layar tampilan, dan `button` untuk setiap tombol. Atribut `data-action` akan sangat berguna nanti saat kita menambahkan JavaScript untuk mengidentifikasi tombol mana yang ditekan.

Page Builder: Komponen untuk Membangun Halaman

Untuk page builder, strukturnya akan sedikit berbeda. Kita memerlukan area kerja utama (canvas) tempat pengguna akan membangun halaman, dan sebuah panel samping (sidebar) yang berisi berbagai komponen yang bisa ditambahkan, seperti teks, gambar, tombol, dan kolom. ```html
``` Di sini, kita menggunakan `sidebar` untuk menampung daftar komponen. Setiap `component-item` memiliki atribut `draggable="true"` agar bisa diseret (drag and drop) ke area `canvas`. Atribut `data-component-type` akan memberi tahu JavaScript jenis komponen apa yang sedang di-drag. Untuk ikon, saya menyertakan placeholder ``. Anda perlu menyertakan Font Awesome CDN di bagian `` HTML Anda agar ikon-ikon ini tampil.

Sentuhan Artistik: CSS untuk Estetika

Setelah kerangka terbangun, saatnya kita mempercantik tampilan. CSS (Cascading Style Sheets) adalah "salon kecantikan" untuk web Anda. Tanpa CSS, halaman web kita akan terlihat seperti resep masakan yang hanya berisi daftar bahan tanpa instruksi memasak yang rapi.

Kalkulator: Fungsional dan Menarik

Kita ingin kalkulator kita terlihat bersih, modern, dan mudah digunakan. ```css .calculator { width: 320px; margin: 50px auto; border: 1px solid #ccc; border-radius: 10px; box-shadow: 0 0 20px rgba(0, 0, 0, 0.1); overflow: hidden; } .calculator-display { width: calc(100% - 20px); padding: 10px; background-color: #222; color: white; font-size: 2em; text-align: right; border: none; outline: none; } .calculator-keys { display: grid; grid-template-columns: repeat(4, 1fr); gap: 1px; /* Membuat garis pemisah antar tombol */ background-color: #ccc; } .calculator-keys button { padding: 20px; font-size: 1.2em; border: none; background-color: #f9f9f9; cursor: pointer; transition: background-color 0.3s ease; } .calculator-keys button:hover { background-color: #ddd; } .calculator-keys button.operator { background-color: #f0ad4e; color: white; } .calculator-keys button.operator:hover { background-color: #ec971f; } .calculator-keys button.equal-sign { background-color: #5cb85c; color: white; grid-column: span 2; /* Tombol '=' mencakup 2 kolom */ } .calculator-keys button.equal-sign:hover { background-color: #4cae4c; } .calculator-keys button[data-action="clear"] { background-color: #d9534f; color: white; } .calculator-keys button[data-action="clear"]:hover { background-color: #c9302c; } ``` Dengan menggunakan CSS Grid, kita bisa dengan mudah mengatur tata letak tombol kalkulator agar menyerupai kalkulator fisik.

Page Builder: UI yang Intuitif

Untuk page builder, kita ingin antarmuka yang bersih, modern, dan responsif. Panel samping harus jelas, dan area kerja harus memberikan ruang yang cukup untuk berkreasi. ```css .page-builder-container { display: flex; height: 100vh; /* Mengisi seluruh tinggi viewport */ } .sidebar { width: 250px; background-color: #f4f7f6; padding: 20px; box-shadow: 2px 0 10px rgba(0, 0, 0, 0.05); overflow-y: auto; } .sidebar h3 { margin-top: 0; color: #333; border-bottom: 1px solid #e0e0e0; padding-bottom: 10px; } .component-item { background-color: #fff; border: 1px solid #e0e0e0; border-radius: 5px; padding: 15px; margin-bottom: 10px; cursor: grab; /* Indikator bahwa elemen bisa diseret */ display: flex; align-items: center; gap: 10px; transition: box-shadow 0.3s ease; } .component-item:hover { box-shadow: 0 0 15px rgba(0, 0, 0, 0.1); } .component-item i { color: #5a5a5a; font-size: 1.2em; } .canvas { flex-grow: 1; /* Mengambil sisa ruang */ padding: 30px; background-color: #fdfdfd; overflow: auto; /* Scroll jika konten melebihi area */ position: relative; /* Penting untuk menempatkan elemen di dalamnya */ } /* Styling untuk elemen yang diletakkan di canvas (akan ditambahkan oleh JS) */ .canvas .component-element { border: 1px dashed #ccc; padding: 15px; margin-bottom: 15px; background-color: #fff; cursor: move; /* Indikator bisa dipindah */ position: absolute; /* Agar bisa diposisikan bebas */ } .canvas .component-element.text-element { width: 100%; /* Default width for text */ height: auto; } .canvas .component-element.image-element { width: 200px; /* Default width for image */ height: 150px; background-color: #e0e0e0; /* Placeholder background */ } .canvas .component-element.button-element { width: auto; height: auto; padding: 10px 20px; background-color: #007bff; color: white; border: none; border-radius: 5px; cursor: pointer; } .canvas .component-element.columns-element { width: 100%; height: 100px; display: flex; gap: 10px; background-color: #e9ecef; border: 1px dashed #adb5bd; } .canvas .component-element.columns-element div { flex: 1; background-color: #dee2e6; border: 1px dashed #ced4da; } ``` Di sini, kita menggunakan Flexbox untuk mengatur tata letak utama (`page-builder-container`). CSS Grid tidak digunakan secara langsung untuk tata letak utama, namun bisa saja digunakan di dalam elemen komponen tertentu, seperti kolom.

Otak di Balik Aksi: JavaScript untuk Interaktivitas

Inilah saatnya membuat kedua aplikasi kita "hidup". JavaScript adalah "mesin" yang menjalankan semua fungsi. Tanpa JavaScript, kalkulator kita hanya tumpukan tombol tanpa fungsi, dan page builder kita hanya tampilan statis tanpa kemampuan membangun.

Kalkulator: Logika Perhitungan

Kita perlu mendengarkan klik pada setiap tombol, mengambil input angka dan operator, melakukan perhitungan, dan menampilkan hasilnya. ```javascript document.addEventListener('DOMContentLoaded', () => { const display = document.querySelector('.calculator-display'); const keys = document.querySelector('.calculator-keys'); let displayedNum = ''; let firstOperand = null; let operator = null; let waitingForSecondOperand = false; keys.addEventListener('click', (e) => { const target = e.target; if (!target.matches('button')) { return; } const action = target.dataset.action; const keyContent = target.textContent; if (action === 'clear') { displayedNum = ''; firstOperand = null; operator = null; waitingForSecondOperand = false; display.value = ''; return; } if (target.classList.contains('operator')) { const inputValue = parseFloat(displayedNum); if (operator && waitingForSecondOperand) { // Jika sudah ada operator dan sedang menunggu operan kedua, lakukan kalkulasi langsung operator = action; return; } if (firstOperand === null) { firstOperand = inputValue; } else if (operator) { const result = calculate(firstOperand, inputValue, operator); display.value = result; firstOperand = result; } waitingForSecondOperand = true; operator = action; displayedNum = ''; // Reset displayedNum untuk input operan kedua return; } if (action === 'decimal') { if (!displayedNum.includes('.')) { displayedNum += '.'; } return; } if (action === 'calculate') { if (firstOperand === null || operator === null) { return; // Tidak bisa menghitung jika belum ada operan atau operator } const secondOperand = parseFloat(displayedNum); const result = calculate(firstOperand, secondOperand, operator); display.value = result; firstOperand = result; operator = null; waitingForSecondOperand = false; displayedNum = String(result); // Hasil menjadi basis untuk perhitungan selanjutnya return; } // Input angka if (waitingForSecondOperand) { displayedNum = keyContent; waitingForSecondOperand = false; } else { displayedNum = displayedNum + keyContent; } display.value = displayedNum; }); function calculate(num1, num2, operator) { if (operator === 'add') return num1 + num2; if (operator === 'subtract') return num1 - num2; if (operator === 'multiply') return num1 * num2; if (operator === 'divide') { if (num2 === 0) return 'Error'; // Pencegahan pembagian dengan nol return num1 / num2; } return undefined; } }); ``` Logika kalkulator ini cukup detail. Kita menyimpan `firstOperand`, `operator`, dan status `waitingForSecondOperand` untuk mengelola alur perhitungan. Setiap tombol memiliki peran spesifik dalam mengubah status ini atau melakukan perhitungan. Ini seperti seorang koki yang mencatat bahan apa saja yang sudah masuk, urutan memasak, dan menunggu bahan berikutnya sebelum melanjutkan ke tahap selanjutnya.

Page Builder: Drag and Drop dan Penambahan Komponen

Untuk page builder, kita perlu mengimplementasikan fungsionalitas drag and drop dari sidebar ke canvas, dan kemudian menambahkan elemen HTML yang sesuai ke canvas. ```javascript document.addEventListener('DOMContentLoaded', () => { const components = document.querySelectorAll('.component-item'); const canvas = document.getElementById('pageCanvas'); let draggedItem = null; // Menyimpan elemen yang sedang diseret // 1. Drag Start: Saat elemen di sidebar mulai diseret components.forEach(component => { component.addEventListener('dragstart', (e) => { draggedItem = component; e.dataTransfer.setData('text/plain', component.dataset.componentType); e.dataTransfer.effectAllowed = 'copy'; // Menandakan ini adalah operasi penyalinan setTimeout(() => { component.style.opacity = '0.5'; // Efek visual saat menyeret }, 0); }); // Mengembalikan opacity elemen setelah drag selesai component.addEventListener('dragend', () => { draggedItem.style.opacity = '1'; draggedItem = null; }); }); // 2. Drag Over: Saat elemen diseret di atas area target (canvas) canvas.addEventListener('dragover', (e) => { e.preventDefault(); // Penting: Mencegah perilaku default agar drop bisa dilakukan e.dataTransfer.dropEffect = 'copy'; // Menampilkan ikon 'copy' saat hover }); // 3. Drop: Saat elemen dilepaskan di area target (canvas) canvas.addEventListener('drop', (e) => { e.preventDefault(); const componentType = e.dataTransfer.getData('text/plain'); // Membuat elemen baru berdasarkan jenis komponen yang di-drop let newElement; switch (componentType) { case 'text': newElement = document.createElement('div'); newElement.classList.add('component-element', 'text-element'); newElement.innerHTML = '

Tulis teks Anda di sini...

'; break; case 'image': newElement = document.createElement('div'); newElement.classList.add('component-element', 'image-element'); newElement.innerHTML = 'Placeholder Image'; // Gunakan gambar placeholder break; case 'button': newElement = document.createElement('div'); newElement.classList.add('component-element', 'button-element'); newElement.innerHTML = ''; break; case 'columns': newElement = document.createElement('div'); newElement.classList.add('component-element', 'columns-element'); newElement.innerHTML = '
'; // Dua kolom default break; default: return; } // Menentukan posisi elemen baru di canvas // Posisi relatif terhadap posisi kursor saat drop const canvasRect = canvas.getBoundingClientRect(); const x = e.clientX - canvasRect.left - 100; // Kurangi setengah lebar perkiraan elemen const y = e.clientY - canvasRect.top - 50; // Kurangi setengah tinggi perkiraan elemen newElement.style.position = 'absolute'; newElement.style.left = `${x}px`; newElement.style.top = `${y}px`; canvas.appendChild(newElement); // Membuat elemen yang baru ditambahkan bisa digerakkan (draggable) makeDraggable(newElement); }); // Fungsi untuk membuat elemen di canvas menjadi bisa digerakkan function makeDraggable(element) { let pos1 = 0, pos2 = 0, pos3 = 0, pos4 = 0; // Fungsi untuk memindahkan elemen saat mouse bergerak const elementDrag = (e) => { e = e || window.event; e.preventDefault(); pos3 = pos1 - e.clientX; pos4 = pos2 - e.clientY; pos1 = e.clientX; pos2 = e.clientY; // Tetapkan posisi baru elemen element.style.top = (element.offsetTop - pos4) + "px"; element.style.left = (element.offsetLeft - pos3) + "px"; } // Fungsi saat mouse dilepas (drag selesai) const closeDragElement = () => { document.onmouseup = null; document.onmousemove = null; element.style.cursor = 'move'; // Kembalikan kursor default } // Fungsi saat tombol mouse ditekan pada elemen element.onmousedown = (e) => { e = e || window.event; e.preventDefault(); pos1 = e.clientX; pos2 = e.clientY; document.onmouseup = closeDragElement; document.onmousemove = elementDrag; element.style.cursor = 'grabbing'; // Ubah kursor saat menyeret } } }); ``` Logika drag and drop ini memanfaatkan event `dragstart`, `dragover`, dan `drop`. Event `preventDefault()` sangat krusial pada `dragover` agar proses `drop` bisa berjalan. Fungsi `makeDraggable` adalah inti dari pemindahan elemen di dalam canvas, mengimplementasikan logika kalkulasi posisi saat mouse bergerak. Ini mirip seperti bagaimana seorang seniman memindahkan kanvasnya di studio untuk mendapatkan sudut pandang terbaik, atau bagaimana tukang memposisikan kusen pintu sebelum dipasang permanen.

Kesimpulan: Kekuatan Kolaborasi Frontend

Membuat aplikasi web seperti kalkulator dan page builder memang memerlukan pemahaman yang baik tentang bagaimana HTML, CSS, dan JavaScript saling bekerja sama. HTML memberikan struktur dasar, CSS mewarnai dan menata agar enak dipandang, sementara JavaScript memberikan kecerdasan dan interaktivitas. Kalkulator kita kini berfungsi penuh, siap melakukan perhitungan matematis. Sementara page builder kita, meskipun masih dalam tahap awal, sudah memperlihatkan potensinya untuk menjadi alat yang ampuh bagi siapa saja yang ingin membangun halaman web tanpa perlu menyelami kode secara mendalam. Tentu saja, kedua aplikasi ini bisa dikembangkan lebih lanjut. Untuk kalkulator, kita bisa menambahkan fungsi saintifik, memori, atau bahkan historis. Untuk page builder, kita bisa menambahkan lebih banyak jenis komponen, opsi kustomisasi (warna, font, layout), fitur save/load, dan antarmuka pengguna yang lebih canggih untuk mengedit properti elemen. Ingat, dunia pengembangan web adalah lautan yang luas. Mulailah dari hal-hal kecil, pahami dasarnya, dan teruslah bereksperimen. Setiap baris kode yang Anda tulis, setiap error yang Anda atasi, adalah batu loncatan menuju keahlian yang lebih besar. Selamat ngoding!