如何在添加元素时更改动态页面上的 DOM?
How to alter DOM on dynamic page as elements are added?
我有一个网页,我正在尝试为其编写一个 Tampermonkey 脚本,该脚本将在特定元素中找到虚拟终端 (VT) 颜色代码,然后用 <span>
元素替换它们。我相信核心代码可以正常工作,但问题是我如何有效地部署代码,以便在通过站点自己的代码将新元素添加到 DOM 时,我的代码将检测并重写。
(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);
})();
我试过以下方法:
addEventListener("DOMNodeInserted", ...)
到运行的代码直接把每一个元素都插入到DOM;但是,由于创建了无限循环,这导致调用堆栈超出错误
setInterval(...)
到运行周期性的代码,寻找代码需要处理的元素;但是,这会导致部分处理或数据丢失,因为 DOM 随着响应的到来而改变,这是不规则的。
addEventListener
→ setTimeout
(如上面的代码所示)——这也不起作用。
我是不是做错了way/is有没有更简单的方法来处理这个问题?
为避免无限循环,您可以在添加要在侦听器中检查的节点之前设置一个标志:
(function () {
'use strict';
let inserting = false;
document.addEventListener("DOMNodeInserted", function () {
if (inserting) return;
inserting = true;
var vtRe = /\x5b(?<color>\d{2}(?:;1)?|0)m/;
// ...rest of your code
inserting = false;
});
})();
像 DomNodeInserted 这样的突变事件是 deprecated。
改用 MutationObserver。
简化示例:
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>
我有一个网页,我正在尝试为其编写一个 Tampermonkey 脚本,该脚本将在特定元素中找到虚拟终端 (VT) 颜色代码,然后用 <span>
元素替换它们。我相信核心代码可以正常工作,但问题是我如何有效地部署代码,以便在通过站点自己的代码将新元素添加到 DOM 时,我的代码将检测并重写。
(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);
})();
我试过以下方法:
addEventListener("DOMNodeInserted", ...)
到运行的代码直接把每一个元素都插入到DOM;但是,由于创建了无限循环,这导致调用堆栈超出错误setInterval(...)
到运行周期性的代码,寻找代码需要处理的元素;但是,这会导致部分处理或数据丢失,因为 DOM 随着响应的到来而改变,这是不规则的。addEventListener
→setTimeout
(如上面的代码所示)——这也不起作用。
我是不是做错了way/is有没有更简单的方法来处理这个问题?
为避免无限循环,您可以在添加要在侦听器中检查的节点之前设置一个标志:
(function () {
'use strict';
let inserting = false;
document.addEventListener("DOMNodeInserted", function () {
if (inserting) return;
inserting = true;
var vtRe = /\x5b(?<color>\d{2}(?:;1)?|0)m/;
// ...rest of your code
inserting = false;
});
})();
像 DomNodeInserted 这样的突变事件是 deprecated。
改用 MutationObserver。
简化示例:
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>