Project shows: 22 document.querySelector(…)’s are used to handle access to the many moving parts of this DOM.
Class objects are used to hold individual customer account data. A class object is also used to hold currency data. An array is used to hold deposits/withdrawls for each account.
Functions are used to do the following:
– displayAccountHolders = function (accts) { *.insertAdjacentHTML(…, html) }
– displayMovements = function (movements, sort = false) { …
… = sort ? ….slice().sort((a, b) => a – b) : …
movs.forEach(function (mov, i) { … *.insertAdjacentHTML(…, html) } }
– calcDisplayBalance = function (acc) { … = ….reduce((acc, mov) => acc + mov, 0) … }
– calcDisplaySummary = function (acc) { … = ….filter(mov => mov > 0).reduce((acc, mov) => acc + mov, 0) …
… = ….filter(mov => mov > 0).map(deposit => (deposit * acc.interestRate) / 100).filter((int, i, arr) => int >= 1).reduce((acc, int) => acc + int, 0) … }
– createUserNames = function (accnts) { … = ….toLowerCase().split(‘ ‘).map(name => name[0]).join(”) }
And a global function is used to:
– updateUI = function (acc) { … displayMovements(…) calcDisplayBalance(…) calcDisplaySummary(acc) }
5 eventListeners are used for:
– btn….addEventListener(‘click’, function (e) { e.preventDefault() …
currentAccount = accounts.find(acc => acc.username === inputLoginUsername.value) … }
<!DOCTYPE html>
<html lang=“en”>
<head>
<meta charset=“UTF-8” />
<meta name=“viewport” content=“width=device-width, initial-scale=1.0” />
<meta http-equiv=“X-UA-Compatible” content=“ie=edge” />
<link rel=“shortcut icon” type=“image/png” href=“/icon.png” />
<link
href=“https://fonts.googleapis.com/css?family=Poppins:400,500,600&display=swap”
rel=“stylesheet”
/>
<link rel=“stylesheet” href=“style.css” />
<title>Bankist</title>
</head>
<body>
<!– TOP NAVIGATION –>
<nav>
<p class=“welcome”>Log in to get started</p>
<img src=“logo.png” alt=“Logo” class=“logo” />
<div class=“accounts”>
<div class=“acc_username_pin”></div>
</div>
<form class=“login”>
<input
type=“text”
placeholder=“user”
class=“login__input login__input–user”
value=“js”
/>
<!– In practice, use type=”password” –>
<input
type=“text”
placeholder=“PIN”
maxlength=“4”
class=“login__input login__input–pin”
value=“1111”
/>
<button class=“login__btn”>→</button>
</form>
</nav>
<main class=“app”>
<!– BALANCE –>
<div class=“balance”>
<div>
<p class=“balance__label”>Current balance</p>
<p class=“balance__date”>
As of <span class=“date”>05/03/2037</span>
</p>
</div>
<p class=“balance__value”>0000€</p>
</div>
<!– MOVEMENTS –>
<div class=“movements”>
<div class=“movements__row”>
<div class=“movements__type movements__type–deposit”>2 deposit</div>
<div class=“movements__date”>3 days ago</div>
<div class=“movements__value”>4 000€</div>
</div>
<div class=“movements__row”>
<div class=“movements__type movements__type–withdrawal”>
1 withdrawal
</div>
<div class=“movements__date”>24/01/2037</div>
<div class=“movements__value”>-378€</div>
</div>
</div>
<!– SUMMARY –>
<div class=“summary”>
<p class=“summary__label”>In</p>
<p class=“summary__value summary__value–in”>0000€</p>
<p class=“summary__label”>Out</p>
<p class=“summary__value summary__value–out”>0000€</p>
<p class=“summary__label”>Interest</p>
<p class=“summary__value summary__value–interest”>0000€</p>
<button class=“btn–sort”>↓ SORT</button>
</div>
<!– OPERATION: TRANSFERS –>
<div class=“operation operation–transfer”>
<h2>Transfer money</h2>
<form class=“form form–transfer”>
<input type=“text” class=“form__input form__input–to” />
<input type=“number” class=“form__input form__input–amount” />
<button class=“form__btn form__btn–transfer”>→</button>
<label class=“form__label”>Transfer to</label>
<label class=“form__label”>Amount</label>
</form>
</div>
<!– OPERATION: LOAN –>
<div class=“operation operation–loan”>
<h2>Request loan</h2>
<form class=“form form–loan”>
<input type=“number” class=“form__input form__input–loan-amount” />
<button class=“form__btn form__btn–loan”>→</button>
<label class=“form__label form__label–loan”>Amount</label>
</form>
</div>
<!– OPERATION: CLOSE –>
<div class=“operation operation–close”>
<h2>Close account</h2>
<form class=“form form–close”>
<input type=“text” class=“form__input form__input–user” />
<input
type=“password”
maxlength=“6”
class=“form__input form__input–pin”
/>
<button class=“form__btn form__btn–close”>→</button>
<label class=“form__label”>Confirm user</label>
<label class=“form__label”>Confirm PIN</label>
</form>
</div>
<!– LOGOUT TIMER –>
<p class=“logout-timer”>
You will be logged out in <span class=“timer”>05:00</span>
</p>
</main>
<!– <footer>
© by Jonas Schmedtmann. Don’t claim as your own 🙂
</footer> –>
<script src=“script.js”></script>
</body>
</html>
‘use strict’;
// Data
const account1 = {
owner: ‘Jonas Schmedtmann’,
movements: [200, 450, –400, 3000, –650, –130, 70, 1300],
interestRate: 1.2, // %
pin: 1111,
};
const account2 = {
owner: ‘Jessica Davis’,
movements: [5000, 3400, –150, –790, –3210, –1000, 8500, –30],
interestRate: 1.5,
pin: 2222,
};
const account3 = {
owner: ‘Steven Thomas Williams’,
movements: [200, –200, 340, –300, –20, 50, 400, –460],
interestRate: 0.7,
pin: 3333,
};
const account4 = {
owner: ‘Sarah Smith’,
movements: [430, 1000, 700, 50, 90],
interestRate: 1,
pin: 4444,
};
const accounts = [account1, account2, account3, account4];
// Elements
const labelWelcome = document.querySelector(‘.welcome’);
const labelDate = document.querySelector(‘.date’);
const labelBalance = document.querySelector(‘.balance__value’);
const labelSumIn = document.querySelector(‘.summary__value–in’);
const labelSumOut = document.querySelector(‘.summary__value–out’);
const labelSumInterest = document.querySelector(‘.summary__value–interest’);
const labelTimer = document.querySelector(‘.timer’);
const accounts_username_pin = document.querySelector(‘.accounts’);
const containerApp = document.querySelector(‘.app’);
const containerMovements = document.querySelector(‘.movements’);
const btnLogin = document.querySelector(‘.login__btn’);
const btnTransfer = document.querySelector(‘.form__btn–transfer’);
const btnLoan = document.querySelector(‘.form__btn–loan’);
const btnClose = document.querySelector(‘.form__btn–close’);
const btnSort = document.querySelector(‘.btn–sort’);
const inputLoginUsername = document.querySelector(‘.login__input–user’);
const inputLoginPin = document.querySelector(‘.login__input–pin’);
const inputTransferTo = document.querySelector(‘.form__input–to’);
const inputTransferAmount = document.querySelector(‘.form__input–amount’);
const inputLoanAmount = document.querySelector(‘.form__input–loan-amount’);
const inputCloseUsername = document.querySelector(‘.form__input–user’);
const inputClosePin = document.querySelector(‘.form__input–pin’);
const currencies = new Map([
[‘USD’, ‘United States dollar’],
[‘EUR’, ‘Euro’],
[‘GBP’, ‘Pound sterling’],
]);
const movements = [200, 450, –400, 3000, –650, –130, 70, 1300];
const displayAccountHolders = function (accts) {
accts.forEach(function (acc, i) {
const html = `
<div class=”acc_username_pin”>Username: ${acc.username}, PIN ${acc.pin}</div>
`;
accounts_username_pin.insertAdjacentHTML(‘afterbegin’, html);
});
};
const displayMovements = function (movements, sort = false) {
containerMovements.innerHTML = ”;
const movs = sort ? movements.slice().sort((a, b) => a – b) : movements;
movs.forEach(function (mov, i) {
const type = mov > 0 ? ‘deposit’ : ‘withdrawal’;
const html = `
<div class=”movements__row”>
<div class=”movements__type movements__type–${type}“>${
i + 1
} ${type}</div>
<div class=”movements__value”>${mov}€</div>
</div>
`;
containerMovements.insertAdjacentHTML(‘afterbegin’, html);
});
};
//displayMovements(account1.movements);
//console.log(containerMovements.innerHTML);
const calcDisplayBalance = function (acc) {
acc.balance = acc.movements.reduce((acc, mov) => acc + mov, 0);
labelBalance.textContent = `${acc.balance} €`;
};
//calcDisplayBalance(account1.movements);
const calcDisplaySummary = function (acc) {
const incomes = acc.movements
.filter(mov => mov > 0)
.reduce((acc, mov) => acc + mov, 0);
labelSumIn.textContent = `${incomes}€`;
const out = acc.movements
.filter(mov => mov < 0)
.reduce((acc, mov) => acc – mov, 0);
labelSumOut.textContent = `${out}€`;
const interest = acc.movements
.filter(mov => mov > 0)
.map(deposit => (deposit * acc.interestRate) / 100)
.filter((int, i, arr) => int >= 1)
.reduce((acc, int) => acc + int, 0);
labelSumInterest.textContent = `${interest}€`;
};
//calcDisplaySummary(account1.movements);
// create the account username and add it to the objects
const createUserNames = function (accnts) {
accnts.forEach(function (accnt) {
accnt.username = accnt.owner
.toLowerCase()
.split(‘ ‘)
.map(name => name[0])
.join(”);
});
};
createUserNames(accounts);
//console.log(accounts);
// Display account holders
displayAccountHolders(accounts);
const updateUI = function (acc) {
// Display movements
displayMovements(acc.movements);
// Display balance
calcDisplayBalance(acc);
// Display summary
calcDisplaySummary(acc);
};
let currentAccount;
btnLogin.addEventListener(‘click’, function (e) {
// Prevent form from submitting
e.preventDefault();
currentAccount = accounts.find(
acc => acc.username === inputLoginUsername.value
);
if (currentAccount?.pin === Number(inputLoginPin.value)) {
// Display UI and welcome message
labelWelcome.textContent = `Welcome back ${
currentAccount.owner.split(‘ ‘)[0]
}`;
containerApp.style.opacity = 100;
// Clear input fields
inputLoginUsername.value = inputLoginPin.value = ”;
inputLoginPin.blur();
// Update UI
updateUI(currentAccount);
}
});
btnTransfer.addEventListener(‘click’, function (e) {
e.preventDefault();
const amount = Number(inputTransferAmount.value);
const receiverAcc = accounts.find(
acc => acc.username === inputTransferTo.value
);
inputTransferAmount.value = inputTransferTo.value = ”;
if (
amount > 0 &&
receiverAcc &&
currentAccount.balance >= amount &&
receiverAcc.username !== currentAccount.username
) {
// Doing the transfer
currentAccount.movements.push(–amount);
receiverAcc.movements.push(amount);
// Update UI
updateUI(currentAccount);
}
});
btnLoan.addEventListener(‘click’, function (e) {
e.preventDefault();
const amount = Number(inputLoanAmount.value);
if (amount > 0 && currentAccount.movements.some(mov => mov >= amount * 0.1)) {
// Add the movement
currentAccount.movements.push(amount);
// Update UI
updateUI(currentAccount);
}
inputLoanAmount.value = ”;
});
btnClose.addEventListener(‘click’, function (e) {
e.preventDefault();
if (
inputCloseUsername.value === currentAccount.username &&
currentAccount.pin === Number(inputClosePin.value)
) {
const index = accounts.findIndex(
acc => acc.username === currentAccount.username
);
// Delete account
accounts.splice(index, 1);
// Hide UI
containerApp.style.opacity = 0;
}
inputCloseUsername = inputClosePin = ”;
});
let sorted = false;
btnSort.addEventListener(‘click’, function (e) {
e.preventDefault();
displayMovements(currentAccount.movements, !sorted);
sorted = !sorted;
});
/*
* Use this CSS to learn some intersting techniques,
* in case you’re wondering how I built the UI.
* Have fun! 😁
*/
* {
margin: 0;
padding: 0;
box-sizing: inherit;
}
html {
font-size: 62.5%;
box-sizing: border-box;
}
body {
font-family: ‘Poppins’, sans-serif;
color: #444;
background-color: #f3f3f3;
height: 100vh;
padding: 2rem;
}
nav {
display: flex;
justify-content: space-between;
align-items: center;
padding: 0 2rem;
}
.accounts {
font-size: 1.5rem;
font-weight: 300;
}
.welcome {
font-size: 1.9rem;
font-weight: 500;
}
.logo {
height: 5.25rem;
}
.login {
display: flex;
}
.login__input {
border: none;
padding: 0.5rem 2rem;
font-size: 1.6rem;
font-family: inherit;
text-align: center;
width: 12rem;
border-radius: 10rem;
margin-right: 1rem;
color: inherit;
border: 1px solid #fff;
transition: all 0.3s;
}
.login__input:focus {
outline: none;
border: 1px solid #ccc;
}
.login__input::placeholder {
color: #bbb;
}
.login__btn {
border: none;
background: none;
font-size: 2.2rem;
color: inherit;
cursor: pointer;
transition: all 0.3s;
}
.login__btn:hover,
.login__btn:focus,
.btn–sort:hover,
.btn–sort:focus {
outline: none;
color: #777;
}
/* MAIN */
.app {
position: relative;
max-width: 100rem;
margin: 4rem auto;
display: grid;
grid-template-columns: 4fr 3fr;
grid-template-rows: auto repeat(3, 15rem) auto;
gap: 2rem;
/* NOTE This creates the fade in/out anumation */
opacity: 0;
transition: all 1s;
}
.balance {
grid-column: 1 / span 2;
display: flex;
align-items: flex-end;
justify-content: space-between;
margin-bottom: 2rem;
}
.balance__label {
font-size: 2.2rem;
font-weight: 500;
margin-bottom: -0.2rem;
}
.balance__date {
font-size: 1.4rem;
color: #888;
}
.balance__value {
font-size: 4.5rem;
font-weight: 400;
}
/* MOVEMENTS */
.movements {
grid-row: 2 / span 3;
background-color: #fff;
border-radius: 1rem;
overflow: scroll;
}
.movements__row {
padding: 2.25rem 4rem;
display: flex;
align-items: center;
border-bottom: 1px solid #eee;
}
.movements__type {
font-size: 1.1rem;
text-transform: uppercase;
font-weight: 500;
color: #fff;
padding: 0.1rem 1rem;
border-radius: 10rem;
margin-right: 2rem;
}
.movements__date {
font-size: 1.1rem;
text-transform: uppercase;
font-weight: 500;
color: #666;
}
.movements__type–deposit {
background-image: linear-gradient(to top left, #39b385, #9be15d);
}
.movements__type–withdrawal {
background-image: linear-gradient(to top left, #e52a5a, #ff585f);
}
.movements__value {
font-size: 1.7rem;
margin-left: auto;
}
/* SUMMARY */
.summary {
grid-row: 5 / 6;
display: flex;
align-items: baseline;
padding: 0 0.3rem;
margin-top: 1rem;
}
.summary__label {
font-size: 1.2rem;
font-weight: 500;
text-transform: uppercase;
margin-right: 0.8rem;
}
.summary__value {
font-size: 2.2rem;
margin-right: 2.5rem;
}
.summary__value–in,
.summary__value–interest {
color: #66c873;
}
.summary__value–out {
color: #f5465d;
}
.btn–sort {
margin-left: auto;
border: none;
background: none;
font-size: 1.3rem;
font-weight: 500;
cursor: pointer;
}
/* OPERATIONS */
.operation {
border-radius: 1rem;
padding: 3rem 4rem;
color: #333;
}
.operation–transfer {
background-image: linear-gradient(to top left, #ffb003, #ffcb03);
}
.operation–loan {
background-image: linear-gradient(to top left, #39b385, #9be15d);
}
.operation–close {
background-image: linear-gradient(to top left, #e52a5a, #ff585f);
}
h2 {
margin-bottom: 1.5rem;
font-size: 1.7rem;
font-weight: 600;
color: #333;
}
.form {
display: grid;
grid-template-columns: 2.5fr 2.5fr 1fr;
grid-template-rows: auto auto;
gap: 0.4rem 1rem;
}
/* Exceptions for interst */
.form.form–loan {
grid-template-columns: 2.5fr 1fr 2.5fr;
}
.form__label–loan {
grid-row: 2;
}
/* End exceptions */
.form__input {
width: 100%;
border: none;
background-color: rgba(255, 255, 255, 0.4);
font-family: inherit;
font-size: 1.5rem;
text-align: center;
color: #333;
padding: 0.3rem 1rem;
border-radius: 0.7rem;
transition: all 0.3s;
}
.form__input:focus {
outline: none;
background-color: rgba(255, 255, 255, 0.6);
}
.form__label {
font-size: 1.3rem;
text-align: center;
}
.form__btn {
border: none;
border-radius: 0.7rem;
font-size: 1.8rem;
background-color: #fff;
cursor: pointer;
transition: all 0.3s;
}
.form__btn:focus {
outline: none;
background-color: rgba(255, 255, 255, 0.8);
}
.logout-timer {
padding: 0 0.3rem;
margin-top: 1.9rem;
text-align: right;
font-size: 1.25rem;
}
.timer {
font-weight: 600;
}