5

I'm trying to find a way to get if the browser is currently busy from JavaScript. I'm looking at making a Firefox extension to inject a Boolean value or something if the current page is loading something (either through ajax or just normal page loads), or the same with a Greasemonkey script, or through some JavaScript API (this would be the best solution, but from what I can see, nothing of the sort exists).

I was wondering what the best way to do this would be. I've been looking for Firefox Addon / Greasemonkey tutorials for making something like this and can't find anything. Does anyone have any tips or resources they could point me towards or better solutions for solving this?

Thanks

Edit: and by busy, I mostly just need to know if the browser is sending or receiving data from a server.

Joel
  • 16,474
  • 17
  • 72
  • 93

3 Answers3

3

jQuery, a great javascript framework for DOM manipulation and performing ajax calls, provides two great hooks for determining when ajax calls are in progress:

$.ajaxStart() and $.ajaxStop()

Both of these hooks take a handler function that will be called when an ajax call is about to start, and when all ajax calls have ceased, respectively. These functions can be bound to any element on the page. You could set a global boolean value in your $.ajaxStart() handler to true and set it back to false in your $.ajaxStop() handler.

You could then check that boolean flag and determine whether ajax calls are in progress.

Something along these lines:

$(document).ajaxStart(function() {
    window.ajaxBusy = true;
});

$(document).ajaxStop(function() {
    window.ajaxBusy = false;
});

As far as determining when the browser is loading the current page, you could check document.readyState. It returns a string of "loading" while the document is loading and a string of "complete" once it has loaded. You can bind a handler to document.onreadystatechange and set a global boolean that will indicate whether the document is still loading or not.

Something like this:

document.onreadystatechange = function() {
    switch (document.readyState) {
        case "loading":
            window.documentLoading = true;
            break;
        case "complete":
            window.documentLoading = false;
            break;
        default:
            window.documentLoading = false;
    }
}   

EDIT:

It appears that $.ajaxStart() and $.ajaxStop() do NOT work for ajax calls invoked without jQuery. All XMLhttprequest objects have an event called readystatechange that you can attach a handler to. You could utilize this functionality to determine whether or not that individual call is done. You could push all references to outstanding calls onto an array, and in a setInterval() check that array's length. If it > 1, there are out standing ajax calls. It's a rough approach, and only one way of getting about it. There are probably other ways to do this. But here's the general approach:

// declare array to hold references to outstanding requets
window.orequets = [];

var req = XMLHttpRequest(); 
// open the request and send it here....
// then attach a handler to `onreadystatechange`
req.onreadystatechange = function() {
    if (req.readyState != 4 || req.readyState != 3) {
        // req is still in progress
        orequests.push(req);
        window.reqPos = orequests.length -1
    } else {
        window.orequests = orequests.slice(reqPos, reqPos + 1);
    }
}

Do the above for each XMLHttpRequest() you will be sending, of course changing the request name for each one. Then run a setInterval() that runs every x amount of milliseconds, and checks the length property of orequests. If it is equal to zero, no requests are happening, if it is greater than zero, requests are still happening. If no requests are happening, you can either clear the interval through clearInterval() or keep it running.

Your setInterval might look something like this:

var ajaxInterval = setInterval(function() {
    if (orequests.length > 0) {
      // ajax calls are in progress
      window.xttpBusy = true;
    } else {
      // ajax calls have ceased
      window.xttpBusy = false;
      // you could call clearInterval(ajaxInterval) here but I don't know if that's your intention
    },
    3000 // run every 3 seconds. (You can decide how often you want to run it)
}); 
Alex
  • 64,178
  • 48
  • 151
  • 180
  • The OP asked how to do this from a Firefox extension, not from a web page that uses jQuery. – PleaseStand Nov 20 '10 at 07:50
  • 1
    I read through the jquery docs on those two methods and they look spot on for what I was looking for. I still have one question though: will those events only bind to Jquery Ajax calls? Or to any JavaScript Ajax call? – Joel Nov 20 '10 at 07:51
  • @idealmachine, I really was asking for any solution including firefox or JavaScript libraries. – Joel Nov 20 '10 at 07:52
  • @idealmachine You can use jQuery in firefox extensions. Many methods exist to include it. Most often I elect to go with this one: http://stackoverflow.com/questions/532507/firefox-extension-with-jquery-1-3/543397#543397 – Alex Nov 20 '10 at 07:53
  • @Alex, Thank you much. I'm going to play around with this. I think it'll do exactly what I need. – Joel Nov 20 '10 at 17:53
  • @Alex, That works great for JQuery ajax calls using `$.ajax({...});` but the events aren't called if I use `var xreq = new XMLHttpRequest(); xreq.open(...); xreq.send();` Here's my code: http://pastie.org/1313324 any way to make that work with just standard XMLHttpRequest objects? – Joel Nov 20 '10 at 18:47
  • Your new solution won't work as I can't go through and change every XMLHttpRequest. This is why I thought maybe a firefox plugin or something to detect when the page is completely loaded (including AJAX calls) would've been a good idea. However you gave me an idea for something, I'm updating my original post to ask exactly how to do it. – Joel Nov 21 '10 at 00:22
  • @Joel I'm sorry I couldn't be of more help. I wish you luck. I'll update this answer if I come across anything that may be of use to you. – Alex Nov 21 '10 at 00:35
3

Here's what I think I'll end up doing. This solution is like the one Alex suggested with the Jquery events, except that it works with anything that uses the XMLHttpRequest (Including Jquery):

var runningAjaxCount = 0;

var oldSend = XMLHttpRequest.prototype.send;
XMLHttpRequest.prototype.send = function() {
    oldOnReady = this.onreadystatechange;
    this.onreadystatechange = function() {
        oldOnReady.call(this);
        if(this.readyState == XMLHttpRequest.DONE) {
            ajaxStopped();
        }
    }
    ajaxStarted();
    oldSend.apply(this, arguments);
}

function ajaxStarted() {
    runningAjaxCount++;
}

function ajaxStopped() {
    runningAjaxCount--;
}

function isCallingAjax() {
    return runningAjaxCount > 0;
}

function isBrowserBusy() {
    return document.readyState != "complete" && isCallingAjax();
}
Joel
  • 16,474
  • 17
  • 72
  • 93
  • I thought I'd comment and say that 3 years later, we're still using this basic approach and it works great! – Joel Jun 08 '14 at 20:50
  • This is really awesome! Thanks! Really usefull for some automatic testing/scraping of websites. – Evers Nov 22 '15 at 20:09
0

The browser technically isn't ever "busy". Business is a very subjective term. Let's assume that the main thread is performing a simple while loop which blocks execution. This could be considered busy, but what if you have something like this:

function busy() {setTimeout(busy, 0);do_something();}
busy();

The browser isn't being blocked (per se), so whether or not the page is "busy" is very unclear. Also, that doesn't even begin to touch on web workers and code in the chrome.

You're going to be hard-pressed to do this, and even if you do, it likely won't work how you expect it to. Good luck, nonetheless.

mattbasta
  • 13,492
  • 9
  • 47
  • 68
  • I really mean network busy. I need to know if thebrowser is sending or recording data. I'll update my post to clarify. Thanks. – Joel Nov 20 '10 at 07:38