23

I'm not exactly sure if this is due to my manifest setup, or if there's something going on with the .on event and pages that generate content/modify content on the fly, but I've run into a stumbling block.

Here's the basic idea: I want to be able to catch a click on any link with a URL that matches a pattern regardless of where the user is/what page they're looking at (and do other stuff instead of navigating to the link). The problem I'm running into is that my listener won't work on any page that modifies its content after content loads (jQuery's $(document).ready) (e.g. gMail). I'm injecting my javascript all over the place and it's still not quite working.

Here's the listener code (in main.js):

$('a[href^="http://www.google.com/calendar/event?action=TEMPLATE"]').on('click', function(event) 
{
  event.preventDefault();
  SKDMmain(this);
});

Here's the code in my background.html: (injects my script when the page loads as well as when the tab/window is changed to, so it should be there. Note: jQuery is included above, along with all of the local .js files I need)

<script type="text/javascript">
  $(document).ready( function(){
    chrome.tabs.executeScript(null,{file:"main.js"});
  });
  chrome.tabs.onActiveChanged.addListener( function(tabID,somethingElse){
      chrome.tabs.executeScript(tabID,{file:"main.js"});
  });   

  chrome.windows.onFocusChanged.addListener( function(windowID){
    if ( windowId != chrome.windows.WINDOW_ID_NONE ) {
      chrome.tabs.executeScript(null,{file:"main.js"}); 
    }
  });
</script>

But in pages like gMail or this, the listener doesn't catch the event. I originally had this as a content script, but I recently moved it to using background and programmatically injecting, but neither seem to be working quite right.

Here's my manifest, for reference:

{
"name": "SkedjoolMi",
"version": "0.5",
"description": "Automated Google Calendar event scheduling",
"background_page": "background.html",
"permissions": [
  "tabs","http://*/","https://*/"
],
"content_scripts": [
  {
    "matches": ["<all_urls>"],
    "js": ["jquery-1-7-1.js"],
    "run_at": "document_end",
    "all_frames": true
  }
]
}
Paul Irish
  • 47,354
  • 22
  • 98
  • 132
Brad Orego
  • 2,976
  • 3
  • 18
  • 24
  • tabs.onUpdated seems to give a little bit finer resolution (e.g. gMail/Facebook, but it still misses the case in the link I included in that post.... – Brad Orego Jan 15 '12 at 02:49
  • It also seems as if the listener doesn't work for gmail at all. I've been using the same link to test in various areas http://www.google.com/calendar/event?action=TEMPLATE&text=aaaa&dates=20110101T060000Z/20110101T060000Z&details=&location=&trp=false&sprop=&sprop=name: – Brad Orego Jan 15 '12 at 02:51

2 Answers2

66
$('a[href^="http:.......').on('click', function() { ...

Will only work with anchors that are already present when the page is rendered—not dynamically added anchors. The above is exactly identical to

$('a[href^="http:.......').bind('click', function() { ...

Here's how you use on with dynamically added content:

$(document).on("click", 'a[href^="http://www.google.com/calendar/event?action=TEMPLATE"]', function() ...

This creates a delegated event. All clicks that happen in descendants of your document (which is everything) will be inspected to see if the source of the click matches your selector. If it does, your event will fire. That's why it works with dynamically added content.

Ideally, if you can guarantee that all of these anchors will be inside of some container, say, a div with id foo, the follow would be more efficient:

$('#foo').on("click", 'a[href^="http://www.google.com/calendar/event?action=TEMPLATE"]', function() ...
Adam Rackis
  • 82,527
  • 56
  • 270
  • 393
0

I am not sure of the exact context here-- it sounds like an inability to jump IFRAME boundaries due to alien domain security.

I would try a timer that tries to gather anything within your target (the gmail doc part of the DOM), if you can do that, you can workaround the inability of jQ live by rolling your own monitor (store the bound links in an array).

Mark Robbins
  • 2,427
  • 3
  • 24
  • 33
  • I'm not sure if that's going to work. With a timer, isn't it possible that the user could act between my events firing? I wouldn't exactly want to be injecting JS every second-or-whatever, either. Also, without hand-coding exceptions for every site I find that doesn't work; there's no way in knowing where the content will live. I want this to be functional on all pages :/ – Brad Orego Jan 16 '12 at 00:14
  • I think you misunderstand. The timer polls for new links dynamically created, binds your method to them, and puts them in an array (so as not to be rebound next tick). As far as knowing where the content will live, maybe that is a point if this method of binding is not the solitary one used for such elems. I have only worked with greasemonkey on this type of thing -- I am not sure of your environment. – Mark Robbins Jan 16 '12 at 07:48
  • Can you get jQuery to grab targets by using the IFRAME.contentDocument context? (I think it is called contentDocument) – Mark Robbins Jan 16 '12 at 07:55
  • It doesn't seem like this is working. Here's the code: `var iframe = document.getElementById("canvas_frame"); var iframeDoc = iframe.contentDocument; var frameDocBody = iframeDoc.body; $(frameDocBody).on("click",'a[href*="http://www.google.com/calendar/event?action=TEMPLATE', function(event){ event.preventDefault(); SKDMmain(this); });`. It _does_ succeed in getting a reference to the body and all that jazz (I was pulling elements out), but it doesn't respond to the click listener :( – Brad Orego Jan 18 '12 at 23:28
  • Update: Apparently the issue is that it's not seeing the link in the email as a link. I set it to catch any `a` clicks and it didn't fire, but it did fire for the menu on top (Mail, Calendar, Documents, etc.). Will update with more findings – Brad Orego Jan 18 '12 at 23:42