Javascript Chrome 扩展设置为 `run_at` `document_start` 运行速度太快?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/28186349/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-23 01:20:43  来源:igfitidea点击:

Chrome extension set to `run_at` `document_start` is running too fast?

javascriptgoogle-chromegoogle-chrome-extensionuserscriptscontent-script

提问by Shadow

EDIT: Something was wrong with my Chrome browser and creating a conflict with my script, a full reinstall eliminated whatever the problem source was. If I happen to find out what was causing it I will include it in here.

编辑:我的 Chrome 浏览器出了点问题,并与我的脚本产生了冲突,完全重新安装消除了任何问题来源。如果我碰巧找出导致它的原因,我会将其包含在此处。

EDIT2: Just to let anyone reading this in 2017 know that I haven't forgotten this and I have never had this problem since my previous edit.

EDIT2:只是为了让任何在 2017 年读到这篇文章的人都知道我没有忘记这一点,自从我之前的编辑以来,我从未遇到过这个问题。

EDIT3: It is 2019 and so far I've never had this problem again.

EDIT3:现在是 2019 年,到目前为止我再也没有遇到过这个问题。



I have been learning how to create a simple Chrome extension which is a userscript port. The script works perfectly with Tampermonkey with the setting run atto document-start, all the necessary events that need to be caught from the beginning are all captured.

我一直在学习如何创建一个简单的 Chrome 扩展程序,它是一个用户脚本端口。该脚本与 Tampermonkey 完美配合,设置run atdocument-start,所有需要从一开始就捕获的必要事件都被捕获。

However, when I set the same settings in the Chrome extension I discovered that the same running setting is faster than Tampermonkey's which causes the first function to fail: (Uncaught TypeError: Cannot call method 'appendChild' of null.) since it tries to append a script element to the headsection, which doesn't exist until 0.010s later.

但是,当我在 Chrome 扩展程序中设置相同的设置时,我发现相同的运行设置比 Tampermonkey 更快,这导致第一个函数失败:( Uncaught TypeError: Cannot call method 'appendChild' of null.) 因为它试图将脚本元素附加到head不存在的部分直到 0.010 秒后。

My dirty solution so far has been to make use of a setIntervalfunction with the timer set to 10 to check if document.headexists and then proceed with the code if the condition is true.

到目前为止,我的肮脏解决方案是使用setInterval定时器设置为 10的函数来检查是否document.head存在,如果条件为真,则继续执行代码。

Is there any way that I can make this work correctly without having to resort to setIntervalor maybe replicate Tampermonkey's grant noneoption which appears to run the userscript on the webpage context?

有什么方法可以使我正常工作而不必求助于setInterval或复制 Tampermonkey 的grant none选项,该选项似乎在网页上下文中运行用户脚本?

The following is my manifest.json file:

以下是我的 manifest.json 文件:

{
    "manifest_version": 2,
    "content_scripts": [ {
        "js":        [ "simpleuserscript.user.js" ],
        "matches":   [ "https://www.google.com/*"],
        "run_at":    "document_start"
    } ],
    "converted_from_user_script": true,
    "description":  "Chrome extension",
    "name":         "Testing",
    "version":      "1"
}

All of this could be avoided if Chrome would adopt the afterscriptexecuteevent, but until that happens I am stuck with the loadevent. I thank in advance any help provided.

如果 Chrome 接受该afterscriptexecute事件,所有这些都可以避免,但在此之前,我一直坚持该load事件。我预先感谢提供的任何帮助。



EDIT: I have already tried the suggestions in the replies: using a different run atpoint, using DOMContentLoadedand append to document.documentElement. All were unsuccessful because: 1 and 2 makes the script miss early events, and 3 returns the same TypeError as when trying to append to document.head.

编辑:我已经尝试了回复中的建议:使用不同的run at点,使用DOMContentLoaded并附加到document.documentElement. 一切都不成功,因为: 1 和 2 使脚本错过早期事件,并且 3 返回与尝试附加到document.head.

