I'm learning how to create Chrome extensions. I just started developing one to catch YouTube events. I want to use it with YouTube flash player (later I will try to make it compatible with HTML5).
manifest.json:
{
"name": "MyExtension",
"version": "1.0",
"description": "Gotta catch Youtube events!",
"permissions": ["tabs", "http://*/*"],
"content_scripts" : [{
"matches" : [ "www.youtube.com/*"],
"js" : ["myScript.js"]
}]
}
myScript.js:
function state() { console.log("State Changed!"); }
var player = document.getElementById("movie_player");
player.addEventListener("onStateChange", "state");
console.log("Started!");
The problem is that the console gives me the "Started!", but there is no "State Changed!" when I play/pause YouTube videos.
When this code is put in the console, it worked. What am I doing wrong?
Content scripts are executed in an "isolated world" environment. You have to inject your
state()
method into the page itself.When you want to use one of the
chrome.*
APIs in the script, you have to implement a special event handler, as described in this answer: Chrome extension - retrieving Gmail's original message.Otherwise, if you don't have to use
chrome.*
APIs, I strongly recommend to inject all of your JS code in the page via adding a<script>
tag:Table of contents
Method 1: Inject another file
This is the easiest/best method when you have lots of code. Include your actual JS code in a file within your extension, say
script.js
. Then let your content script be as follows (explained here: Google Chome “Application Shortcut” Custom Javascript):Note: If you use this method, the injected
script.js
file has to be added to the"web_accessible_resources"
section (example). If you do not, Chrome will refuse to load your script and display the following error in the console:Method 2: Inject embedded code
This method is useful when you want to quickly run a small piece of code. (See also: How to disable facebook hotkeys with Chrome extension?).
Note: template literals are only supported in Chrome 41 and above. If you want the extension to work in Chrome 40-, use:
Method 2b: Using a function
For a big chunk of code, quoting the string is not feasible. Instead of using an array, a function can be used, and stringified:
This method works, because the
+
operator on strings and a function converts all objects to a string. If you intend on using the code more than once, it's wise to create a function to avoid code repetition. An implementation might look like:Note: Since the function is serialized, the original scope, and all bound properties are lost!
Method 3: Using an inline event
Sometimes, you want to run some code immediately, e.g. to run some code before the
<head>
element is created. This can be done by inserting a<script>
tag withtextContent
(see method 2/2b).An alternative, but not recommended is to use inline events. It is not recommended because if the page defines a Content Security policy that forbids inline scripts, then inline event listeners are blocked. Inline scripts injected by the extension, on the other hand, still run. If you still want to use inline events, this is how:
Note: This method assumes that there are no other global event listeners that handle the
reset
event. If there is, you can also pick one of the other global events. Just open the JavaScript console (F12), typedocument.documentElement.on
, and pick on of the available events.Dynamic values in the injected code
Occasionally, you need to pass an arbitrary variable to the injected function. For example:
To inject this code, you need to pass the variables as arguments to the anonymous function. Be sure to implement it correctly! The following will not work:
The solution is to use
JSON.stringify
before passing the argument. Example:If you have many variables, it's worthwhile to use
JSON.stringify
once, to improve readability, as follows:If you wish to inject pure function, instead of text, you can use this method:
And you can pass parameters (unfortunatelly no objects and arrays can be stringifyed) to the functions. Add it into the baretheses, like so:
The only thing
missinghidden from Row W's excellent answer is how to call from the injected script to the content script and vice versa (especially if you have objects that can't be stringified).In either the injected or your content script add an event listener:
On the other side (content or injected script) call the event:
in Content script , i add script tag to the head which binds a 'onmessage' handler, inside the handler i use , eval to execute code. In booth content script i use onmessage handler as well , so i get two way communication. Chrome Docs
pmListener.js is a post message url listener
This way , I can have 2 way communication between CS to Real Dom. Its very usefull for example if you need to listen webscoket events , or to any in memory variables or events.
I've also faced the problem of ordering of loaded scripts, which was solved through sequential loading of scripts. The loading is based on Rob W's answer.
The example of usage would be:
Actually, I'm kinda new to JS, so feel free to ping me to the better ways.