Skanda Suresh
Skanda Suresh

Reputation: 123

Difference between ::after and ::before in CSS

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">&times;</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

Answers (2)

Temani Afif
Temani Afif

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

Gabriel
Gabriel

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

Related Questions