The script has to be inserted/running when document.readyState = loadingor else it will miss early necessary events, but not so early to the point of being unable to append childs to either documentElementor head

脚本必须在何时插入/运行,document.readyState = loading否则它会错过早期必要的事件,但不会太早以至于无法将子项附加到documentElementhead

An example of the code inside simpleuserscript.user.js:

里面的代码示例simpleuserscript.user.js

var script = document.createElement("script");
script.textContent = "console.log('success')";
if(document.head) {
    document.head.appendChild(script);
} else if(document.documentElement) {
    document.documentElement.appendChild(script);
}

Console will show TypeError: Cannot call method 'appendChild' of null

控制台会显示 TypeError: Cannot call method 'appendChild' of null

回答by Brock Adams

Chrome extension Content scripts(run from a manifest.json) that are run at document_start, dofire before document.readyStateDochas reached interactive-- which is the earliest you want to start messing with most page elements.

Chrome扩展内容的脚本(从运行manifest.json是在运行)document_start火之前document.readyState文件已经达到了interactive-这是你要开始与大多数网页元素搞乱最早的。

However, you can inject most <script>nodes right away if you wish. Just not to document.heador document.bodybecause they don't exist yet.
Append to documentElementinstead. For example:

但是,<script>如果您愿意,您可以立即注入大多数节点。只是不这样做document.headdocument.body因为它们还不存在。改为
追加documentElement。例如:

var s = document.createElement ("script");
s.src = "http://ajax.googleapis.com/ajax/libs/jquery/2.1.0/jquery.min.js";
s.async = false;
document.documentElement.appendChild (s);

Or

或者

var s = document.createElement ("script");
s.src = chrome.extension.getURL ("MyPwnCode.js");
s.async = false;
document.documentElement.appendChild (s);


If you are adding or modifying otherDOM elements, in a script running at document_start, wait until the DOMContentLoadedevent like so:

如果您正在添加或修改其他DOM 元素,请在运行于 的脚本中document_start等待,直到DOMContentLoaded发生如下事件:

document.addEventListener('DOMContentLoaded', fireContentLoadedEvent, false);

function fireContentLoadedEvent () {
    console.log ("DOMContentLoaded");
    // PUT YOUR CODE HERE.
    //document.body.textContent = "Changed this!";
}

回答by Marco Bonelli

Your problem is that, when using "run_at": "document_start", the only element that is granted to exist in the DOM is the <html>element. If you want to avoid errors relative to page load, like trying to access some element that hasn't been created yet, you'll have to either:

您的问题是,在使用时"run_at": "document_start",DOM 中唯一被授予存在的<html>元素是元素。如果您想避免与页面加载相关的错误,例如尝试访问一些尚未创建的元素,您必须:

  • Make your script run at "document_idle"or "document_end". Although "document_end"still doesn't grant you that all the elements and resources of the page have been fully loaded (but the DOM has already been parsed), the "document_idle"keyword will give you the certainty that the DOM has been parsed and all the elements and resources have been loaded properly before your script runs.

  • Or, instead, you can continue using "document_start"wrapping your code inside a "DOMContentLoaded"listener, which will make it run when the DOM has completely finished loading and parsing, similarly to "document_idle". Here's an example:

    document.addEventListener("DOMContentLoaded", function() {
        // Run your code here...
    });
    
  • 使您的脚本在"document_idle"或 处运行"document_end"。虽然"document_end"仍然不能保证页面的所有元素和资源都已完全加载(但 DOM 已经被解析),但"document_idle"关键字会给你确定 DOM 已经被解析并且所有元素和资源已经在脚本运行之前正确加载。

  • 或者,您可以继续使用"document_start"将代码包装在"DOMContentLoaded"侦听器中,这将使其在 DOM 完全完成加载和解析后运行,类似于"document_idle". 下面是一个例子:

    document.addEventListener("DOMContentLoaded", function() {
        // Run your code here...
    });