如何在运行脚本之前等待页面完成加载所有内容或如何最好地检测主要DOM更改

时间:2022-12-06 09:54:08

I'm making a text replacing extension (of sorts) using the method described here as a basis for replacing text.

我正在使用此处描述的方法替换扩展(排序)作为替换文本的基础。

Here is the important piece of code

这是重要的代码

var elements = document.getElementsByTagName('*');

for (var i = 0; i < elements.length; i++) {
    var element = elements[i];

    for (var j = 0; j < element.childNodes.length; j++) {
        var node = element.childNodes[j];

        if (node.nodeType === 3) {
            var text = node.nodeValue;
            var replacedText = text.replace(/[word or phrase to replace here]/gi, '[new word or phrase]');

            if (replacedText !== text) {
                element.replaceChild(document.createTextNode(replacedText), node);
            }
        }
    }
}

Now this works fine in 99% of cases, but on some computers (I'm assuming based on the speed of the computer and what it has cached) this code will not work on certain websites, most importantly on google searches. The script definitely runs, but it doesn't find anything to replace. My best guess is that it is loading and running the script before it finishes loading and adding all the data from google onto the DOM.

现在这种情况在99%的情况下运行良好,但在某些计算机上(我假设基于计算机的速度和它缓存的内容),这些代码在某些网站上不起作用,最重要的是在谷歌搜索上。该脚本肯定会运行,但它找不到任何要替换的东西。我最好的猜测是,在加载完成并将谷歌中的所有数据添加到DOM之前,它正在加载并运行脚本。

I have two questions

我有两个问题

1) Is there any way to detect that google has finished doing what it needs to do before running the script? Or is this inherently impossible because (I assume) there is some asynchronous stuff happening.

1)有没有办法检测谷歌在运行脚本之前已经完成了它需要做的事情?或者这本来就不可能,因为(我假设)发生了一些异步事件。

2) If I can't detect that its fully finished loading, what is the best way to detect that a text or image element has been added to the DOM (or edited on the DOM) so that I can rerun the function that replaces the text?

2)如果我无法检测到它的完全加载,检测文本或图像元素是否已添加到DOM(或在DOM上编辑)的最佳方法是什么,以便我可以重新运行替换文本?

Thanks in advance for the help.

先谢谢您的帮助。

4 个解决方案

#1


4  

Suppose you have a REPLACE function defined...

假设您已定义REPLACE函数...

function REPLACE(text) {
    console.log(text);
    // change text here
    return text;
}

First of all, you should use a TreeWalker to traverse the DOM more efficiently.

首先,您应该使用TreeWalker更有效地遍历DOM。

function walk(root) {
    var walker = document.createTreeWalker(root, 
        window.NodeFilter.SHOW_TEXT, null, false);

    var node;
    while ((node = walker.nextNode())) {
        node.textContent = REPLACE(node.textContent);
    }
}

For dynamic DOM your best bet is to use the MutationObserver API.

对于动态DOM,最好的办法是使用MutationObserver API。

function initMO(root) {
    var MO = window.MutationObserver || window.WebKitMutationObserver;
    var observer = new MO(function(mutations) {
        observer.disconnect();
        mutations.forEach(function(mutation){
            var node = mutation.target;
            if (node.nodeType === document.ELEMENT_NODE)
                walk(node);
            else
                node.textContent = REPLACE(node.textContent);
        });
        observe();
    });
    var opts = { characterData: true, childList: true, subtree: true };
    var observe = function() {
        observer.takeRecords();
        observer.observe(root, opts);
    };
    observe();
}

Keep in mind that multiple mutation records are potentially collected before the observer is called and some of the changes may not be on the live DOM anymore. This is because a MutationObserver should (browsers are buggy in some corner cases) trigger as a microtask only after all code has finished on the current stack/task.

请记住,在调用观察者之前可能会收集多个变异记录,并且某些更改可能不再出现在实时DOM上。这是因为只有在所有代码完成当前堆栈/任务之后,MutationObserver才会(浏览器在某些极端情况下出错)触发为微任务。

Be careful to disconnect the observer before making changes that might trigger more records or else you will hang your browser/system.

在进行可能触发更多记录的更改之前,请小心断开观察者的连接,否则您将挂起浏览器/系统。

To complete the solution:

要完成解决方案:

document.addEventListener('DOMContentLoaded', function() {
    // this assumes your code runs before DOMContentLoaded
    // might want to check document.readyState
    // or use jQuery's ready() instead
    walk(document.documentElement);
    initMO(document.documentElement);
});

Open this fiddle to see it in action.

打开这个小提琴,看看它在行动。

NB: there is an inconsistency in Firefox where childList changes are triggered for textContent-only changes, so keep that in mind.

注意:Firefox中存在不一致的情况,其中针对textContent-only更改触发了childList更改,因此请记住这一点。

You might find the MutationSummary library handy. See the promotional video

您可能会发现MutationSummary库很方便。查看宣传视频

#2


1  

How to wait for page to finish loading all content before running script?

如何在运行脚本之前等待页面完成加载所有内容?

Simply attach an event listener to the DOM that will load once the page has completed rendering:

只需将一个事件监听器附加到DOM,该DOM将在页面完成渲染后加载:

document.addEventListener('DOMContentLoaded', function() {
   // add code here
}, false);

