简体   繁体   中英

Javascript not always working in Chrome Extension, when should I run it?

I have a Chrome Extension which is injecting both CSS and Javascript into the page, and while the CSS is always being injected, the Javascript is only working sometimes and not others. By not working, I assume it's running before the stuff exists that it has to deal with.

The Javascript is simply moving a div above another using insertBefore but this isn't always happening, and I assume it's because of when it's loading.

My manifest is currently:

{
  ...
  "content_scripts": [{
    "matches": [ "*://example.com/*" ],
     "js": [
        "js/jquery-1.8.3.min.js", "js/myjavascript.js"
      ],
      "run_at": "document_end",
      "css": [
        "css/mycss.css"
      ]
  }]
} 

I have run_at set to document_end currently because it seemed to run smoother (when it does work), so I figure that's what needs to be changed.

If anybody has any suggestions, they're greatly appreciated.

EDIT: A follow-up question regarding the answer to this question was added here , I felt it may be useful for others if I added it to this question.

If the code is not always working, then that suggests that the element(s) are being created/loaded by the page's javascript. So, you need to use standard AJAX-aware techniques to move that div.

The techniques are: timers, mutation observers, mutation events, and intercepting AJAX XHR (if applicable). These are all discussed in other Stack Overflow questions, but: mutation events are deprecated/buggy; intercepting XHR gets messy and may not apply here; and mutation observers can get complicated.

I'd recommend downloading and adding the waitForKeyElements() utility to your manifest.

Then the code to move that <div> becomes merely:

waitForKeyElements (
    "jQuery selector for div(s) you want to move",
    moveSelectDivs
);
function moveSelectDivs (jNode) {
    jNode.insertBefore ("APPROPRIATE JQUERY SELECTOR");
}


Keep your manifest set to run at document_end . This is equivalent to DOMContentLoaded -- which is when $(document).ready() fires. The default run_at fires too late and at unpredictable times. run_at document_start won't do any good, since: timers, mutation observers, etc. all will not fire until after the DOMContentLoaded event -- for current versions of Chrome anyway (Other browsers don't have the same limits).



Alternate method, for the fastest action but slower webpage:

Regarding:

(the page is loading and then you can see the div move), which is something I wish to avoid.

There are only two ways to do anything before DOMContentLoaded that work in Chrome:

  1. You can set mycss.css to hide the div until after you've moved it. This might be the most cost-effective method, performance wise.
  2. Or, you can override document.createElement() . This will respond to the new element as fast as possible (assuming it's created before DOMContentLoaded ), but it might slow down the page overall, by a noticeable amount.

Set the content script to run at document_start , and the override code must be injected. So, the script would look like:

addJS_Node (null, null, scriptMain);

function scriptMain () {
    var _orig_createElement = document.createElement;

    document.createElement  = function (tag) {
        var element = _orig_createElement.call (document, tag);

        var movingNode      = document.querySelector ("MOVING DIV SELECTOR");
        if (movingNode) {
            var target      = document.querySelector ("TARGET DIV SELECTOR");
            if (target) {
                target.parentNode.insertBefore (movingNode, target);

                document.createElement = _orig_createElement;
            }
        }

        return element;
    };
}

function addJS_Node (text, s_URL, funcToRun) {
    var D                                   = document;
    var scriptNode                          = D.createElement ('script');
    scriptNode.type                         = "text/javascript";
    if (text)       scriptNode.textContent  = text;
    if (s_URL)      scriptNode.src          = s_URL;
    if (funcToRun)  scriptNode.textContent  = '(' + funcToRun.toString() + ')()';

    var targ = D.getElementsByTagName ('head')[0] || D.body || D.documentElement;
    targ.appendChild (scriptNode);
}


Do not try to use jQuery for this.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM