<div class="container">
  <div class="head-section">
    <h2>Undoable counter</h2>
    <div class="undo-redo-buttons">
      <button onClick="undo()">Undo</button>
      <button onClick="redo()" id="redoButton">Redo</button>
    </div>
  </div>
  <div class="counter-component">
    <ul class="rest-buttons">
      <li><button onclick="exec(-100)">-100</button></li>
      <li><button onclick="exec(-10)">-10</button></li>
      <li><button onclick="exec(-1)">-1</button></li>
    </ul>
    <div class="counter"><p id="counter">0</p></div>
    <ul class="add-buttons">
      <li><button onclick="exec(1)">+1</button></li>
      <li><button onclick="exec(10)">+10</button></li>
      <li><button onclick="exec(100)">+100</button></li>
    </ul>
  </div>
  <div class="history">
    <h4>History</h4>
    <ul id="historyList"></ul>
  </div>
</div>
.container {
  display: flex;
  flex-direction: column;
  align-items: center;
}
.undo-redo-buttons {
  display: flex;
  width: 70%;
  justify-content: space-around;
}
.head-section {
  align-items: center;
  display: flex;
  flex-direction: column;
}
.rest-buttons, .add-buttons, .counter, #historyList {
  display: inline-block;
  padding: 0;
}
.rest-buttons li, .add-buttons li {
  display: inline-block;
}
.rest-buttons li, .add-buttons li, #historyList li {
  list-style: none;
}
#historyList {
  border: 1px solid;
  padding: 5px 20px;
  height: 200px;
  overflow-y: auto;
  width: 100px;
}
.history {
  text-align: center;
}
/*
 * https://frontendeval.com/questions/undoable-counter
 *
 * Create a simple counter with undo/redo functionality
 */
let history = [0];
let redoHistory = [];
let counter = 0;
const UNDO_MAX_LENGTH = 51;

function undo() {
  if (history.length > 1 ) {
    const currentValue = history.pop();
    redoHistory.push(currentValue);
    counter = history[history.length - 1];
    render(counter);
    removeLastFromHistory();
  }
}
function redo() {
  if (redoHistory.length > 0) {
    const currentValue = redoHistory.pop();
    addToQueue(currentValue);
    counter = history[history.length - 1];
    render(counter);
    addToHistory();
  }
  
}
function exec(expresion) {
  const number = parseInt(expresion);
  counter += number;
  addToQueue(counter);
  cleanRedoHistory();
  render(counter);
  addToHistory();
}

function render(counter) {
  const p = document.getElementById('counter');
  p.innerHTML = counter;
}

function cleanRedoHistory() {
  redoHistory = [];
}

function addToQueue(number) {
  history = history.length === UNDO_MAX_LENGTH ?
    [...history.slice(1), number] : [...history, number];
}

function addToHistory() {
  const historyEl = document.getElementById('historyList');
  const item = createHistoryItem();
  historyEl.appendChild(item); 
}
function removeLastFromHistory(){
  const listEl = document.getElementById('historyList');
  listEl.removeChild(listEl.lastChild);
}

function createHistoryItem() {
  const itemEl = document.createElement('li');
  const actionEl = document.createElement('span');
  const resultEl = document.createElement('span');
  const elBefore = history[history.length-2];
  const elAfter = counter;
  const action = elAfter - elBefore;
  actionEl.innerHTML = action > 0 ? `+${action}` : action;
  resultEl.innerHTML = (history.length > 1) ? `(${elBefore} -> ${elAfter})` : elAfter;
  itemEl.appendChild(actionEl);
  itemEl.appendChild(resultEl);
  return itemEl;
}
Run Pen

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

This Pen doesn't use any external JavaScript resources.