My Chrome extension has a Content-Script that injects a custom DIV into the current page. This part works.
But then, the extension also has a right-click Context Menu, which when clicked, should modify this injected DIV in some way (let's say, add some text into that DIV). The issue is that the injected content isn't found. The right-click menu handler is in Background.js, and this file doesn't know anything about the content.
manifest.js
"content_scripts": [
{
"matches": [
"http://*/*",
"https://*/*"
],
"css": ["contentstyle.css"],
"js": ["jquery-1.11.2.min.js", "contentscript.js"],
"background": {
"persistent": true,
"scripts": ["background.js"]
},
contentscript.js
// Add Custom DIV - works OK
var div = document.createElement( 'div' );
div.id = 'infoDiv';
document.body.appendChild( div );
document.getElementById('infoDiv').innerHTML = 'TEST';
background.js
// Add menu - gets added, but can't see Injected Content from here
chrome.contextMenus.create({
"title": "My Right-Click Menu",
"contexts": ["image"],
"onclick" : changeDiv
});
function changeDiv(e)
{
var divHTML = document.getElementById('infoDiv').innerHTML;
alert('Current HTML in DIV: ' + divHTML);
}
I'm unable to get the divHTML from the Background script, there is some kind of error and no alert box. Can there be communication between the Background and Content? I'm forced to implement menus in the Background script, right?
Wrong document in
var divHTML = document.getElementById('infoDiv').innerHTML;
Please read the Architecture Overview first. Your background script is executed in a separate HTML document, and as such won't "see" the page in the tab.
You'll need to pass the value to the content script to do something with a visible page. You'll probably need Messaging.
Related
I've been working on a new Chrome Extension that pulls the content property from meta tags and displays them in the popup.js window. I've experimented with a few different types of coding, but nothing works. Currently, settings include:
manifest.json
"permissions": ["activeTab", "tabs", "storage", "<all_urls>"],
popup.js
let metaContent = $('#id-string') // div to display in popup
// event listener to click on button in popup to grab meta tag
popupButton.on('click', function(metaContent) {
let tags = $('meta[property="keywords"]').attr('content')
metaContent.html(tags)
}
It looks like this may be a popular problem.
you need to write the grab meta code in a content script, not in popup.js
It seeems that my content script document.URL always has the same value even when I click on a random <a href link.
In the below function in the content script, I have a function called performclick() which finds a random link on the current document, then clicks it.
Now shouldn't the next time I call performclick(), I get another document object? i.e. document of the currently focused active tab?
Like when I click a link on the main page and it opens a new tab with another website loaded in it, then I need the content script to give me the document of this new tab that has the clicked website link loaded in it.
But some how I always keep getting the document object of the same main page.
Please help
Manifest
{
"manifest_version": 2,
"name": "OSP Updater",
"version": "1.0.0",
"content_scripts": [{
"run_at": "document_start",
"js": ["content.js"],
"matches": [ "<all_urls>" ]
}],
"permissions": [
"background", "webRequest", "webRequestBlocking", "tabs", "<all_urls>"
],
"background": {
"scripts": ["background.js"],
"persistent": true
},
"web_accessible_resources": ["jsui.js"]
}
Content Script
window.onload = function () {
var elm = document.getElementById("my-container");
if (elm != undefined && elm != null) {
//alert("Main Page LOADED!!!");
}
}
chrome.runtime.onMessage.addListener(function (request, sender, sendResponse) {
if (request.clickit == "yes") {
performclick();
}
});
function performclick() {
//document.URL is always the same
var links = document.querySelectorAll("a");
if (links.length) {
var linkToClick = links[1];
var clickEvent = document.createEvent('MouseEvents');
clickEvent.initEvent('click', true, true);
linkToClick.dispatchEvent(clickEvent);
}
}
Update #1:
Silly me, it turns out that I was sending a message from my background script to my content script via the first tab.
I did my clicking decision in the background script than sends a signal to the content script so that the content script can do the actual clicking.
So since I was sending message from the background script to the content script in the 1st tab, it lead the document object to always belong to the first tab (i.e. main page).
Hope this helps someone, always check if you are doing messaging then check its logic.
In console I can input document.getElementById('...') and get a value back. Or even .textContent and get the string I want.
Once I pop this into my chrome extension and run it, it evaluates document.getElementById('...') as null. What's up?
Manifest.json:
{
"name": "CSUF RMP",
"version": "0.1",
"manifest_version" : 2,
"description": "Displays professor ratings on icon click",
"background" : {
"scripts" : ["background.js"]
},
"browser_action": {
"default_icon": "icon16.png"
},
"content_scripts": [
{
"matches": ["https://mycsuf.fullerton.edu/*"],
"js": ["script.js"]
}
],
"permissions": ["<all_urls>", "*://*/*", "http://*/*", "https://*/*"]
}
Background.js:
chrome.browserAction.onClicked.addListener(function(tab) {
chrome.tabs.executeScript(null, {file: "script.js"});
});
My script.js is literally what I posted at the top. The script is supposed to have access to the web page's DOM (thus I need a content script) and run it on click of the icon (hence the background.js)
I can get the page to run and show an alert or something, but that line isn't evaluating the page's dom, just null.
I think I know what is the problem here,
you are executing script.js just like a normal script, and a normal script can't interact with the page DOM, you can think about it as just runing a script from a file- it don't have the content script's privileges that way.
What you can do is open a new tab (with the url of the content script), and then pass to the content script at that new tab a message which tells him to run a specific function there.
You can test it without using message sending by setting the onload of the content script to something like: onload=alert(document.getElementById('...')); and than open a new tab from the background page: chrome.tabs.create({"url":"https://mycsuf.fullerton.edu"});
tell me how it goes :)
Edit: forgot to mention that you need the 'tabs' permission in your manifest file in order to open new tabs and test the thing out.
I'm trying to build a somehow dummy Chrome extension. I want it to run only in specific pages, so I'm using a Page Action.
Let's say I want the page action to run on the Instagram website, then (accordingly the docs), I would need something like this on my manifest.json right?
{
"manifest_version": 2,
"name": "Some name",
"version": "0.0.3",
"description": "Some description",
"content_scripts": [
{
"matches": [
"https://www.instagram.com/*"
],
"js": ["content.js"]
}
],
"page_action": {
"default_icon": "icon.png"
},
"background": {
"scripts": ["background.js"]
}
}
while the content script runs only on instagram pages as one would expect, the browser extension is not clickable (gray look, and when I click most options are not clickable).
this makes impossible to act upon extension button click. In my background.js I have:
function click(tab) {
console.log('click from ' + tab);
}
chrome.pageAction.onClicked.addListener(click);
that never gets called.
So, what's wrong that makes impossible to act upon extension click on some pages?
Note: I saw this question/answer, but couldn't find the problem/solution How can I add a click for pageAction?
You have to call pageAction.show in order for your pageAction button to be enabled (clickable).
The pageAction documentation says (emphasis mine):
You make a page action appear and be grayed out using the pageAction.show and pageAction.hide methods, respectively. By default, a page action appears grayed out. When you show it, you specify the tab in which the icon should appear. The icon remains visible until the tab is closed or starts displaying a different URL (because the user clicks a link, for example).
With a manifest.json content_scripts entry
Because you already have a content script that runs on the page you desire to have this function on, probably the easiest way to do this is to have your content script send a message to your background script telling it to show the page-action button for that tab.
Your content script could look something like:
chrome.runtime.sendMessage({type: showPageAction});
Your background script could look something like:
chrome.runtime.onMessage(function(message, sender, sendResponse) {
if(typeof message === 'object' && message.type === 'showPageAction') {
chrome.pageAction.show(sender.tab.id);
}
});
Without a manifest.json content_scripts entry
If you did not have a content script, you would probably want to use a webNavigation.onCompleted listener, or tabs.onUpdated listener, to listen for a change in the tab's URL in order to determine that the page-action button should be shown. Obviously, the trigger for calling pageAction.show() does not have to be the URL which is currently displayed in the tab, but that is the most common.
I'm trying to create a chrome extension. When the user clicks my extension's icon (browserAction) the content script appends an extra div to the body of the open page(current tab). It works fine in all the sites except google's search page and youtube. I'm not getting any error message or anything. It simply wont give any response.
This is my code in content.js:
alert('sdsd');
$('body').append("<div id='popup'>My extension name</div>");
I've put the alert for testing purpose. So when extension is toggled it should show an alert message followed by appending the div to body, ideally! But it wont for these 2 sites.
Any idea what could be going wrong here?
manifest
{
"name": "My first extension",
"version": "1.0",
"background": { "scripts": ["background.js"] },
"content_scripts": [{
"all_frames": true,
"css": ["style.css"],
"matches": ["http://*/*","https://*/*"]
}],
"permissions": [ "tabs","http://*/*" ],
"browser_action": { "name": "test" },
"manifest_version": 2
}
background.js
chrome.browserAction.onClicked.addListener(function(tab){
chrome.tabs.executeScript(null,{file:"jquery.min.js"},function(){
chrome.tabs.executeScript(null,{file:"content.js"});
});
});
In Youtube's page, $ is overwritten and isn't jQuery. It's
bound: function ()
{
return document.getElementById.apply(document, arguments)
}
So your code makes an exception as there document.getElementById('body') is undefined.
You should try using noConflict().
EDIT :
Why aren't you simply listing jQuery.min.js and your content.js in the content_scripts instead of injecting them programmatically. This would avoid conflicts.
EDIT 2 :
Now that you use content scripts, you should use communication as described here to send from background.js to the content script the instruction to show the alert.
EDIT 3 :
Another solution would have been to use programmatic injection (as you initially did) and not use jquery, $('body').append("<div id='popup'>My extension name</div>"); being translated in vanilla JS to
var div = document.createElement('div');
div.id = 'popup';
document.body.appendChild(div);
document.getElementById('popup').innerHTML = "My extension name";
But it's generally cleaner (and requires less permissions) to avoid programmatic injection.