7

[Edit: I'm replacing the original, confusing question with a simplified example demonstrating the problem.]

Background

I'm trying to write a userscript which will run in Chrome. This script needs to call a JavaScript function AlertMe() that is outside of the userscript -- this function is part of the page and contains variables that are generated dynamically on the server-side, so it isn't possible to re-write this function in my userscript.

Code

Script on the page (visit the page):

<script type="text/javascript">
    function AlertMe()
    {
        alert("Function AlertMe was called!");
        // then do stuff with strings that were dynamically generated
        // on the server so that I can't easily rewrite this into the userscript
    }
</script>

My userscript (install it in Chrome):

function tryAlert()
{
    if (typeof AlertMe == "undefined") {
        console.log('AlertMe is undefined.');
        window.setTimeout(tryAlert, 100);
    }
    else {
        AlertMe();
    }
}

tryAlert();

The Problem

When I tried to simply call the function, Chrome's console let me know that AlertMe is not defined. Thinking that this was because my userscript was running before all other scripts had been loaded, I used setTimeout to wait for the AlertMe function to become defined.

Unfortunately, if you install the script then visit the page, you'll see that this just outputs AlertMe is undefined. forever and never calls the function. If you type typeof AlertMe into Chrome's console, it will correctly respond with "function", so why is it that my userscript always thinks that AlertMe is undefined?

Michael Martin-Smucker
  • 11,927
  • 7
  • 31
  • 36

3 Answers3

9

You can always write a little function that checks to see if the function is loaded

function waitForFnc(){
  if(typeof absearch == "undefined"){
    window.setTimeout(waitForFnc,50);
  }
  else{
    runMyFunction();
  }
}

function runMyFunction(){
    var urlParams = window.location.search.substring(1).split('&'),
        username = "",
        hscEmailInput = document.getElementById('userfield6'),
        i = 0;
    if (urlParams !== "") {
        for (i = 0; i < urlParams.length; i++) {
            if (urlParams[i].substr(0,4) === "USER") {
                username = urlParams[i].replace('USER=', '');
                hscEmailInput.value = username + '@example.com';
                absearch('&PAGESIZE=1');
            }
        }
    }
}

waitForFnc();
epascarello
  • 204,599
  • 20
  • 195
  • 236
  • I like the timeout option, but wasn't sure on the specifics. Thanks for the example... I'll give this a shot. – Michael Martin-Smucker Feb 23 '11 at 15:23
  • See my update to the question. My script eternally thinks that `absearch` is undefined, even though I can ask the console for `typeof absearch` and it correctly tells me that it's a function. Ideas? – Michael Martin-Smucker Feb 23 '11 at 15:54
  • Okay, I completely rewrote the question. There is now a simplified test case and userscript that you can install if you want to test the issue yourself. Even after Chrome knows that the function is defined, my userscript still thinks it's undefined. I have no idea why. – Michael Martin-Smucker Feb 23 '11 at 16:44
  • @Michael Martin-Smucker - It's a matter of security, not timing. Please see my answer. – Wayne Feb 23 '11 at 17:40
  • @lwburk, thanks. @epascarello, +1 because you answered my question as it was originally phrased, and as a bonus I got to learn about `window.setTimeout` in the process. Thanks :) – Michael Martin-Smucker Feb 23 '11 at 19:34
7

This is not a matter of timing.

You're bumping into Greasemonkey's security restrictions, which prevent you from executing functions in the page. Please see my answer to this previous question for an explanation and some safe workarounds:

UserScripts & Greasemonkey: calling a website's JavaScript functions

Community
  • 1
  • 1
Wayne
  • 59,728
  • 15
  • 131
  • 126
  • Thanks so much. Since I was just calling one function, I used the location hack and it worked perfectly. Plus, that was a good read, and might be a very useful resource in the future. – Michael Martin-Smucker Feb 23 '11 at 18:54
-1

If the problem is indeed that your script runs too early, perhaps checking and waiting until the DOM is ready is the solution: Relying on DOM readiness to invoke a function (instead of window.onload)

Edit

This answer on SO might also prove to be useful.

Community
  • 1
  • 1
no.good.at.coding
  • 20,221
  • 2
  • 60
  • 51
  • The DOM ready event means just that: the document has been loaded. This event happens before external resources (like scripts) have been loaded. If you need something to happen after all resources are available, you should use the window.onload event instead. – glomad Feb 23 '11 at 15:23
  • @ithcy I don't think that's true. While I can't find any definitive answer- [this](https://developer.mozilla.org/en/Gecko-Specific_DOM_Events) and [this bug report](https://bugzilla.mozilla.org/show_bug.cgi?id=481534) seem to suggest that images and CSS are considered external resources- JavaScript is parsed as it appears. I tested this in FF4b11 by modifying and hosting [this page](http://ie.microsoft.com/testdrive/HTML5/DOMContentLoaded/Default.html) locally and accessing it through a proxy throttled to 33kbps. The DOMContentLoaded event fired only once the external JS I added was loaded. – no.good.at.coding Feb 23 '11 at 15:52
  • My belief system - you've destroyed it! :) – glomad Feb 23 '11 at 15:55
  • @ithcy If it helps, I learned something today- I only thought about this when you pointed it out :) – no.good.at.coding Feb 23 '11 at 16:12