javascriptgreasemonkeytypeerrorgm-xmlhttprequest

Looping GM_xmlhttpRequest gives "TypeError Null" on a variable


I have some links in a page. I want to count the responses of each link and insert the numbers in front of the links. Here is what I have:

var links = document.evaluate('..../td[1]/font//a[@href]', document, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
var headings = document.evaluate('.../td[1]',document, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null)
for(var i = 0; i < links.snapshotLength; i++){
  var urls = links.snapshotItem(i).href;
  GM_xmlhttpRequest({
    method: 'GET',
    url: urls,
    onload function (res){
      var dt = document.implementation.createDocumentType("html", 
          "-//W3C//DTD HTML 4.01 Transitional//EN", "http://www.w3.org/TR/html4/loose.dtd");
          doc = document.implementation.createDocument('', '', dt);
          html = doc.createElement('html');
          html.innerHTML = res.responseText;
          doc.appendChild(html);
      var responses = doc.evaluate('.../tr', doc, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
      var nResponse = responses.snapshotLength - 1;
      var numResponse = document.createElement('font');
      numResponse.innerHTML = 
       '<b>' + nResponse +
       '</b>' ;
      headings.snapshotItem(i).parentNode.insertBefore(numResponse, headings.snapshotItem(i));
    }
  });
}

And I got the error message:

TypeError: headings.snapshotItem(...) is null


Solution

  • There are at least 3 problems:

    1. Trying to pass a value to GM_xmlhttpRequest's onload without a closure.
    2. Looping on links, but trying to index headings.
    3. Missing colon after the onload property.

    (1) GM_xmlhttpRequest operates asynchronously. Which means by the time onload fires that the variables i and headings will either be undefined or will be their ultimate value, not the loop value you want.

    To pass a value to onload, use a closure. (In the code below, parseURL provides the closure.)

    (2) The variable i is looping on links, but the code is trying to use it to index headings! It is very unlikely that there are the same number of each (and poor practice, even if there are). Is the "heading" always a parent of the link? If so use that.

    Putting it all together, use code like this:

    var links = document.evaluate (
        '..../td[1]/font//a[@href]', document, null, 
        XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null
    );
    //-- "Headings" are relative to links
    
    for (var J = links.snapshotLength - 1;  J >= 0;  --J) {
        var targUrl = links.snapshotItem (J).href;
        parseURL (targUrl, J);
    }
    
    function parseURL (targUrl, J) {
        GM_xmlhttpRequest ( {
            method: 'GET',
            url:    targUrl,
            onload: function (res) {
                var dt = document.implementation.createDocumentType (
                    "html", "-//W3C//DTD HTML 4.01 Transitional//EN", 
                    "http://www.w3.org/TR/html4/loose.dtd"
                );
                var doc         = document.implementation.createDocument ('', '', dt);
                var html        = doc.createElement ('html');
                html.innerHTML  = res.responseText;
                doc.appendChild (html);
    
                var responses = doc.evaluate (
                    '.../tr', doc, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null
                );
                var nResponse = responses.snapshotLength - 1;
                var numResponse = document.createElement ('font');
                numResponse.innerHTML = '<b>' + nResponse + '</b>';
    
                var heading     = links.snapshotItem (J).parentNode.parentNode;
                heading.parentNode.insertBefore (numResponse, heading);
            }
        } );
    }