<section>
<textarea placeholder="Write something and click Save" rows="10" id="data-input"></textarea>
<div>
<button id="data-save">Save</button>
<button id="data-undo">Undo</button>
</div>
<h2>Current</h2>
<pre id="data-current"></pre>
<h2>History</h2>
<div id="data-history">
</div>
</section>
* {
box-sizing: border-box;
}
body {
padding: 2rem;
}
textarea {
width: 100%;
display: block;
margin-bottom: 1rem;
}
button {
padding: 0.25rem 2rem;
}
button + button {
margin-left: 1rem;
}
pre {
background-color: #eee;
overflow: auto;
display: block;
padding: 1rem;
}
/* PROXY */
const handleGet = (obj, prop) => {
if (prop === "undo") {
const last = obj.history.pop();
if (!last) {
return;
}
const { content, lastUpdated } = last;
Reflect.set(obj, "content", content);
Reflect.set(obj, "lastUpdated", lastUpdated);
return { content, lastUpdated };
}
return Reflect.get(obj, prop);
};
const handleSet = (obj, prop, value) => {
if (prop === "content" && value.length > 0 && value.length < 255) {
const { content, lastUpdated, history = [] } = obj;
const historyUpdated = [...history, { content, lastUpdated }];
if (content && lastUpdated) {
Reflect.set(obj, "history", historyUpdated);
}
Reflect.set(obj, prop, value);
Reflect.set(obj, "lastUpdated", Date.now());
return true;
}
console.error(
"Content length should not be empty and be less than 255 characters"
);
return false;
};
const createDocProxy = (doc) => {
return new Proxy(doc, {
get: handleGet,
set: handleSet
});
};
/* INITIALIZE */
const doc = createDocProxy({
content: "",
lastUpdated: Date.now()
});
/* EVENTS */
const input = document.getElementById("data-input");
const containerCurrent = document.getElementById("data-current");
const containerHistory = document.getElementById("data-history");
const buttonSave = document.getElementById("data-save");
const buttonUndo = document.getElementById("data-undo");
buttonSave.addEventListener("click", save);
buttonUndo.addEventListener("click", undo);
function save() {
const value = input.value;
doc.content = value;
logCurrent();
logHistory();
}
function undo() {
const previous = doc.undo;
if (!previous) {
return;
}
input.value = doc.content;
logCurrent();
logHistory();
}
/* LOG CHANGES */
function logCurrent() {
containerCurrent.innerHTML = doc.content;
}
function logHistory() {
const list = document.createElement("ol");
if (!doc.history || !doc.history.length) {
containerHistory.innerHTML = "";
return;
}
doc.history.forEach((h) => {
const item = document.createElement("li");
item.innerText = h.content;
list.appendChild(item);
});
containerHistory.innerHTML = "";
containerHistory.appendChild(list);
}
This Pen doesn't use any external CSS resources.
This Pen doesn't use any external JavaScript resources.