Reputation: 123
I'm following this tutorial on building a javascript calculator to learn a little more about web development. The CSS code below adds styling when pressing an operator (+, -, *, /) key. When an operator key is pressed, it should darken until another key is pressed to show that the operator is "active". However, when pressing the same operator key over and over again, there should still be some visual indication of the repeated clicking.
const calculator = document.querySelector(".calculator");
const keys = calculator.querySelector(".calculator__keys");
const display = document.querySelector(".calculator__display");
keys.addEventListener('click', e => {
if (e.target.matches('button')) {
const key = e.target;
const action = key.dataset.action;
const keyValue = key.textContent;
const displayNum = display.textContent;
Array.from(key.parentNode.children).forEach(k=>k.classList.remove('is-depressed'));
if (!action) {
if (displayNum === "0" || calculator.dataset.previousKeyType === "operator") {
display.textContent = keyValue;
calculator.dataset.previousKeyType = null;
} else {
display.textContent = displayNum + keyValue;
}
calculator.dataset.previousKeyType = "number";
console.log("Number key")
}
if (action === "decimal") {
if (calculator.dataset.previousKeyType === "operator") {
display.textContent = "0.";
calculator.dataset.previousKeyType = "decimal";
} else if (!displayNum.includes(".")) {
display.textContent += ".";
}
}
if (
action === 'add' ||
action === 'subtract' ||
action === 'multiply' ||
action === 'divide'
) {
calculator.dataset.previousKeyType = "operator";
calculator.dataset.previousNum = displayNum;
calculator.dataset.operator = action;
key.classList.add("is-depressed");
}
if (action === "clear") {
calculator.dataset.previousKeyType = "clear";
console.log("AC key");
}
if (action === "calculate") {
calculator.dataset.previousKeyType = "calculate";
const secondVal = displayNum;
const firstVal = calculator.dataset.previousNum;
const operator = calculator.dataset.operator;
display.textContent = calculate(firstVal, secondVal, operator);
}
}
});
function calculate(firstVal, secondVal, operator) {
let num1 = +firstVal;
let num2 = +secondVal;
if (operator === "add") {
return num1 + num2;
} else if (operator === "subtract") {
return num1 - num2;
} else if (operator === "multiply") {
return num1 * num2;
} else {
return num1 / num2;
}
}
// NOTE: You don't need to mess around with
// CSS to follow the tutorial. Focus on the
// JavaScript instead!
// =========================
// Some personal resets
html {
box-sizing: border-box;
}
*,
*::before,
*::after {
box-sizing: inherit;
}
body {
margin: 0;
}
/* Responsive Images */
embed,
iframe,
img,
object,
video {
max-width: 100%;
}
h1,
h2,
h3,
h4,
h5,
h6,
ul,
ol,
li,
p,
pre,
blockquote,
figure,
hr {
margin: 0;
padding-right: 0;
padding-left: 0;
}
a {
text-decoration: none;
}
a:focus {
outline: none;
}
h1,
h2,
h3,
h4,
h5,
h6 {
display: block;
}
/* Removes all decimals and discs from lists */
ol,
ul {
list-style: none;
}
/*
* Completely resets form items
* ----------------------------
* Super hard reset that removes all borders
* and radiuses of all form items (including
* checkboxes and radios)
*/
input,
textarea,
button {
border: 0;
border-radius: 0;
background-color: transparent;
font-size: inherit;
font-family: inherit;
font-weight: inherit;
outline: none;
appearance: none;
text-align: left;
}
input:hover,
input:active,
input:focus,
textarea:hover,
textarea:active,
textarea:focus,
button:hover,
button:active,
button:focus {
outline: none;
}
:root {
font-family: Helvetica, Arial, sans-serif;
}
html {
font-size: 175%;
font-weight: 300;
line-height: 1.3;
}
body {
align-items: center;
background-image: linear-gradient(236deg, #74ebd5, #acb6e5);
display: flex;
height: 100vh;
justify-content: center;
}
.container {
max-width: 20em;
}
.container > p {
text-align: center;
}
.calculator {
border-radius: 12px;
box-shadow: 0 0 40px 0px rgba(0, 0, 0, 0.15);
margin-left: auto;
margin-right: auto;
margin-top: 2em;
max-width: 15em;
overflow: hidden;
}
.calculator__display {
background-color: #222222;
color: #fff;
font-size: 1.714285714em;
padding: 0.5em 0.75em;
text-align: right;
}
.calculator__keys {
background-color: #999;
display: grid;
grid-gap: 1px;
grid-template-columns: repeat(4, 1fr);
}
.calculator__keys > * {
background-color: #fff;
padding: 0.5em 1.25em;
position: relative;
text-align: center;
}
.calculator__keys > *:active::after,
.calculator__keys > .is-depressed::before {
background-color: rgba(0, 0, 0, 0.2);
bottom: 0;
box-shadow: 0 0 6px 0 rgba(0, 0, 0, 0.5) inset;
content: "";
left: 0;
opacity: 0.3;
position: absolute;
right: 0;
top: 0;
z-index: 1;
}
.key--operator {
background-color: #eee;
}
.key--equal {
background-image: linear-gradient(to bottom, #fe886a, #ff7033);
grid-column: -2;
grid-row: 2 / span 4;
}
<!DOCTYPE html>
<html lang="en" dir="ltr">
<head>
<meta charset="utf-8">
<title>Calculator</title>
<link rel="stylesheet" href="calculator.css" type="text/css">
<script type="text/javascript" src="calculator.js" defer></script>
</head>
<body>
<div class="container">
<p>
This component works exactly like the calculator you know. Click any number to start calculating!
</p>
<div class="calculator">
<div class="calculator__display">0</div>
<div class="calculator__keys">
<button class="key--operator" data-action="add">+</button>
<button class="key--operator" data-action="subtract">-</button>
<button class="key--operator" data-action="multiply">×</button>
<button class="key--operator" data-action="divide">÷</button>
<button>7</button>
<button>8</button>
<button>9</button>
<button>4</button>
<button>5</button>
<button>6</button>
<button>1</button>
<button>2</button>
<button>3</button>
<button>0</button>
<button data-action="decimal">.</button>
<button data-action="clear">AC</button>
<button class="key--equal" data-action="calculate">=</button>
</div>
</div>
</div>
</body>
</html>
The is-depressed
class is a class I add to the operator key element when it is clicked. The code above doesn't give the visual indicator of multiple clicks, but when I change *:active::before
to *:active::after
, the code does what I want. Why is that?
.calculator__keys > *:active::after,
.calculator__keys > .is-depressed::before {
background-color: rgba(0, 0, 0, 0.2);
bottom: 0;
box-shadow: 0 0 6px 0 rgba(0, 0, 0, 0.5) inset;
content: "";
left: 0;
opacity: 0.3;
position: absolute;
right: 0;
top: 0;
z-index: 1;
}
Upvotes: 3
Views: 1565
Reputation: 272817
The answer is simple. Having :active::after
and .is-depressed::before
means that when your element is pressed you will change the before
element AND when you click again you will change the after
element so two different elements.
When having both of them as before
then it's one element and it is already updated via .is-depressed
so the active state will do nothing.
To illustrate this with a more simple example:
.box {
border:1px solid;
width:150px;
height:150px;
display:inline-block;
position:relative;
}
.box:hover::before,
.box.active::before{
content:"";
display:block;
height:50%;
background:red;
}
<div class="box"></div>
<div class="box active"></div>
In the above, nothing will happen when you hover on the second element because active
already did the job:
.box {
border:1px solid;
width:150px;
height:150px;
display:inline-block;
position:relative;
}
.box:hover::after,
.box.active::before{
content:"";
display:block;
height:50%;
background:red;
}
<div class="box"></div>
<div class="box active"></div>
In the last exmple the active will add the before element and the hover will add the after element. As simple as that.
Upvotes: 2
Reputation: 2190
The problem with your code is that ::before
has position: absolute
but the buttons don't, so it's position is relative to the body. Add position: relative
or absolute
to the buttons.
Regarding your question, they are the same, they're virtual elements (pseudo-elements) (they don't belong to the DOM but they are rendered as if they were regular elements if they have a content
property), the difference between them is that ::before
is placed at the beginning of the element and ::after
at the end.
Upvotes: -1