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