Executing code at page-level from Background.js and returning the value
You are quite right in understanding the 3-layer context separation.
- A background page is a separate page and therefore doesn't share JS or DOM with visible pages.
- Content scripts are isolated from the webpage's JS context, but share DOM.
- You can inject code into the page's context using the shared DOM. It has access to the JS context, but not to Chrome APIs.
Background <-> Content talk through Chrome API.
The most primitive is the callback of executeScript
, but it's impractical for anything but one-liners.
The common way is to use Messaging.
Uncommon, but it's possible to communicate using chrome.storage
and its onChanged
event.
Page <-> Extension cannot use the same techniques.
Since injected page-context scripts do not technically differ from page's own scripts, you're looking for methods for a webpage to talk to an extension. There are 2 methods available:
While pages have very, very limited access to
chrome.*
APIs, they can nevertheless use Messaging to contact the extension. This is achieved through"externally_connectable"
method.I have recently described it in detail this answer. In short, if your extension declared that a domain is allowed to communicate with it, and the domain knows the extension's ID, it can send an external message to the extension.
The upside is directly talking to the extension, but the downside is the requirement to specifically whitelist domains you're using this from, and you need to keep track of your extension ID (but since you're injecting the code, you can supply the code with the ID). If you need to use it on any domain, this is unsuitable.
Another solution is to use DOM Events. Since the DOM is shared between the content script and the page script, an event generated by one will be visible to another.
The documentation demonstrates how to use
window.postMessage
for this effect; using Custom Events is conceptually more clear.Again, I answered about this before.
The downside of this method is the requirement for a content script to act as a proxy. Something along these lines must be present in the content script:
while the background script processes this with awindow.addEventListener("PassToBackground", function(evt) {
chrome.runtime.sendMessage(evt.detail);
}, false);chrome.runtime.onMessage
listener.
executeScript
with a file
attribute instead of code
, and not rely on its callback. Messaging is cleaner and allows to return data to background script more than once. I'm trying to get jquery working in the background.js of my chrome extension and it won't work. why?
You are correctly loading jQuery into the background page. If you were to use $(document).ready()
in its code, it would work.
The problem is, when you use executeScript
, the code you specify does not execute in the same context as background.js
. A brand new JS context is created, attached to the tab in question, and that context does not have jQuery in it (even if the tab's context itself has it, by the way, see isolated context).
Furthermore, you're calling executeScript
wrong. Its arguments are:
- integer (optional)
tabId
- object
details
- function (optional)
callback
So, to fix, you will need to inject jQuery first in that execution context, and then your code. You need to chain 2 calls to ensure they execute in the correct order.
chrome.browserAction.onClicked.addListener(function(tab) {
chrome.tabs.executeScript(
tab.id,
{ file: 'jquery-2.1.1.js' },
function() {
chrome.tabs.executeScript(
tab.id,
{ code: '$( document ).ready(function() { console.log( "This works now" ); });' }
);
}
);
});
How to run javascript function in background / without freezing UI
There is on most modern browsers (but not IE9 and earlier): Web Workers.Apparently there is no real way to run something on background...
But I think you're trying to solve the problem at the wrong level: 1. It should be possible to loop through all of your controls in a lot less than five seconds, and 2. It shouldn't be necessary to loop through all controls when only one of them has changed.
I suggest looking to those problems before trying to offload that processing to the background.
For instance, you could have an object that contains the current value of each item, and then have the UI for each item update that object when the value changes. Then you'd have all the values in that object, without having to loop through all the controls again.
Execute web page js from chrome extension content script
Chrome executes content scripts in a sandbox, so you need to inject an inline script to interact with the webpage:
var injectedCode = 'update()';
var script = document.createElement('script');
script.appendChild(document.createTextNode('('+ injectedCode +')();'));
(document.body || document.head || document.documentElement).appendChild(script);
Chrome Extension - Passing object from page to context script
The global variables of a content script and a page-level injected script are isolated.
Emphasis mine.Content scripts execute in a special environment called an isolated world. They have access to the DOM of the page they are injected into, but not to any JavaScript variables or functions created by the page. It looks to each content script as if there is no other JavaScript executing on the page it is running on. The same is true in reverse: JavaScript running on the page cannot call any functions or access any variables defined by content scripts.
To pass the data to your content script, you don't have to employ extra DOM elements. You just need custom DOM events.
// Content script
// Listen for the event
window.addEventListener("FromPage", function(evt) {
/* message is in evt.detail */
}, false);
// Page context
var message = {/* whatever */};
var event = new CustomEvent("FromPage", {detail: message});
window.dispatchEvent(event);
See this answer for far more details.
Related Topics
Escaping Backslash in String - JavaScript
Prevent JavaScript Keydown Event from Being Handled Multiple Times While Held Down
How to Calculate How Many Seconds Between Two Dates
Localstorage Object Is Undefined in Ie
How to Achieve Dynamic Scoping in JavaScript Without Resorting to Eval
Why Escape_JavaScript Before Rendering a Partial
Focus Next Element in Tab Index
Why Duck Typing Is Allowed for Classes in Typescript
JavaScript .Replace Only Replaces First Match
Adding New Data to Firebase Users
Typeerror: Illegal Invocation on Console.Log.Apply
Pass Parameter with Python Flask in External JavaScript
Remove Zero-Width Space Characters from a JavaScript String
Rotate Object on Specific Axis Anywhere in Three.Js - Including Outside of Mesh
React Useeffect Comparing Objects
Jsf/Primefaces Ajax Updates Breaks Jquery Event Listener Function Bindings