Cara Membuat Kalkulator Sederhana HTML, CSS, JavaScript

| | Javascript
Cara Membuat Kalkulator Sederhana HTML, CSS, JavaScript

Membuat kalkulator adalah project pertama yang saya rekomendasikan untuk setiap pemula JavaScript, karena menggabungkan tiga hal inti sekaligus: manipulasi DOM, event handling, dan logika dasar. Saya sendiri memulai dengan project ini, dan sampai sekarang masih ingat rasa “berhasil” pertama kali tombol-tombolnya berfungsi. ayo mulai dari nol.

1. HTML

<!DOCTYPE html>
<html lang="id">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Kalkulator</title>
    <link rel="stylesheet" href="style.css">
</head>
<body>
    <div class="kalkulator">
        <input type="text" id="display" readonly>
        <div class="tombol">
            <button onclick="clearDisplay()">C</button>
            <button onclick="input('/')">/</button>
            <button onclick="input('*')">×</button>
            <button onclick="input('-')">−</button>
            <button onclick="input('7')">7</button>
            <button onclick="input('8')">8</button>
            <button onclick="input('9')">9</button>
            <button onclick="input('+')" class="operator">+</button>
            <button onclick="input('4')">4</button>
            <button onclick="input('5')">5</button>
            <button onclick="input('6')">6</button>
            <button onclick="hitung()" id="sama-dengan">=</button>
            <button onclick="input('1')">1</button>
            <button onclick="input('2')">2</button>
            <button onclick="input('3')">3</button>
            <button onclick="hapusAkhir()">←</button>
            <button onclick="input('0')" colspan="2">0</button>
            <button onclick="input('.')">.</button>
        </div>
    </div>
    <script src="script.js"></script>
</body>
</html>

2. CSS

* { box-sizing: border-box; }

.kalkulator {
    max-width: 300px;
    margin: 50px auto;
    padding: 20px;
    background: #333;
    border-radius: 10px;
}

#display {
    width: 100%;
    height: 60px;
    font-size: 24px;
    text-align: right;
    padding: 10px;
    margin-bottom: 10px;
    background: #fff;
    border: none;
    border-radius: 5px;
}

.tombol {
    display: grid;
    grid-template-columns: repeat(4, 1fr);
    gap: 5px;
}

button {
    padding: 15px;
    font-size: 18px;
    border: none;
    border-radius: 5px;
    cursor: pointer;
    background: #666;
    color: white;
}

