2

I have written a Userscript for Facebook and it groups similar notifications together. Now, using just a static HTML page with old notifications I had, the script works 100%.

Here's the problem: the DIV that holds notifications by default, has no notifications in it until the user clicks on the Notification button. Facebook has an inline onclick event that displays the DIV (Notifications menu) and uses AJAX to grab the notifications. Since my userscript only runs on startup, it finds nothing there - it has no effect on the page. I tried using a .click() event but right when you click the Notifications button it runs my code. The thing is, Facebook is still running it's AJAX request for the notifications, meaning that there are still no notifications for my script to work with, making it have no effect.

I don't want to use a generic setTimeout because I don't want the user to have to see the notifications before and then suddenly see them after. Is there a way to monitor the DIV so once the notifications are added, it runs? Or that once Facebook finishes it's AJAX request, it will run my code?

Justin Johnson
  • 30,978
  • 7
  • 65
  • 89
NessDan
  • 1,137
  • 1
  • 16
  • 34
  • I haven't looked into the code myself, so I'm not prepared to submit an answer, but I would recommend seeing if you can figuring out where the XML HTTP Request (AJAX) object appears, and overriding the .onreadystatechange callback function. Obviously, you should preserve the existing functionality through `xmlobj.onreadystatechange = function(){onreadystatechange(); your_new_functionality();}` Obviously, this will be infinity harder because the Facebook code will probably be obfuscated/compressed, but some js debugging elbow grease should be able to coax out the xml object. – Steven Jun 20 '10 at 01:19
  • 1
    @Steven - that would tightly bind the solution with Facebook's current implementation. Any changes on their side will impact the application. `setInterval` seems to be a more sane approach, until the [DOM Mutation events](http://www.w3.org/TR/2000/REC-DOM-Level-2-Events-20001113/events.html#Events-eventgroupings-mutationevents) become commonplace. Also, see http://stackoverflow.com/questions/1091661/detect-div-content-changes-with-jquery – Anurag Jun 20 '10 at 01:22

3 Answers3

1

You can setInterval() to monitor the div's innerHTML, once populated you can run.

Babiker
  • 18,300
  • 28
  • 78
  • 125
  • For the sake of performance, I'm about to put a setInterval that starts on-click of the Notifications button and does a check every .5sec . How and when can I end it? – NessDan Jun 20 '10 at 02:59
  • Thanks man, I ended up using a setInterval() and called all the JS to change the notifications (I also used CSS to hide the default-looking notifications.) Everything turned out better than expected :) – NessDan Jun 20 '10 at 04:21
  • 1
    using setInterval() is going to further hurt the performance of an already hurting website. It's much better to listen to the DOMNodeInserted event. – erikvold Jun 22 '10 at 20:43
  • Right now I have on click of the notifications button, run the setInterval which calls function X every .1 seconds. I replaced that with adding an event listener to the Facebook notifications on click. This did nothing for me :/ The function wasn't called and all it said was that addEventListener was not defined. Can you give me a code snippet? Code as of now for me looks like this: http://www.jsfiddle.net/aeYpx/ – NessDan Jun 24 '10 at 01:04
0

Here's the problem: the DIV that holds notifications by default, has no notifications in it until the user clicks on the Notification button. Facebook has an inline onclick event that displays the DIV (Notifications menu) and uses AJAX to grab the notifications.

...

I don't want to use a generic setTimeout because I don't want the user to have to see the notifications before and then suddenly see them after. Is there a way to monitor the DIV so once the notifications are added, it runs?

Yes there is:

var notificationDiv = document.getElementById(...);
var lastTimeoutID = null;
var main = function(evt){
  // evt.target is the inserted element
  // if you expect more elements, then do the following:
  lastTimeoutID = setTimeout(function(){
    lastTimeoutID = null;
    // uncomment below block if you only need to do the work once.
    /*
    notificationDiv.removeEventListener("DOMNodeInserted", main, false);
    */

    // do your work here.
  }, 500); // waiting 500ms for next element to be added, otherwise doing work.
  if (lastTimeoutID) {
    clearTimeout(lastTimeoutID);
    lastTimeoutID = null;
  }
};
notificationDiv.addEventListener("DOMNodeInserted", main, false);

All of the timeout code above might be removable depending on what it is you're userscript is doing.

erikvold
  • 15,988
  • 11
  • 54
  • 98
-1

Maybe you can add a setTimeout loop. Something like

setTimeout(function(){
/* check for divs */

if(/* divs are found */){
//apply code
}
else {
setTimeout(/* loop again */);
}

},1000);
tcooc
  • 20,629
  • 3
  • 39
  • 57
  • I see your point but if for some reason Facebook stalls, then I might miss the point. If Facebook loads quite fast, I don't want my script to have to way down the user and make them wait on it. Thanks though! – NessDan Jun 20 '10 at 03:00