<div class="container">
<section class="top-section">
<h2>Undoable Counter</h2>
<div class="do-buttons-container">
<button class="undo" onclick="undo()">Undo</button>
<button class="redo" onclick="redo()" disabled>Redo</button>
</div>
</section>
<section class="counter-section">
<ul class="decrement-buttons">
<li>
<button onclick="run(-100)">-100</button>
</li>
<li>
<button onclick="run(-10)">-10</button>
</li>
<li>
<button onclick="run(-1)">-1</button>
</li>
</ul>
<div class="counter-container">
<p class="current-count">0</p>
</div>
<ul class="increment-buttons">
<li>
<button onclick="run(100)">100</button>
</li>
<li>
<button onclick="run(10)">10</button>
</li>
<li>
<button onclick="run(1)">1</button>
</li>
</ul>
</section>
<section class="history-section">
<h3>History</h3>
<ul class="history-items"></ul>
</section>
</div>
.container {
display: flex;
align-items: center;
flex-direction: column;
}
.top-section {
margin-bottom: 5px;
}
.do-buttons-container {
display: flex;
justify-content: space-evenly;
align-items: center;
width: 80%;
margin: auto;
}
.decrement-buttons, .counter-container, .increment-buttons {
display: inline-block;
padding: 0;
}
.counter-container {
margin: 0 5px;
text-align: center;
}
.decrement-buttons li, .increment-buttons li {
display: inline-block;
list-style: none;
}
.history-section {
text-align: center;
}
.history-items {
border: 1px solid black;
height: 200px;
width: 200px;
overflow-y: auto;
padding: 0;
}
.history-items li {
list-style: none;
}
/*
* https://frontendeval.com/questions/undoable-counter
*
* Create a simple counter with undo/redo functionality
*/
const INITIAL_VALUE = 0;
const MAX_UNDOS = 50;
const counterEl = document.querySelector('.current-count');
const redoBtn = document.querySelector('.redo');
const historyItems = document.querySelector('.history-items');
let currentCount = INITIAL_VALUE;
let oldValue, newValue;
let history = [];
let redos = [];
function run(operation, withClear = true) {
oldValue = currentCount;
newValue = currentCount += operation;
console.log(oldValue, currentCount, newValue);
updateCounter(currentCount);
if (history.length === MAX_UNDOS) {
history = [...history.slice(1), operation];
} else {
history.push(operation);
}
if (withClear) clearRedoArray();
recordHistory(oldValue, newValue, operation);
}
function updateCounter(value) {
counterEl.innerText = value;
}
function undo() {
const toUndo = history.pop();
if (toUndo) {
currentCount -= toUndo;
updateCounter(currentCount);
redos.push(toUndo);
if (redoBtn.disabled) redoBtn.disabled = false;
removeHistory();
}
}
function redo() {
const toRedo = redos.pop();
run(toRedo, false);
if (!redos.length) redoBtn.disabled = true;
}
function clearRedoArray() {
redos = [];
redoBtn.disabled = true;
}
function recordHistory(oldVal, newVal, operation) {
const listItem = document.createElement('li');
const action = document.createElement('span');
const result = document.createElement('span');
action.innerHTML = `${operation}: `;
result.innerHTML = `(${oldVal} -> ${newVal})`
listItem.appendChild(action);
listItem.appendChild(result);
historyItems.appendChild(listItem);
}
function removeHistory() {
historyItems.removeChild(historyItems.lastChild);
}
View Compiled
This Pen doesn't use any external CSS resources.
This Pen doesn't use any external JavaScript resources.