W poradniku pokażemy, jak można stworzyć prosty, wygodny i szybki kalkulator na stronę internetową przy użyciu języka JavaScript na przykładzie kalkulatora do przeliczania metrów kwadratowych na hektary oraz hektarów na metry kwadratowe. To jedno z tych narzędzi, które z pozoru wydają się niewielkie, ale w praktyce bardzo poprawiają użyteczność strony. Użytkownik dostaje jasny formularz, wpisuje wartość, wybiera jednostki i natychmiast widzi wynik. Od strony programistycznej taki kalkulator jest z kolei świetnym przykładem dobrze uporządkowanego kodu, w którym osobno obsługujemy dane wejściowe, logikę przeliczania i aktualizację interfejsu.
Od czego zaczyna się działanie kalkulatora
Całość została napisana w czystym JavaScripcie, bez frameworków i bez zewnętrznych bibliotek. To dobra wiadomość, bo taki skrypt można łatwo osadzić na zwykłej stronie HTML, w systemie CMS, w artykule blogowym albo w sekcji z narzędziami. Dodatkowo kod jest zamknięty w samowywołującej się funkcji, dzięki czemu nie zaśmieca globalnego zakresu i nie koliduje z innymi skryptami obecnymi na stronie.
(function () {
"use strict";
// ...
})();
Taka konstrukcja oznacza, że cały kalkulator działa w swoim własnym zamkniętym środowisku. Z punktu widzenia praktyki frontendowej to bardzo rozsądne rozwiązanie, bo zmienne i funkcje używane przez ten moduł nie "wychodzą" na zewnątrz. Do tego dochodzi tryb "use strict", który pilnuje bardziej rygorystycznych zasad działania JavaScriptu i pomaga unikać niepotrzebnych błędów.
Przygotowanie danych wejściowych
Pierwszym ważnym elementem w tym skrypcie jest funkcja odpowiedzialna za przygotowanie liczby wpisywanej przez użytkownika:
function normalizeNumber(str) {
if (str == null) return NaN;
str = String(str).trim().replace(/\s+/g, "").replace(",", ".");
if (!/^-?\d*(\.\d*)?$/.test(str)) return NaN;
return parseFloat(str);
}
To właśnie tutaj zaczyna się cały proces przeliczania. Użytkownik może wpisać wartość na różne sposoby: jako liczbę całkowitą, z przecinkiem, z kropką, a czasem nawet ze spacjami. Funkcja najpierw zamienia otrzymaną wartość na tekst, usuwa zbędne odstępy, zamienia przecinek na kropkę, a później sprawdza, czy finalnie mamy do czynienia z czymś, co rzeczywiście przypomina liczbę. Jeśli nie, zwracane jest NaN, czyli informacja, że obliczenia nie mogą zostać wykonane.
To bardzo ważny moment, bo dobrze napisany kalkulator nie powinien ślepo liczyć wszystkiego, co wpisze użytkownik. Najpierw trzeba dane uporządkować i zweryfikować. Dzięki temu narzędzie nie będzie zwracało przypadkowych wyników dla błędnych danych wejściowych. Z perspektywy użytkownika wygląda to po prostu tak, że przy niepoprawnym wpisie wynik się nie pojawia, zamiast generować chaos w formularzu.
Dlaczego normalizacja danych ma znaczenie
W praktyce użytkownicy bardzo rzadko wpisują dane w idealnie jednolity sposób. Jedni używają przecinków, inni kropek, a jeszcze inni dodają spacje między cyframi. Dobrze zaprojektowany kalkulator powinien takie sytuacje przewidywać i spokojnie sobie z nimi radzić, zamiast od razu uznawać dane za błędne.
1500, 1 500 czy 1,5 mogą zostać poprawnie przygotowane do dalszych obliczeń, jeśli wcześniej zostaną odpowiednio znormalizowane.Formatowanie wyniku i prezentacja jednostek
Kolejna pomocnicza funkcja odpowiada za formatowanie wyniku:
function formatNumber(num, decimals) {
if (!isFinite(num)) return "";
return num.toFixed(decimals);
}
Jej zadanie jest bardzo proste, ale niezwykle istotne dla wygody korzystania z kalkulatora. Nawet jeśli obliczenie matematyczne da poprawny wynik, trzeba go jeszcze pokazać w czytelnej formie. Funkcja sprawdza więc, czy liczba jest poprawna, a następnie formatuje ją do określonej liczby miejsc po przecinku. Dzięki temu użytkownik może zobaczyć wynik jako na przykład 0.25, 0.250 albo 0.2500, zależnie od wybranego ustawienia zaokrąglenia.
Dalej pojawia się funkcja odpowiedzialna za estetyczne wyświetlenie jednostki:
function unitLabel(u) {
return (u === "m2") ? "m²" : "ha";
}
To nieduży detal, ale bardzo sensowny. W kodzie wygodniej operować na prostych identyfikatorach typu m2 i ha, natomiast użytkownik powinien zobaczyć ładny zapis jednostki, czyli m² albo ha. Dzięki temu logika wewnętrzna pozostaje prosta, a interfejs czytelny i bardziej naturalny.
Po co oddzielać logikę od wyglądu
To podejście sprawia, że kod jest czytelniejszy i łatwiejszy do rozwijania. Wewnętrznie kalkulator może operować na prostych nazwach technicznych, ale użytkownik końcowy widzi już elegancki zapis dopasowany do realnego interfejsu.
Jak działa właściwe przeliczanie m² i ha
Sercem całego kalkulatora jest oczywiście funkcja konwersji:
function convert(value, fromUnit, toUnit) {
if (!isFinite(value)) return NaN;
if (fromUnit === toUnit) return value;
if (fromUnit === "m2" && toUnit === "ha") return value / 10000;
if (fromUnit === "ha" && toUnit === "m2") return value * 10000;
return NaN;
}
To tutaj znajduje się właściwa logika matematyczna. Kalkulator korzysta z podstawowej zależności: jeden hektar to dokładnie dziesięć tysięcy metrów kwadratowych. Jeśli więc użytkownik chce zamienić metry kwadratowe na hektary, wartość należy podzielić przez 10 000. Jeśli sytuacja jest odwrotna i przeliczamy hektary na metry kwadratowe, trzeba wykonać mnożenie przez 10 000. Warto zauważyć, że funkcja od razu obsługuje też sytuację, w której jednostki są takie same. W takim przypadku zwraca po prostu tę samą wartość.
1 ha = 10 000 m²
1 m² = 0,0001 ha
Połączenie logiki z formularzem
Na tym etapie mamy już najważniejsze cegiełki: wiemy, jak odczytać liczbę, jak ją sformatować, jak pokazać jednostkę i jak wykonać samo przeliczenie. Kolejny krok to połączenie tego wszystkiego z konkretnymi elementami interfejsu. Za to odpowiada funkcja initCalc(root), która uruchamia cały kalkulator w obrębie wskazanego kontenera.
function initCalc(root) {
var inputs = root.querySelectorAll(".calc-m2ha__input");
var selects = root.querySelectorAll(".calc-m2ha__select");
var valueEl = inputs && inputs[0] ? inputs[0] : null;
var resultEl = inputs && inputs[1] ? inputs[1] : null;
var fromEl = selects && selects[0] ? selects[0] : null;
var toEl = selects && selects[1] ? selects[1] : null;
var roundEl = selects && selects[2] ? selects[2] : null;
// ...
}
Skrypt nie zakłada, że na stronie istnieje tylko jeden kalkulator. Zamiast tego dostaje konkretny element nadrzędny i dopiero wewnątrz niego wyszukuje potrzebne pola formularza. Dzięki temu ten sam kod może obsłużyć wiele kalkulatorów umieszczonych na jednej stronie. Każdy działa wtedy niezależnie od pozostałych.
W tym fragmencie skrypt pobiera dwa pola input: jedno dla wartości wejściowej, drugie dla wyniku. Następnie odczytuje trzy pola select: pierwsze określa jednostkę źródłową, drugie jednostkę docelową, a trzecie liczbę miejsc po przecinku. Potem wyszukiwane są przyciski odpowiedzialne za zamianę jednostek, obliczanie i czyszczenie formularza, a także elementy podsumowania, w których wyświetlane są wartości wejściowe i końcowe.
Dlaczego taki układ kodu jest wygodny
Dzięki takiej konstrukcji jeden skrypt może obsługiwać wiele modułów na stronie. To rozwiązanie jest wygodne, skalowalne i bardzo praktyczne przy rozbudowanych serwisach internetowych.
Zaokrąglenie wyniku i kontrola jednostek
Bardzo przydatna jest również funkcja odpowiedzialna za odczyt poziomu zaokrąglenia:
function decimals() {
if (!roundEl) return 2;
var d = parseInt(roundEl.value, 10);
return isFinite(d) && d >= 0 ? d : 2;
}
Dzięki niej kalkulator może działać elastycznie. Jeśli użytkownik wybierze liczbę miejsc po przecinku, skrypt wykorzysta właśnie tę wartość. Jeżeli z jakiegoś powodu pole z zaokrągleniem nie istnieje albo zawiera niepoprawne dane, włączana jest bezpieczna wartość domyślna, czyli dwa miejsca po przecinku. To przykład prostego, ale rozsądnego zabezpieczenia.
Kolejny ciekawy fragment odpowiada za pilnowanie, żeby jednostka wejściowa i wyjściowa nie były takie same:
function syncOppositeUnit() {
if (fromEl.value === toEl.value) {
toEl.value = (fromEl.value === "m2") ? "ha" : "m2";
}
}
W praktyce oznacza to, że kalkulator sam dba o sensowny stan formularza. Jeżeli użytkownik przypadkiem ustawi po obu stronach tę samą jednostkę, skrypt automatycznie zmieni jednostkę docelową na przeciwną. To wygodne rozwiązanie, bo narzędzie pozostaje skupione na realnym przeliczaniu, a nie na pokazywaniu tej samej wartości pod inną etykietą.
Podsumowanie wyniku dla użytkownika
Ważnym elementem jest funkcja aktualizująca podsumowanie:
function setSummary(val, fromU, out, toU) {
if (sumIn) sumIn.textContent = isFinite(val) ? (formatNumber(val, decimals()) + " " + unitLabel(fromU)) : "-";
if (sumOut) sumOut.textContent = isFinite(out) ? (formatNumber(out, decimals()) + " " + unitLabel(toU)) : "-";
}
Ta funkcja sprawia, że użytkownik poza samym wynikiem widzi też krótkie, czytelne podsumowanie całego działania. Zamiast jednej liczby w polu formularza dostaje informację w stylu: z tylu metrów kwadratowych otrzymano tyle hektarów. Jeżeli dane są niepoprawne albo wynik nie może zostać policzony, w podsumowaniu pojawia się neutralny znak pauzy. Dzięki temu interfejs pozostaje schludny.
Dlaczego podsumowanie zwiększa użyteczność
Dodatkowy opis wyniku sprawia, że użytkownik od razu widzi pełen kontekst obliczenia. To drobny element, ale bardzo poprawia czytelność całego formularza.
Centralna funkcja aktualizacji kalkulatora
Najważniejsza część całego kalkulatora to funkcja update(), która spina wszystkie wcześniej opisane elementy w jeden proces:
function update() {
syncOppositeUnit();
var val = normalizeNumber(valueEl.value);
if (!isFinite(val)) {
resultEl.value = "";
setSummary(NaN, fromEl.value, NaN, toEl.value);
return;
}
var out = convert(val, fromEl.value, toEl.value);
resultEl.value = formatNumber(out, decimals());
setSummary(val, fromEl.value, out, toEl.value);
}
To tutaj naprawdę realizuje się cały kalkulator. Na początku funkcja pilnuje poprawnego ustawienia jednostek. Następnie pobiera wartość z pola wejściowego i przekazuje ją do normalizacji. Jeśli użytkownik wpisał coś błędnego, wynik jest czyszczony, podsumowanie wraca do stanu neutralnego i działanie funkcji się kończy. Jeżeli wszystko jest poprawne, uruchamiana jest funkcja konwersji, a potem wynik trafia zarówno do pola formularza, jak i do podsumowania tekstowego.
W praktyce taka konstrukcja jest wygodna, bo niezależnie od tego, co zrobi użytkownik, zawsze można uruchomić jedną centralną funkcję odpowiedzialną za odświeżenie stanu kalkulatora. To znacznie lepsze niż rozrzucanie podobnej logiki po wielu miejscach w kodzie.
Zamiana jednostek i czyszczenie formularza
Oprócz standardowego przeliczania skrypt oferuje również możliwość zamiany jednostek miejscami. Odpowiada za to funkcja swap():
function swap() {
var tmp = fromEl.value;
fromEl.value = toEl.value;
toEl.value = tmp;
update();
try { valueEl.focus(); } catch (e) {}
}
Jej działanie jest przejrzyste. Najpierw zapamiętywana jest jednostka źródłowa, potem jednostki zamieniają się miejscami, a na końcu wywoływana jest funkcja update(), która od razu przelicza wartość na nowo. Dzięki temu użytkownik może jednym kliknięciem przejść z trybu m² na ha do trybu ha na m². Dodatkowo fokus wraca na pole wejściowe, co poprawia wygodę dalszej pracy.
Podobnie działa funkcja czyszczenia formularza:
function clearAll() {
valueEl.value = "";
resultEl.value = "";
setSummary(NaN, fromEl.value, NaN, toEl.value);
try { valueEl.focus(); } catch (e) {}
}
Tutaj skrypt usuwa wpisaną wartość, czyści pole wyniku i resetuje podsumowanie. Z punktu widzenia użytkownika całość wraca do pustego stanu, gotowego na kolejne obliczenie. To drobna funkcja, ale bardzo praktyczna, szczególnie wtedy, gdy ktoś chce wykonać kilka przeliczeń pod rząd.
Kiedy kalkulator reaguje na działania użytkownika
Kiedy wszystkie pomocnicze funkcje są już gotowe, przychodzi czas na podpięcie zdarzeń, czyli momentów, w których kalkulator ma reagować na działania użytkownika:
valueEl.addEventListener("input", update);
fromEl.addEventListener("change", update);
toEl.addEventListener("change", update);
if (roundEl) roundEl.addEventListener("change", update);
if (swapBtn) swapBtn.addEventListener("click", function (e) { e.preventDefault(); swap(); });
if (calcBtn) calcBtn.addEventListener("click", function (e) { e.preventDefault(); update(); });
if (clearBtn) clearBtn.addEventListener("click", function (e) { e.preventDefault(); clearAll(); });
update();
Ten fragment decyduje o tym, kiedy kalkulator ma się odświeżać. Jeśli użytkownik zacznie wpisywać liczbę, funkcja update() uruchomi się automatycznie. To samo stanie się po zmianie jednostki wejściowej, jednostki wyjściowej albo liczby miejsc po przecinku. Oprócz tego podpięte są przyciski zamiany, obliczania i czyszczenia. Warto zwrócić uwagę na e.preventDefault(), które zatrzymuje domyślne zachowanie przycisków, na przykład wysłanie formularza. Na samym końcu wywoływane jest jeszcze jedno update(), aby kalkulator od razu po inicjalizacji ustawił poprawny stan początkowy.
Co to daje w praktyce
Użytkownik nie musi wykonywać zbędnych kroków. Kalkulator reaguje od razu, dzięki czemu korzystanie z niego jest szybkie, intuicyjne i wygodne.
Uruchomienie kalkulatora na stronie
Na koniec pozostaje jeszcze uruchomienie całego mechanizmu dla wszystkich kalkulatorów obecnych na stronie. Służy do tego funkcja boot():
function boot() {
var roots = document.querySelectorAll(".calc-m2ha");
for (var i = 0; i < roots.length; i++) initCalc(roots[i]);
}
if (document.readyState === "loading") {
document.addEventListener("DOMContentLoaded", boot);
} else {
boot();
}
To prosty i skuteczny sposób inicjalizacji. Skrypt najpierw wyszukuje wszystkie kontenery kalkulatora oznaczone klasą .calc-m2ha, a następnie uruchamia dla każdego z nich osobną instancję logiki. Dodatkowo sprawdzany jest stan dokumentu. Jeśli strona jeszcze się ładuje, kalkulator poczeka na moment, w którym DOM będzie gotowy. Jeśli wszystko zostało już wczytane, inicjalizacja następuje od razu.
Jak wygląda cały przepływ działania
Z punktu widzenia działania całego narzędzia schemat jest więc bardzo czytelny. Po wejściu na stronę skrypt wyszukuje moduły kalkulatora i podłącza do nich logikę. Gdy użytkownik wpisuje wartość lub zmienia ustawienia, uruchamia się centralna funkcja aktualizacji. Ta normalizuje dane, sprawdza ich poprawność, przelicza jednostki i wyświetla wynik. Jeśli użytkownik kliknie zamianę, jednostki zmieniają się miejscami i obliczenie wykonywane jest ponownie. Jeśli kliknie czyszczenie, formularz wraca do pustego stanu.
Dlaczego taki kalkulator jest dobrym przykładem uporządkowanego kodu
Zaletą tego rozwiązania jest porządek. Kod nie miesza logiki matematycznej z obsługą interfejsu bardziej, niż to konieczne. Każda funkcja ma swoje konkretne zadanie. Jedna przygotowuje dane, inna przelicza wartość, inna formatuje wynik, a jeszcze inna aktualizuje widok. Dzięki temu cały kalkulator jest czytelny, łatwy do utrzymania i prosty do dalszego rozwijania.
W praktyce taki moduł można bez większego problemu rozbudować. Nic nie stoi na przeszkodzie, by później dodać kolejne jednostki powierzchni, inne sposoby formatowania liczb, blokadę liczb ujemnych albo bardziej rozbudowane komunikaty dla użytkownika. Fundament jest już gotowy i został napisany w sposób, który dobrze nadaje się do dalszych modyfikacji.
Opisany w tym poradniku kalkulator m2 na ha został wdrożony w witrynie domzogrodem.pl i działa online jako kalkulator m2 na ha. To praktyczny przykład wykorzystania prostego, dobrze uporządkowanego kodu JavaScript w realnym narzędziu dostępnym dla użytkowników. Na tej samej zasadzie przygotowano również kalkulator m2 na ar, który pozwala szybko przeliczać ary na metry kwadratowe. Oba narzędzia pokazują, że nawet nieskomplikowany mechanizm przeliczania może być bardzo wygodnym, praktycznym i wartościowym dodatkiem do nowoczesnego serwisu internetowego.

Komentarze