Egy egyszerű számológép készítése, melynél több műveleti operátort is megadhatunk egymás után, ekkor viszont ERROR-t kapunk és a többféle művelet összefűzése nem a precedencia szabályai szerint értékelődik ki, hanem egyszerűen balról-jobbra.
A HTML váz most tényleg csak egy váz, a számok celláit ugyanazon szám értékű id-ekkel jelöljük.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Számológép</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<div class="calculator">
<div class="display"></div>
<div class="cell plus firstLine" id="+">+</div>
<div class="cell minus firstLine" id="-">-</div>
<div class="cell multiply firstLine" id="*">x</div>
<div class="cell divide firstLine" id="/">÷</div>
<div class="cell number" id="7">7</div>
<div class="cell number" id="8">8</div>
<div class="cell number" id="9">9</div>
<div class="equal">=</div>
<div class="cell number" id="4">4</div>
<div class="cell number" id="5">5</div>
<div class="cell number" id="6">6</div>
<div class="cell number" id="1">1</div>
<div class="cell number" id="2">2</div>
<div class="cell number" id="3">3</div>
<div class="cell number" id="0">0</div>
<div class="cell dot">.</div>
<div class="cell clear">C</div>
</div>
<script src="main.js"></script>
</body>
</html>
A számológép layout-jának szabálytalan elem elhelyezkedése a CSS Grid után kiált, de gyakorlásképpen ugyanezen design a github repoban CSS Flexbox használatával került megvalósításra.
* {
margin: 0;
padding: 0;
}
*, *::before, *::after {
box-sizing: inherit;
}
html {
box-sizing: border-box;
}
body {
min-height: 100vh;
position: relative;
font-family: sans-serif;
}
.calculator {
display: grid;
grid-template-columns: auto auto auto auto;
border: 1px solid rgba(0,0,0, 0.2);
box-shadow: 0 4px 8px rgba(0,0,0, 0.2);
gap: .7rem;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
padding: 1.5rem;
}
.display {
grid-column: 1 / span 4;
border: 1px solid rgba(0,0,0, 0.2);
box-shadow: 1px 1px 3px rgba(0,0,0, 0.2);
color: rgba(0,0,0, 0.5);
height: 5rem;
font-size: 2rem;
display: flex;
justify-content: flex-end;
align-items: center;
padding-right: .5rem;
}
.equal {
grid-row: 3 / span 4;
grid-column-start: 4;
font-size: 3rem;
display: flex;
justify-content: center;
align-items: center;
background-color: rgb(102, 102, 252);
box-shadow: 0 1px 2px rgba(0,0,0, 0.2);
color: #fff;
cursor: pointer;
}
.equal:hover {
background-color: rgb(15, 15, 221);
}
.cell {
border: 1px solid rgba(0,0,0, 0.2);
font-size: 2rem;
height: 4rem;
width: 7rem;
display: flex;
justify-content: center;
align-items: center;
background-color: rgba(0,0,0, 0.03);
box-shadow: 0 1px 2px rgba(0,0,0, 0.2);
cursor: pointer;
color: rgba(0,0,0, 0.5);
}
.cell:hover {
background-color: rgba(0,0,0, 0.2);
}
.firstLine {
background-color: rgba(0,0,0, 0.15);
}
.firstLine:hover {
background-color: rgba(0,0,0, 0.5);
color: #fff;
}
Először felírjuk a használni kívánt DOM elemeket, Nodelist-eket.
const display = document.querySelector('.display');
const numberButtons = document.querySelectorAll('.number');
const clear = document.querySelector('.clear');
const operatorButtons = document.querySelectorAll('.firstLine');
const dot = document.querySelector('.dot');
const equal = document.querySelector('.equal');
Beállítjuk a változók kezdeti értékeit és a kijelzőt 0-val indtjuk.
let result = 0;
display.textContent = 0;
let numbers = [];
let operators = [];
A szám és az operátor gombokra felírjuk az eseményfigyelőket, mely megjeleníti a lenyomott gomb tartalmát a kijelzőn. A NodeList-eket jelen esetben nem muszáj tömbbé alakítani, mert nekik is van beépített forEach() metódusuk. Ha 0 van a kijelzőn, akkor az operátorok nem kerülnek elfogadásra, de ez egyéni, mert így viszont nem lehet 0-val kezdeni a műveletet. Ezért "egyszerű' a számológépünk.
Array.from(numberButtons).forEach(item => {
item.addEventListener('click', () => {
if (display.textContent.startsWith('0')) display.textContent = '';
display.textContent += item.textContent;
})
});
Array.from(operatorButtons).forEach(item => {
item.addEventListener('click', () => {
if (display.textContent.startsWith('0')) {
return false;
}
display.textContent += item.textContent;
})
})
A C gombbal mindent letörlünk és alaphelyzetbe állítunk.
clear.addEventListener('click', () => {
display.textContent = 0;
result = 0;
operators = [];
numbers = [];
})
Az egyenlőségjel kiértékeli a kijelzőn megjelenő sztringet, mégpedig úgy, hogy külön tömbbe szedi a számokat és az operátorokat és elvégzi a megfelelő műveleteket.
equal.addEventListener('click', () => {
handleResult();
display.textContent = result;
})
function handleResult() {
getNumbersArray();
getOperatorsArray();
operations();
return result;
}
A számok és az operátorok külön tömbbökbe szedése hasonló módon történik, mert előbbi esetben a kijelzőn megjelenő szöveget a nem számok mentén, míg utóbbi esetbenn a számok mentén feldaraboljuk és a megfelelő értékeket tömbbe rakjuk.
function getNumbersArray() {
let numbersString = [];
numbersString = display.textContent.split(/\D+/);
numbersString.forEach(item => {
if (typeof parseInt(item) === 'number') {
numbers.push(parseInt(item));
}
});
}
function getOperatorsArray() {
let operatorsString = [];
operatorsString = display.textContent.split(/\d/);
operators = operatorsString.filter(item => item !== '');
}
Végül a szám és az operátor tömb megfelelő elemeivel elvégezzük a műveletet, kizárva azt a lehetőséget amikor több operátor van egymás mellett, mert akkor kiugrunk a végrehajtásból és ERROR-t adunk vissza.
function operations() {
result = numbers[0];
numbers.shift();
numbers.forEach((item, index) => {
handleValidOperators();
if (operators[index] === "+") { result += item }
else if (operators[index] === '-') { result -= item }
else if (operators[index] === '÷') { result /= item }
else if (operators[index] === 'x') { result *= item };
})
}
function handleValidOperators() {
operators.forEach(item => {
if (item.length >= 2) {
result = "ERROR";
return false;
}
})
}