I have a web page that I am attempting to write a Tampermonkey script for that will find Virtual Terminal (VT) color codes in specific elements and then replace them with <span>
elements. I am confident that the core code works fine, but the issue is how I can effectively deploy the code so that as new elements are added to the DOM via the site's own code, mine will detect and rewrite.
(function() {
'use strict';
document.addEventListener("DOMNodeInserted",function(){
setTimeout(function(){
var vtRe = /\x5b(?<color>\d{2}(?:;1)?|0)m/;
var nodeList = document.querySelectorAll("div.command-output:not(.vt-colorized)");
nodeList.forEach((node) => {
// Replace VT Color Codes with span colors
var iText = node.innerText;
var arr = iText.split(/\x1b/);
node.innerText = '';
arr.forEach((item) => {
var newEle = document.createElement("span");
let match = vtRe.exec(item);
if (match) {
switch(match.groups.color) {
case "30":
newEle.style.color = 'black';
break;
case "31":
newEle.style.color = 'darkred';
break;
case "32":
newEle.style.color = 'darkgreen';
break;
case "33":
newEle.style.color = 'darkyellow';
break;
case "34":
newEle.style.color = 'darkblue';
break;
case "35":
newEle.style.color = 'darkmagenta';
break;
case "36":
newEle.style.color = 'darkcyan';
break;
case "37":
newEle.style.color = 'darkgray';
break;
case "30;1":
newEle.style.color = 'gray';
break;
case "31;1":
newEle.style.color = 'red';
break;
case "32;1":
newEle.style.color = 'green';
break;
case "33;1":
newEle.style.color = 'yellow';
break;
case "34;1":
newEle.style.color = 'blue';
break;
case "35;1":
newEle.style.color = 'magenta';
break;
case "36;1":
newEle.style.color = 'cyan';
break;
case "37;1":
newEle.style.color = 'white';
break;
case "32":
newEle.style.color = 'green';
break;
case "0":
break;
default:
break;
}
}
newEle.innerText = item.replace(vtRe,'');
node.append(newEle);
});
node.classList.add('vt-colorized');
});
}, 10000);
},false);
})();
I have tried the following methods:
addEventListener("DOMNodeInserted", ...)
to run the code directly as every element is inserted into the DOM; however, this resulted in a call stack exceeded error due to the infinite loop createdsetInterval(...)
to run the code periodically, looking for the elements that need to be processed by the code; however, this resulted in partial processing or lost data, since the DOM gets changed as the responses come in, which is irregular.addEventListener
→ setTimeout
(as seen in the code above) — this doesn't work either.Am I going about this the wrong way/is there an easier way to handle this?
Mutation events like DomNodeInserted are deprecated.
Use a MutationObserver instead.
Simplified example:
const targetNode = document.querySelector("#cont");
const observerOptions = {
childList: true,
attributes: false,
// Omit (or set to false) to observe only changes to the parent node
subtree: true
}
const observer = new MutationObserver(callback);
observer.observe(targetNode, observerOptions);
function callback(mutationList, observer) {
mutationList.forEach((mutation) => {
if (mutation.type === 'childList' && mutation.addedNodes.length) {
mutation.addedNodes.forEach(el => {
if (el.matches('#cont ul li')) {
console.log('<li> Added')
el.style.color = 'red'
}
});
}
});
}
// insert an element every 1.5 seconds
let ctr=0;
const timer = setInterval(()=>{
if(++ctr===5){
clearInterval(timer)
}
const li = document.createElement('li')
li.textContent = `Item ${ctr}`
document.querySelector('#cont ul').append(li)
},1500)
<div id="cont">
<ul>
</ul>
</div>