0

This is what I have here:

"manifest.json"

{..."permissions": [
"https:/mywebsite.com/"],"content_scripts": [{
  "matches" : ["http://*/*", "https://*/*"],
  "js": ["js/jquery-1.7.2.min.js", "contentScript1.js", "contentScript2.js"],
  "all_frames" : true,
  "run_at": "document_end"
} ]}

"contentScript1.js"

$(document).ready(function() {
$('#someDiv').load('https://mywebsite.com/index.html');}

"contentScript2.js"

function showMessage()
{alert ('Hello World!');}

"index.html"

<a href="" onclick="showMessage();"> <img src="https://mywebsite.com/images/myimage.png"></a>

What I m actually doing here is injecting a clickable picture to the code of the the page that I m visiting and I expect that by clicking the picture a "Hello World" message will be appeared. Despite the fact that the content scripts and the picture are loaded succesfully, when I click on the image the function is not called and I get the following error in the console:

Uncaught ReferenceError: showMessage is not defined

I suppose that it cannot find the function as it is looking for it in the website that I have injected the code and not in the content scripts. But why is that, I mean if I call the function within the content script when it is loaded and not by clicking the image, the message appears. Can anyone get me out of here?

svatsi
  • 31
  • 1
  • 8
  • See [Chrome extension code vs Content scripts vs Injected scripts](http://stackoverflow.com/questions/9915311/chrome-extension-code-vs-content-scripts-vs-injected-scripts). – Rob W Jul 03 '12 at 08:20

2 Answers2

1

I think I m gonna answer my own question:

The reason that this happening is because content scripts run in an isolated world see: http://code.google.com/chrome/extensions/content_scripts.html#execution-environment

So, you simply cannot call functions, once you injected some html code, in content_scripts to perform some work in the current page of user.

What you have to do is to inject your scripts in the page as you do with html code. So:

(1) add the files you want to inject in web resources in your manifest file see: http://code.google.com/chrome/extensions/manifest.html#web_accessible_resources

"web_accessible_resources": [
"Script2.js",
 "index.html",
"jquery-1.7.2.min.js"]

(2) in contentScript1.js (load this as a content_script)

 //inject your javascript files to the head of the page
function injectJs(srcFile) {
   var scr = document.createElement('script');
   scr.type="text/javascript";
   scr.src=srcFile;
   document.getElementsByTagName('head')[0].appendChild(scr);
}

injectJs(chrome.extension.getURL('jquery-1.7.2.min.js'));
injectJs(chrome.extension.getURL('Script2.js'));

//inject your html by loading query and passing your html page
$(document).ready(function() {
$('#someDiv').load(chrome.extension.getURL('./index.html'));}

That's all!

Vijay
  • 439
  • 3
  • 15
svatsi
  • 31
  • 1
  • 8
  • I strongly recommend against **injecting** jQuery directly. You'll break many pages which already define `$`. Make sure that you use [`$.noConflict`](http://api.jquery.com/jQuery.noConflict/), as follows: `(function($){ /*your code here*/ })(jQuery.noConflict(true));` (in `Script2.js`). – Rob W Jul 03 '12 at 13:32
  • You are absolutely right,unfortunatey there are conflicts. The problem seems to be the jQuery library itself and not the code that I m executing in Script2.js. If I m not injecting the JQuery lib the page is ok otherwise I can see for e.g when selecting option in dropdown menus of the page- some elements do not appear, and I can imagine that there must be some conflicts somewhere else too. I' ve read the documentation you provided but I don't understand how this can be solved as I said the conflict seems to be because I m injecting the lib. Can you give me some more details please?? – svatsi Jul 03 '12 at 17:00
  • Try reducing the number of scripts to one. Modify `Script2.js`'s code as I did in my previous comment, paste it after `jQuery-min.js`, and check if the problem persists. – Rob W Jul 03 '12 at 20:00
  • Hi Rob and thanks for responding. I 've tried what you said and the problem persists. I injected only the jQuery-min.js through contentScript1.js which I describe above. Even if I do not inject Script2.js the problem persists. I also tried to inject older versions of jquery and still the same problem. Have you tried it and works fine for you? – svatsi Jul 03 '12 at 20:56
  • Can you post a minimal example (manifest + scripts) so that I can reproduce your problem? – Rob W Jul 03 '12 at 21:07
  • manifest.json : http://pastie.org/4195572 contentScript1.js: http://pastie.org/4195575 – svatsi Jul 03 '12 at 21:59
1

You did not understand my solution to avoid conflicts does not work with your current code. Instead of using $.noConflict, you're wrapping your script injection function in a $().ready method.

You have to remove jQuery from the "js" part of the manifest:

  "js": ["contentScript1.js"],

And contentScript1.js

function injectJs(srcFile) {
    var scr = document.createElement('script');
    scr.src = srcFile;
    document.getElementsByTagName('head')[0].appendChild(scr);
}
injectJs(chrome.extension.getURL('js/jquery-min.js'));
injectJs(chrome.extension.getURL('js/yourscript.js'));

Don't forget to add js/yourscript.js to web_accessible_resources, so that it can be used:

"web_accessible_resources": [
    "index3.html",
    "js/jquery-min.js"
    "js/yourscript.js"
]

In js/yourscript.js, wrap your function logic in an anonymous function in conjunction with $.noConflict. $.noConflict(true) is used to avoid conflicts with scripts in the page. It restores the original value of $ and jQuery.

(function(jQuery, $) {
    // Here, you can do anything you want.
    // jQuery and $ refer to the same jQuery object from `js/jquery-min.js`

})(jQuery, jQuery.noConflict(true));

After looking at your question again, I noticed that you're loading content through ajax: $('#someDiv').load(...). When the script is injected, it runs in the scope of the page. That's why your AJAX call fails: The request is blocked because of the Same origin policy.

Now, we can use a different approach to fix your code. Instead of moving the logic from Content script to the page (by an injected script), we modify the page index.html. The click event is not pre-set, but added in the content script. For example:

"index.html":

<a href="" id="showMessage"> <img src="https://mywebsite.com/images/myimage.png"></a>

"contentscript2.js":

$('#showMessage').click(showMessage);
Rob W
  • 341,306
  • 83
  • 791
  • 678
  • Rob, I never said that the AJAX request fails - the html is succesfully loaded. However, is a very interesting approach what you describe here. So, basically I m gonna need to change every onclick, onmouseover event that I have in the html to .click and .hover jquery functions and include them in a contentscript. So, in this way no script is needed to be injected! Jquery and my scripts will be loaded as content scripts. However, it seems not to work and that the html I injected cannot communicate with contentscript.Have you tried it and works?? – svatsi Jul 04 '12 at 13:36
  • @svatsi Content scripts are only executed when the page loads. It does not activate when content is added dynamically through AJAX. If that's the cause, you can fix it by using [`$(document).on('click', 'selector', function(){ ... })`](http://api.jquery.com/on/) instead of `$('selector').click(...);` – Rob W Jul 04 '12 at 13:41
  • Thar's my main contentscript: http://pastie.org/4199199. That's the second contentscript that I m loading too and I want to hide some images and initialize the 'roundabout' stuff: http://pastie.org/4199019 (however the images remain not hidden and the roundabout stuff are not initialized). Now if I change the second contentscript to: http://pastie.org/4199094 and call the 'runThis()' on click event like this: http://pastie.org/4199225 THEN the code in the second contentscript runs succesfully and the images become hidden and roundabout stuff are initialized – svatsi Jul 04 '12 at 14:49
  • In the first set of scripts, you're evaluating the `runThis` logic when the page is loaded. In the last set of scripts, you're calling `runThis()` on click. That's a big difference... – Rob W Jul 04 '12 at 15:01
  • Yes, but why is not working when the page is loaded? I also tried to put everything in a single script but didn't work. – svatsi Jul 04 '12 at 15:06
  • @svatsi Guess that the element does not exist on load? – Rob W Jul 04 '12 at 15:09
  • OK, found that: $('#someDiv').load(chrome.extension.getURL('./index3.html'), function(){runThis();}); Rob, thanks a lot for your help all this time, I really appreciate. I ve got one last question. I ve noticed that by navigating from 'Home' page to 'Profile' page in FB, the index3.html is not loaded but I need to refresh the page in order to be loaded. Have any idea why this happening? – svatsi Jul 04 '12 at 15:21
  • @svatsi The page is probably updated using AJAX, without unloading the page. You have to add an event listener to capture this click. – Rob W Jul 04 '12 at 15:29
  • Yes, you are right. However, it doesn't seem to work - http://pastie.org/4199898. The hover event works but the click doesn't. Also, even if click event worked what should happen? Call the function (i.e. loading index3.html) and then navigate to user's profile or the opposite? – svatsi Jul 04 '12 at 17:26
  • @svatsi Yes. RE: Click not working; It's probably not working because FB stops the propagation of the event. Since you're developing for Chrome, you can fall back to vanilla JavaScript using [`addEventListener`](https://developer.mozilla.org/en/DOM/element.addEventListener), using the capture flag = true. – Rob W Jul 04 '12 at 17:29
  • It's being some time now that I m trying to add listeners but I get an annoying error all the time. I assume it is the wrong way I m getting the element but I can't figure it out.http://jsfiddle.net/KjrQD/20/ ERROR: TypeError: Object # has no method 'addEventListener' – svatsi Jul 04 '12 at 19:06
  • `getElementsByClassName` returns a NodeList. So, use `[0]`. See http://jsfiddle.net/KjrQD/24/ – Rob W Jul 04 '12 at 19:12
  • Yes, you are right about that - no error now. However, still face the same problem as before - The mouseover event works but the click doesn't. – svatsi Jul 04 '12 at 19:20
  • @svatsi In the fiddle? I was actually thinking about something like this: http://jsfiddle.net/KjrQD/28/ (bind the event to `document.body`, and check the class name/id/whatever property of `event.target` (=clicked element). – Rob W Jul 04 '12 at 19:24
  • fiddle example you provided is working fine! I mean in FB http://pastie.org/4200464 – svatsi Jul 04 '12 at 19:27
  • @svatsi Now, you and I know the problem and solution. But I can imagine that others do not want to read through the long comment chain. Please consider editing your question to include the initial code of the problem. – Rob W Jul 04 '12 at 19:29
  • Yes Rob, I m gonna edit it later. Do you have any idea why the click event does not work even with an eventListener? – svatsi Jul 04 '12 at 19:38
  • @svatsi In your code before my fiddle, you attempted to bind the event to an element which did not exist yet. See [event delegation](http://davidwalsh.name/event-delegate). – Rob W Jul 04 '12 at 19:39
  • fiddle example you provided is fine and I modified my code. Please see here http://pastie.org/4200464 – svatsi Jul 04 '12 at 19:42
  • @svatsi addEventListener('click', func, **true**). The last property is important: It's the difference betwene capturing or not. When the capture flag is set, the event listener is fired on capture, which means that the propagation cannot be blocked. – Rob W Jul 04 '12 at 19:57
  • Yes, you are right. Now the function is called on click event, however it seems to be called before navigating to the page (href="https://www.facebook.com/?ref=logo"), so the index3.html is loaded and then moves away. Is there any way to get the function called after the new page is loaded? – svatsi Jul 04 '12 at 20:07
  • @svatsi On user interaction? By event delegation. Automatically? By hooking FB's ajax method, or using polling. – Rob W Jul 04 '12 at 20:08
  • Yes, on user ineraction. I m not familiar with delegation. Could you give me an example? – svatsi Jul 04 '12 at 20:13
  • @svatsi Event delegation: 1. Bind the event listener to a parent **which exists at run time**. 2. Bind the event listener using the capture flag set to `true`. That is the third argument of `addEventListener`. 3. `event.target` in the event listener refers to the target element, ie. the clicked element. Validate that the element is correct, and do something if necessary: http://jsfiddle.net/pt8RV/5/ – Rob W Jul 04 '12 at 20:58
  • I ve done it as you proposed with delegation and I really can't see the difference. The function is still called before the FB gets me to the new page. If for example I am in the profile page and click to logo-top left (http://pastie.org/4200949), I want the FB to get me in the Home page and THEN execute the function in order to load the index3.html. – svatsi Jul 04 '12 at 21:22
  • Ah, we were talking about different problems: I thought that you failed at implementing a click event for an element. So, you want to execute a function *after* something is loaded? In that case, the simpliest solution without knowing much about the source code is by picking an unique element in the source code, and regularly check if the element exists (or not), using `setInterval`/`setTimeout`. – Rob W Jul 04 '12 at 21:27
  • Might I suggest taking this discussion to chat? The comment string here is getting a little out of hand. That might provide a better mechanism for resolving this problem. – Brad Larson Jul 04 '12 at 21:29
  • Yes, that's the case. I thought about timers - I just didn't want to involve such things because they are not always reliable and you never know what is the connection strength of the user. If there is no other way I ll give it a try now. – svatsi Jul 04 '12 at 21:31
  • @svatsi Let's end the conversation. There're 42 comments at this post, way too much. Especially considering that most comments have nothing to do with the original question, but other questions. If you've got a new question, do not ask it here, but create a new question instead (after doing your own research, of course!). – Rob W Jul 04 '12 at 21:40
  • Yes, I agree! The most difficult part has been solved! Rob, thanks a lot for your help! – svatsi Jul 04 '12 at 21:42