<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);
}

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

This Pen doesn't use any external JavaScript resources.