<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;
}
This Pen doesn't use any external CSS resources.
This Pen doesn't use any external JavaScript resources.