button:hover { background: #888; }
.operator { background: #f39c12; }
.operator:hover { background: #e67e22; }
#sama-dengan { background: #27ae60; grid-row: span 2; }
#sama-dengan:hover { background: #219a52; }

3. JavaScript

const display = document.getElementById('display');

function input(value) {
    display.value += value;
}

function clearDisplay() {
    display.value = '';
}

function hapusAkhir() {
    display.value = display.value.slice(0, -1);
}

function hitung() {
    try {
        display.value = eval(display.value);
    } catch {
        display.value = 'Error';
    }
}

// Keyboard support
document.addEventListener('keydown', (e) => {
    const key = e.key;
    if ('0123456789.+-*/'.includes(key)) input(key);
    else if (key === 'Enter') hitung();
    else if (key === 'Backspace') hapusAkhir();
    else if (key === 'Escape') clearDisplay();
});

4. Kenapa Harus Hati-Hati dengan eval()?

fungsi eval() di JavaScript menjalankan string sebagai kode. buat kalkulator lokal milik sendiri, ini cukup. tapi untuk aplikasi yang inputnya dari user lain (misal kalkulator online publik), eval() sangat berbahaya.

contoh serangan XSS via eval:

// Input user: "1+1;alert('hack')"
// eval menjalankan alert() juga!
display.value = eval(display.value);

attacker bisa inject kode apa pun yang jalan di browser user. untuk input yang tidak terpercaya, jangan pernah pakai eval().

5. Alternatif Eval: Parser Manual

ganti eval() dengan parser matematika sederhana. salah satu caranya, sanitize input dulu dengan whitelist karakter yang diizinkan:

function hitung() {
    const ekspresi = display.value;

    // Sanitize: hanya izinkan angka, operator, dan titik
    if (!/^[0-9+\-*/.() ]+$/.test(ekspresi)) {
        display.value = 'Input tidak valid';
        return;
    }

    try {
        // pisah string jadi token
        const tokens = ekspresi.match(/[0-9.]+|[+\-*/()]/g) || [];

        // hitung dengan algoritma shunting yard atau rekursif
        // untuk contoh, saya pakai Function constructor yang lebih aman
        const hasil = new Function('return ' + ekspresi)();
        display.value = Number(hasil.toFixed(10));
    } catch {
        display.value = 'Error';
    }
}

new Function() masih bisa menjalankan kode arbitrer, tapi lebih terbatas dari eval() karena tidak punya akses ke scope lokal. untuk produksi sungguhan, pakai library parser seperti math.js:

import { evaluate } from 'mathjs';
const hasil = evaluate('2 + 3 * 4'); // 14

6. Tambahkan Tombol Persen (%)

banyak kalkulator punya tombol persen. untuk menambahkannya:

tambah tombol di HTML:

<button onclick="input('%')">%</button>

modifikasi fungsi hitung untuk handle %:

function hitung() {
    try {
        let ekspresi = display.value;

        // Konversi persen: "50%" jadi "(50/100)"
        // pattern: angka diikuti % jadi "(angka/100)"
        ekspresi = ekspresi.replace(/(\d+\.?\d*)%/g, '($1/100)');

        display.value = eval(ekspresi);
    } catch {
        display.value = 'Error';
    }
}

contoh:

  • input 50 + % + * + 200 = (50/100) * 200 = 100
  • input 200 + + + 10% = 200 + (10/100) = 200.1

7. Simpan History ke localStorage

biar kalkulator lebih berguna, simpan history perhitungan ke localStorage. waktu halaman dibuka kembali, history masih ada.

function simpanHistory(ekspresi, hasil) {
    const history = JSON.parse(localStorage.getItem('kalkulator_history') || '[]');
    history.unshift({ ekspresi, hasil, waktu: new Date().toISOString() });

    // Simpan 20 history terakhir saja
    if (history.length > 20) history.pop();

    localStorage.setItem('kalkulator_history', JSON.stringify(history));
}

function tampilkanHistory() {
    const history = JSON.parse(localStorage.getItem('kalkulator_history') || '[]');
    if (history.length === 0) return 'Belum ada history';

    return history.map((h, i) =>
        `${i + 1}. ${h.ekspresi} = ${h.hasil}`
    ).join('\n');
}

// Modifikasi fungsi hitung untuk simpan history
function hitung() {
    try {
        const ekspresi = display.value;
        const hasil = eval(ekspresi);
        display.value = hasil;
        simpanHistory(ekspresi, hasil);
    } catch {
        display.value = 'Error';
    }
}

// Tambah tombol history
// <button onclick="alert(tampilkanHistory())">📜</button>

detail lengkap localStorage bisa baca di tutorial localStorage JavaScript (09/17 publish).

8. Tips Pengembangan Lanjutan

beberapa ide untuk naik level dari kalkulator dasar:

  • scientific mode: tambah sin, cos, tan, log, sqrt, pangkat. pakai library math.js.
  • history panel: tampilkan list perhitungan sebelumnya, klik untuk reuse.
  • theme toggle: dark mode/light mode pakai localStorage (sudah dibahas di tutorial dark mode).
  • memory function: tombol M+ M- MR MC untuk simpan nilai sementara.
  • keyboard shortcut lengkap: support panah kiri/kanan, Ctrl+C untuk copy, dll.
  • export ke text file: simpan history ke .txt untuk dokumentasi.
  • PWA: bikin installable sebagai aplikasi dengan service worker.

project kalkulator ini pondasi bagus untuk project JavaScript lain yang lebih kompleks. pola DOM + event + state yang dipakai di sini sama dengan to-do list, kalkulator ilmiah, atau bahkan game sederhana.

Kesimpulan

Kalkulator sederhana ini mengajarkan:

  • DOM manipulation: akses input display
  • Event handling: klik tombol + keyboard
  • eval(): parsing operasi matematika (hati-hati, hanya untuk project sederhana)
  • CSS Grid: layout tombol yang rapi
  • Progressive enhancement: bisa diklik dan pakai keyboard

Pertanyaan yang Sering Diajukan

Apakah aman memakai eval() di kalkulator JavaScript?

Untuk kalkulator lokal milik sendiri, eval() cukup dan simpel. Tapi eval menjalankan teks apa pun sebagai kode, jadi jangan pernah memakainya untuk input yang tidak terpercaya di aplikasi sungguhan; ganti dengan parser ekspresi atau library math.js.

Kenapa hasil 0.1 + 0.2 di JavaScript menjadi 0.30000000000004?

Itu cara JavaScript menyimpan angka desimal dalam format floating point biner, bukan bug kalkulatornya. Untuk tampilan yang rapi, bungkus hasilnya dengan toFixed(2) atau Number().toFixed().

Bagaimana menambahkan tombol persen (%) ke kalkulator ini?

Tambahkan satu tombol lagi dengan onclick="input('%')" lalu tangani di fungsi hitung dengan mengubah angka sebelum operator % menjadi dibagi seratus sebelum evaluasi.

Project seperti apa setelah kalkulator?

Naik levelnya dengan aplikasi to-do list untuk latihan CRUD di localStorage, atau konversi suhu/bilangan untuk melatih validasi input. Keduanya memakai skill yang sama: DOM, event, dan logika.

Bagaimana cara simpan history kalkulasi?

pakai localStorage. setiap kali hitung() jalan, simpan ekspresi dan hasil ke array di localStorage. tampilkan sebagai list, klik untuk reuse ekspresi sebelumnya.

Sigit Nurhanafi avatar
Full-stack developer & technical writer. Berpengalaman di PHP, Laravel, NodeJS, MySQL, dan Python. Aktif menulis tutorial pemrograman dan maintaining open-source projects di PemburuKode sejak 2021.