Asked  7 Months ago    Answers:  5   Viewed   196 times

I'm totally new to Greasemonkey, javascript, in fact all the UI stuff.

Requirement: Userscript is run by GS once after the page loads. However, I need the same script to be run multiple times without refresh

Use case: For ex, Amazon.com search happens using Ajax. I need to embed a custom element in the search results.

I need to inject my content to the search-results-div along with the results every time a search happens in the same page (there's no page refresh)

My current script runs only with the page refresh.

I hope above explanation is clear. Please help.

 Answers

25

The simplest, most robust way is to use the waitForKeyElements() utility.

Here is a complete script that uses jQuery and waitForKeyElements to alter Amazon search results:

// ==UserScript==
// @name     _Amazon Search, alter results
// @include  http://www.amazon.com/s/*
// @require  http://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js
// @require  https://gist.github.com/raw/2625891/waitForKeyElements.js
// @grant    GM_addStyle
// ==/UserScript==
/*- The @grant directive is needed to work around a design change
    introduced in GM 1.0.   It restores the sandbox.
*/

function addCustomSearchResult (jNode) {
    //***** YOUR CODE HERE *****
    jNode.prepend (
        '<div id="result_000" class="fstRow">Buy my stuff, instead!</div>'
    );
}

waitForKeyElements ("#atfResults", addCustomSearchResult);
Tuesday, June 1, 2021
 
Nickool
answered 7 Months ago
60

You can use GM_getValue, GM_setValue & GM_addValueChangeListener to achieve cross-tab user script communication.

Add the following lines in your user script header.

// @grant        GM_setValue
// @grant        GM_getValue
// @grant        GM_addValueChangeListener

The following lines of rough code will simplify the cross-tab user script communication.

function GM_onMessage(label, callback) {
  GM_addValueChangeListener(label, function() {
    callback.apply(undefined, arguments[2]);
  });
}

function GM_sendMessage(label) {
  GM_setValue(label, Array.from(arguments).slice(1));
}

So all you'll need to do is the following to send and receive messages.

GM_onMessage('_.unique.name.greetings', function(src, message) {
  console.log('[onMessage]', src, '=>', message);
});
GM_sendMessage('_.unique.name.greetings', 'hello', window.location.href);

NOTE Sending messages may not trigger your callback if the message sent is the same as before. This is due to GM_addValueChangeListener not firing because the value has not changed, i.e. same value as before even though GM_setValue is called.

Thursday, August 5, 2021
 
Magnanimity
answered 5 Months ago
13

It's impossible to be sure what's going on, because the target page(s) are behind a pay-wall and their alleged "Free Trial" mechanism blows chunks.

Here are some possible causes of the current behavior:

  1. The initial request is insecure (http) but redirects to a secure page (https).
  2. The first page load does a some other kind of redirect to the actual page.
  3. The target content is in an <iframe> that does not load right away.
  4. The target content is AJAXed-in.
  5. Something exotic that we would need to see the actual page to figure out.
  6. The initial URL does not really end in changesets.

Also, get into the habit of escaping the /s in the middle of regular expressions. It's not always needed, but it will eventually bite you in the [censored] if you don't. So the script should use:

// @include  /^https://.*.rallydev.com/.*/changesets$/

to start, but see below.


Steps to a solution:

  1. Change your @include to account for http and the Possibility of trailing space or trailing slash in the URL. Use:

    // @include  /^https?://.*.rallydev.com/.*/changesets(?:s|/)*$/
    
  2. Examine the page with Firebug. Is the content AJAXed-in? Is it in an <iframe>? If so, what is the iframe URL, if any?
  3. To also detect AJAX and/or redirects, use Firebug's Net panel and/or Wireshark.
  4. If possible, provide us with login credentials so that we may see a problematic page.
  5. Snapshot a problematic page (Save it via Firefox) and link to that HTML and JS in Pastebin.com.
  6. Consider using code like:

    if (window.top != window.self) {
        //--- Don't run on/in frames or iframes.
        return;
    }
    

    To have the script run only in (or not in) iframes, as applicable.


If the problem is caused by AJAX delays (or loading of new content), get around that by using the waitForKeyElements() utility as shown in "Fire Greasemonkey script on AJAX request".

Thursday, August 5, 2021
 
user3082821
answered 5 Months ago
15

@match only works on the protocol/scheme, host, and pathname of a URL.

To trigger off the query parameters, you can either use @include or use @match and also test the URL yourself.
Note that the @match approach performs faster.

With @include, you can use a regex syntax. See, also Include and exclude rules.

In this case, use either:

...
// @include  /^https?://example.com/page.php*key1=value1*/
// ==/UserScript==

**Or:**
...
// @match *://example.com/page.php*
// ==/UserScript==

if (/bkey1=value1b/.test (location.search) ) {
    // DO YOUR STUFF HERE.
}
Monday, August 9, 2021
 
IcedAnt
answered 4 Months ago
32

You should define your overwrite_content_for as the following (if I understand your question correctly):

  def overwrite_content_for(name, content = nil, &block)
    content = capture(&block) if block_given?
    @_content_for[name] = content if content
    @_content_for[name] unless content
  end

Note, that in case your block yields nil, then the old content will be retained. However, the whole idea doesn't sound good, as you're obviously doing some rendering (or at least object instantiation) twice.

Monday, October 11, 2021
 
incandescentman
answered 2 Months ago
Only authorized users can answer the question. Please sign in first, or register a free account.
Not the answer you're looking for? Browse other questions tagged :  
Share