<div class="history">Mutation Observer Undo/Redo</div>
  <div class="options">
    <button id='undo' onclick='undo()' class="disabled red">Undo</button>
    <div contenteditable="" class="editContent"></div>
    <button id='redo' onclick='redo()' class="disabled">Redo</button>
  </div>
  <button class="add disabled" onclick='add()'>ADD</button>
  <ol></ol>
  <div>
    <button onclick="connect()" class="connect">Connect</button>
    <button onclick="disconnect()" class="disconnect red disabled">Disconnect</button>
  </div>
body {
  background: #f1f1f1;
  height: 95vh;
  display: flex;
  align-items: center;
  justify-content: center;
  font-family: sans-serif;
  flex-direction: column;
}

.editContent {
  width: 400px;
  height: 25px;
  background: #fff;
  font-size: 21px;
  display: flex;
  align-items: center;
  padding: 15px;
  border: 1px solid #ddd;
  border-radius: 3px;
}

.editContent:focus,
button:focus {
  outline: 0;
}

.history {
  margin: 20px;
  font-size: 21px;
}

button {
  font-size: 16px;
  margin: 10px 20px;
  padding: 10px 20px;
  background: #07ab0e;
  color: #fff;
  border: 0;
  font-weight: bold;
  border-radius: 3px;
  cursor: pointer;
}

.red {
  background: #F44336;
}

.add {
  background: #3F51B5;
  margin-top: 20px;
}

.disabled {
  pointer-events: none;
  opacity: 0.5;
}

.options {
  display: flex;
}

ol {
  width: 400px;
  height: 200px;
  overflow: auto;
  background: #fff;
  padding: 15px;
  border: 1px solid #ddd;
  list-style-type: none;
}

li {
  padding: 10px;
}
var redoValue, undoValue;
    /* Buttons*/
    var undoButton = document.getElementById('undo');
    var redoButton = document.getElementById('redo');
    var addButton = document.querySelector('.add');
    var connectButton = document.querySelector('.connect');
    var disconnectButton = document.querySelector('.disconnect');

    /* Mutation Observer Targets*/
    var target = document.querySelector('ol');
    var inputTarget = document.querySelector('.editContent');

    /* Mutation Observer Configuration*/
    var config = {
      childList: true
    }

    /*Mutation Observer Creation*/
    var Observer = new MutationObserver((mutationrecords) => {
      mutationrecords.map((mutationrecord)=>{
        if(mutationrecord.target.className === 'editContent'){
          if(mutationrecord.addedNodes.length !==0 && mutationrecord.removedNodes.length !==0){
            Array.from(mutationrecord.removedNodes).map((removedNode)=>redoValue = removedNode.textContent);
          }else if(mutationrecord.addedNodes.length !==0){
            redoValue = undefined;
          }else if(mutationrecord.removedNodes.length !==0){
            Array.from(mutationrecord.removedNodes).map((removedNode)=>undoValue = removedNode.textContent);
          }
        }
        if (mutationrecord.target === 'ol') {
          if(mutationrecord.removedNodes.length>0){
            undoValue = mutationrecord.removedNodes[0].textContent;
            inputTarget.textContent = undoValue;
          }else{
            redoValue = mutationrecord.addedNodes[0].textContent;
          }
        }
      });
    });

    /* Add Child Elements code*/
    let add = () => {
      var li = document.createElement('li');
      var value = inputTarget.textContent;
      li.textContent = value;
      target.append(li);
      inputTarget.textContent = '';
      inputTarget.focus();
      if (target.querySelectorAll('li') !== undefined) {
        undoButton.classList.remove('disabled');
      } else {
        undoButton.classList.add('disabled');
        redoButton.classList.add('disabled');
      }
      addButton.classList.add('disabled');
    }

    /* Enter KeyPress code*/
    inputTarget.addEventListener('keyup', (e) => {
      if (e.keyCode === 13) { add(); }
      inputTarget.textContent === '' ? addButton.classList.add('disabled'):addButton.classList.remove('disabled');
    });

    /*Undo Button Click Code*/
    let undo = () =>{
      redoButton.classList.remove('disabled');
      if(target.querySelectorAll('li')[target.querySelectorAll('li').length-1]){
        undoValue = target.querySelectorAll('li')[target.querySelectorAll('li').length-1].textContent;
        target.querySelectorAll('li')[target.querySelectorAll('li').length-1].remove();
      }else{
        inputTarget.textContent = '';
      }
      inputTarget.textContent = undoValue?undoValue:inputTarget.textContent;
      target.querySelectorAll('li').length === 0 ? undoButton.classList.add('disabled'): 0 ;
    }

    /*Redo Button Click Code*/
    let redo = () =>{
      add();
      if(redoValue !== undefined){
        if(undoValue === redoValue){
          inputTarget.textContent = '';
          redoButton.classList.add('disabled');
        }else{
          inputTarget.textContent = redoValue;
        }
        redoValue = undefined;
      }else{
        inputTarget.textContent = '';
        redoButton.classList.add('disabled');
      }
    }

    /*Observer Start*/
    let connect = () =>{
      /* Start Mutation Observer for List*/
      Observer.observe(target, config);

      /* Start Mutation Observer for List*/
      Observer.observe(inputTarget, config);

      disconnectButton.classList.remove('disabled');
      connectButton.classList.add('disabled');
      console.log('Observers Started to listen!!');
    }

    /*Observer End*/
    let disconnect = () =>{
      Observer.disconnect();
      disconnectButton.classList.add('disabled');
      connectButton.classList.remove('disabled');
      console.log('Observers Disconnected!!');
    }
Run Pen

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

This Pen doesn't use any external JavaScript resources.