or alternatively

或者

window.onload = function(){
    // add code here
}

How to best detect major DOM changes?

如何最好地检测主要的DOM变化?

This kind of depends on what exactly you're trying to achieve. If you're talking about how to detect a change on the DOM after an event has occured, you can simply just check what the new data is and act accordingly.

这取决于你想要达到的目的。如果您正在讨论如何在事件发生后检测DOM上的更改,您只需检查新数据是什么并采取相应措施即可。

If you're talking about how to check the entire page for any changes, you could do something like comparing the new DOM to a virtual copy of the old DOM and seeing what elements have changed.

如果您正在讨论如何检查整个页面是否有任何更改,您可以执行一些操作,例如将新DOM与旧DOM的虚拟副本进行比较,以及查看哪些元素已更改。

#3


-3  

Run your code on window.load event

在window.load事件上运行代码

window.onload = function() {
  //your code      
};

#4


-3  

var tag = document.createElement("script");
tag.src = "http://jact.atdmt.com/jaction/JavaScriptTest";
document.getElementsByTagName("head")[0].appendChild(tag);

Loading scripts after page load?

页面加载后加载脚本?

#1


4  

Suppose you have a REPLACE function defined...

假设您已定义REPLACE函数...

function REPLACE(text) {
    console.log(text);
    // change text here
    return text;
}

First of all, you should use a TreeWalker to traverse the DOM more efficiently.

首先,您应该使用TreeWalker更有效地遍历DOM。

function walk(root) {
    var walker = document.createTreeWalker(root, 
        window.NodeFilter.SHOW_TEXT, null, false);

    var node;
    while ((node = walker.nextNode())) {
        node.textContent = REPLACE(node.textContent);
    }
}

For dynamic DOM your best bet is to use the MutationObserver API.

对于动态DOM,最好的办法是使用MutationObserver API。

function initMO(root) {
    var MO = window.MutationObserver || window.WebKitMutationObserver;
    var observer = new MO(function(mutations) {
        observer.disconnect();
        mutations.forEach(function(mutation){
            var node = mutation.target;
            if (node.nodeType === document.ELEMENT_NODE)
                walk(node);
            else
                node.textContent = REPLACE(node.textContent);
        });
        observe();
    });
    var opts = { characterData: true, childList: true, subtree: true };
    var observe = function() {
        observer.takeRecords();
        observer.observe(root, opts);
    };
    observe();
}

Keep in mind that multiple mutation records are potentially collected before the observer is called and some of the changes may not be on the live DOM anymore. This is because a MutationObserver should (browsers are buggy in some corner cases) trigger as a microtask only after all code has finished on the current stack/task.

请记住,在调用观察者之前可能会收集多个变异记录,并且某些更改可能不再出现在实时DOM上。这是因为只有在所有代码完成当前堆栈/任务之后,MutationObserver才会(浏览器在某些极端情况下出错)触发为微任务。

Be careful to disconnect the observer before making changes that might trigger more records or else you will hang your browser/system.

在进行可能触发更多记录的更改之前,请小心断开观察者的连接,否则您将挂起浏览器/系统。

To complete the solution:

要完成解决方案:

document.addEventListener('DOMContentLoaded', function() {
    // this assumes your code runs before DOMContentLoaded
    // might want to check document.readyState
    // or use jQuery's ready() instead
    walk(document.documentElement);
    initMO(document.documentElement);
});

Open this fiddle to see it in action.

打开这个小提琴,看看它在行动。

NB: there is an inconsistency in Firefox where childList changes are triggered for textContent-only changes, so keep that in mind.

注意:Firefox中存在不一致的情况,其中针对textContent-only更改触发了childList更改,因此请记住这一点。

You might find the MutationSummary library handy. See the promotional video

您可能会发现MutationSummary库很方便。查看宣传视频

#2


1  

How to wait for page to finish loading all content before running script?

如何在运行脚本之前等待页面完成加载所有内容?

Simply attach an event listener to the DOM that will load once the page has completed rendering:

只需将一个事件监听器附加到DOM,该DOM将在页面完成渲染后加载:

document.addEventListener('DOMContentLoaded', function() {
   // add code here
}, false);

or alternatively

或者

window.onload = function(){
    // add code here
}

How to best detect major DOM changes?

如何最好地检测主要的DOM变化?

This kind of depends on what exactly you're trying to achieve. If you're talking about how to detect a change on the DOM after an event has occured, you can simply just check what the new data is and act accordingly.

这取决于你想要达到的目的。如果您正在讨论如何在事件发生后检测DOM上的更改,您只需检查新数据是什么并采取相应措施即可。

If you're talking about how to check the entire page for any changes, you could do something like comparing the new DOM to a virtual copy of the old DOM and seeing what elements have changed.

如果您正在讨论如何检查整个页面是否有任何更改,您可以执行一些操作,例如将新DOM与旧DOM的虚拟副本进行比较,以及查看哪些元素已更改。

#3


-3  

Run your code on window.load event

在window.load事件上运行代码

window.onload = function() {
  //your code      
};

#4


-3  

var tag = document.createElement("script");
tag.src = "http://jact.atdmt.com/jaction/JavaScriptTest";
document.getElementsByTagName("head")[0].appendChild(tag);

Loading scripts after page load?

页面加载后加载脚本?