设置为`run_at``document_start`的Chrome扩展程序运行得太快了?

时间:2015-01-28 06:55:34

标签: javascript google-chrome google-chrome-extension userscripts content-script

编辑:我的Chrome浏览器出现问题并与我的脚本产生冲突,无论问题来源是什么,都可以完全重新安装。如果我碰巧找出导致它的原因,我会把它包含在这里。

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

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


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

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

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

有没有什么方法可以让我正确地工作而不必诉诸setInterval或者可能复制Tampermonkey的grant none选项,该选项似乎在网页上下文中运行用户脚本?

以下是我的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"
}

如果Chrome会采用afterscriptexecute事件,则可以避免所有这一切,但在此之前我会遇到load事件。我提前感谢你提供的任何帮助。


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

必须在document.readyState = loading时插入/运行脚本,否则它将错过早期必要事件,但不能太早到无法将子项附加到documentElement或{{1 }}

head内的代码示例:

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); }

2 个答案:

答案 0 :(得分:17)

document.readyStateDoc之前启动的manifest.json执行

Chrome扩展程序内容脚本(从document_start运行)达到了interactive - 这是你想要开始搞乱大多数页面元素的最早版本。

但是,如果您愿意,可以立即注入大多数<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);

或者

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

如果要在document_start运行的脚本中添加或修改其他 DOM元素,请等到DOMContentLoaded事件,如下所示:

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

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

答案 1 :(得分:6)

您的问题在于,使用"run_at": "document_start"时,DOM中唯一允许存在的元素是<html>元素。如果你想避免相对于页面加载的错误,比如试图访问一些尚未创建的元素,你必须要么:

  • 让您的脚本在"document_idle""document_end"运行。虽然"document_end"仍然没有授予您页面的所有元素和资源都已完全加载(但DOM已经被解析),"document_idle"关键字会让您确定在脚本运行之前,已经解析了DOM并且已经正确加载了所有元素和资源。

  • 或者,您可以继续使用"document_start"将代码包装在"DOMContentLoaded"侦听器中,这将使DOM在完全加载和解析时运行,类似于{{ 1}}。这是一个例子:

    "document_idle"