zakaria
zakaria

Reputation: 426

waitForKeyElements not waiting for ajax loaded data on some browsers?

In my script for Greasemonkey/Tampermonkey is working perfectly in Google Chrome but in Firefox this:

waitForKeyElements ("table#ID-rowTable tr td span._GAmD", replaceAffid);

is not waiting for AJAX loaded content.

Here is the relevant part of my script:

// ==UserScript==
// @name        ChangeProvider
// @description Change Provider Name
// @include     https://analytics.google.com/analytics/web/*
// @version     1
// @grant       GM_xmlhttpRequest
// ==/UserScript==

<snip>...

waitForKeyElements ("table#ID-rowTable tr td span._GAmD", replaceAffid);

<snip>...

function waitForKeyElements (
    selectorTxt,    /* Required: The jQuery selector string that
                        specifies the desired element(s).
                    */
    actionFunction, /* Required: The code to run when elements are
                        found. It is passed a jNode to the matched
                        element.
                    */
    bWaitOnce,      /* Optional: If false, will continue to scan for
                        new elements even after the first match is
                        found.
                    */
    iframeSelector  /* Optional: If set, identifies the iframe to
                        search.
                    */
) {
    var targetNodes, btargetsFound;

    if (typeof iframeSelector == "undefined")
        targetNodes     = $(selectorTxt);
    else
        targetNodes     = $(iframeSelector).contents ()
                                           .find (selectorTxt);

<snip>...


The full code is at pastebin.com.

Upvotes: 2

Views: 1970

Answers (1)

Brock Adams
Brock Adams

Reputation: 93473

The problem(s) is/are:

  1. waitForKeyElements() requires jQuery.

  2. Your script must either provide jQuery (recommended), or use @grant none mode and be running on a page that already uses jQuery (a brittle way to do things, AKA "time-bomb code").

  3. Tampermonkey has a bug and possible security weakness whereby it doesn't always sandbox properly. This means that the script can (sometimes) see the page's jQuery, even when @grant is not none. This allowed the script to run in Chrome (for now) but is a very dicey thing to depend on.

  4. Firefox properly sandboxes the scope when you use @grant GM_ ... so the script cannot see the page's jQuery.

  5. If you had looked at Firefox's Browser Console, you would have seen error messages pointing you to the problem.

The solution is: Don't use waitForKeyElements without @requireing jQuery!
In fact, you should require both libraries, as shown in this answer, as it (A) Runs faster, (B) Only fetches the code once, when you install/edit the userscript, and (C) makes for cleaner, easier to grok code.

So, your entire script would become something like:

// ==UserScript==
// @name        GoogleAnalytics Change Provider
// @description Change Provider Name
// @include     https://analytics.google.com/analytics/web/*
// @version     1
// @require     http://ajax.googleapis.com/ajax/libs/jquery/2.1.0/jquery.min.js
// @require     https://gist.github.com/raw/2625891/waitForKeyElements.js
// @grant       GM_xmlhttpRequest
// ==/UserScript==
var providers = new Array ();

GM_xmlhttpRequest ( {
    method: "GET",
    url: "http://localhost:3000/api/t2_provider_list",
    onload: function (response) {
        var provider_list = JSON.parse (response.responseText);

        for (i = 0; i < provider_list.length; i++) {
            providers[provider_list[i].analytics_prefix] = provider_list[i].provider_name;
        }
        waitForKeyElements ("table#ID-rowTable tr td span._GAmD", replaceAffid);
    }
} );

/*--- replaceAffid ():  Match the fields with a given pattern 
and replace with the provider name and affid
*/
function replaceAffid () {
    console.log (providers);
    var regExp = /([a-z,A-Z,0-9]+)---([a-z,A-Z,0-9,_,-]+)/g;

    var spans = document.evaluate ("//span[contains(@class, '_GAmD') and not(contains(@class, '_GAe'))]/text()", document, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
    console.log (spans);
    for (var i = 0; i < spans.snapshotLength; i++) {
        match = regExp.exec (spans.snapshotItem (i).textContent);

        if (match != null) {
            if (typeof providers[match[1]] === undefined) {
                // do nothing
            } else {
                spans.snapshotItem (i).textContent = "Provider: " + providers[match[1]] + " \n\r  Affid: " + match[2];
            }
        }
    }
}

Finally, it looks like you pasted in an old version of waitForKeyElements.
Since May of 2012, that function has had this text near the top:

IMPORTANT: This function requires your script to have loaded jQuery.

If you grabbed your copy of the function from one of my old answers, I apologize. I just updated it to avoid a repeat of this confusion.

Upvotes: 1

Related Questions