Okay, I'm building an extension like so:
has a browseraction which invokes a popup.html that has an input field, a submit button, and a link that invokes a javascript action
a background.html page that receives the input value data via popup.html and then uses them to inject custom CSS into the page
I'm using localStorage to store the input data for future use. Here's what I need help with:
run javascript in the background.html (which injects the CSS) when a button is clicked in the popup.html
passing along the data from the input field in the popup.html to background.html
I scoured google source code but could not really find anything. I'm not looking to inject the CSS to every page (I know how to do that) or when the browser action icon is clicked (I know how to do that as well). I need to get that CSS injected only when a user inputs data into the popup.html and clicks on a link or button.
UPDATED:
I'm still not able to do what I need to do with this. Here's my code:
<script type="text/javascript">
var bgrSize = localStorage.getItem('gridSize');
function insert(){
chrome.tabs.executeScript(null, {code:'body{ backgroundImage: -webkit-linear-gradient(#eee 0.05em, transparent 0.05em); backgroundPosition: 100% 0 %; backgroundSize: 100% '+ bgrSize +';}'});
}
</script>
That's on my background.html page. Here's what fires from the popup.html page:
function showGrid(){
chrome.extension.getBackgroundPage().insert();
}
The "showGrid" function fires when a button is clicked. The gridSize data is already stored in localStorage (I have a section of the popup.html that updates to show the localStorage information).
What am I doing wrong? I just can't figure it out. Btw, the manifest.json includes my background.html
In popup use chrome.extension.getBackgroundPage and then execute proper function in background page. Inject data with insertCSS or executeScript
Pass data thru function parameter or localStorage if you want use it later.
Resuming:
// popup.html
<script>
window.onload = function(){
document.getElementById('my-form').addEventListener('submit', function(){
console.log('popup');
chrome.extension.getBackgroundPage().insert({
param: 'example',
input: document.getElementById('my-input').value
});
});
}
</script>
<form id="my-form">
<input type="text" id="my-input"/>
<input type="submit" />
</form>
// background.html
function insert(data){
console.log('inserting', data.input);
chrome.tabs.insertCSS(null, {
code: 'a { font-size: ' + data.input + 'px;}'
}, function(){
console.log('inserted');
});
}
// manifest.js
...
"permissions": ["tabs", "*://*/*"]
This example works - I have checked it manually ;)
Related
This is my issue:
I update the localStorage in popup.js in a new tab. I access the same localStorage(same key) in the background.js.
Now this is returning null in every tab apart from the chrome://extensions tab(when I load the extensions.)
I thought localStorage was persistant across all tabs.
Code:
popup.js:
$(document).ready(function (){
alert(localStorage.getItem('filters'));
var oldFilters = localStorage.getItem('filters');
//All the filters show up on the popup.html page.
document.getElementById('td1').innerHTML = oldFilters;
var dat = oldFilters + "," + newArray[j]
localStorage.setItem('filters',String(dat));
}
background.js:
$(window).ready(function() {
// Handler for .ready() called.
var filters = localStorage.getItem('filters');
alert("background + "+ filters);
//This shows all the filters in the chrome:extensions page but always pops up "background + null" in every new tab load.
//changeImage(filters);
});
Background and Browser Action(In your case) Pages live in isolated worlds, their local storage details are not accessible to each other, if you want this sort of access to happen use chrome.storage for your storage needs.
It has few advantages
Your extension's content scripts can directly access user data without the need for a background page.
A user's extension settings can be persisted even when using split incognito behavior.
User data can be stored as objects (the localStorage API stores data in strings).
Methods used
chrome.storage.local.get
chrome.storage.local.set
(use sync instead of local if the data needs to be synchronized with Google Sync)
Demonstration
manifest.json
Ensure all permissions are available for accessing storage API.
{
"name":"Local Storage Demo",
"description":"This is a small use case for using local storage",
"version":"1",
"manifest_version":2,
"background":{
"scripts":["background.js"]
},
"browser_action":{
"default_popup":"popup.html",
"default_icon":"logo.png"
},
"permissions":["storage"]
}
popup.html
A trivial popup html page which refers popup.js to surpass CSP.
<!doctype html>
<html>
<head>
<script src="popup.js"></script>
</head>
<body>
</body>
</html>
background.js
This scripts sets content to chrome storage
//Set some content from background page
chrome.storage.local.set({"identifier":"Some awesome Content"},function (){
console.log("Storage Succesful");
});
//get all contents of chrome storage
chrome.storage.local.get(null,function (obj){
console.log(JSON.stringify(obj));
});
popup.js
This script retrieves and sets content from\to chrome storage
document.addEventListener("DOMContentLoaded",function (){
//Fetch all contents
chrome.storage.local.get(null,function (obj){
console.log(JSON.stringify(obj));
});
//Set some content from browser action
chrome.storage.local.set({"anotherIdentifier":"Another awesome Content"},function (){
console.log("Storage Succesful");
});
});
If you look at outputs of these js pages, communication of storage (Background -> popup and popup -> background) is achieved.
background
I simply want to create a chrome extension where I click on the extension icon, it loads a popup that loads a javascript file.
I was able to do an html only popup simply by adding these two files:
manifest.json
{
..
"browser_action": {
"default_popup": "popup.html",
..
}
}
popup.html
<html>
..
hello world
</html>
problem
I want to actually load a chrome events page so that the popup page calls the events page and interacts with it.
what i have tried
I added this to manifest.json
"background": {
"scripts": ["eventsPage.js"],
"persistent": false
}
and added a simple eventsPage.js file:
chrome.runtime.onInstalled.addListener(onInit);
chrome.runtime.onStartup.addListener(onStartup);
function onInit() {
console.log("on init");
}
function onStartup() {
console.log("on startup");
}
if (chrome.runtime && chrome.runtime.onStartup) {
chrome.runtime.onStartup.addListener(function() {
console.log('on startup stuff');
});
}
when I launch the extension and click on inspect to see chrome dev tools.. nothing shows up on the console:
I've also tried adding the src of eventsPage.js to popup.html:
</head>
..
<script src="eventsPage.js"></script>
<body>
..
but that changes nothing, I can't even find the eventsPage.js source in chrome dev tools.
How do I do this?
Many ways:
Add a script for example popup.js in popup.html and call chrome.runtime.getBackgroundPage(function callback) to interact with event page.
popup.html
...
<script src="popup.js"></script>
...
popup.js
chrome.runtime.getBackgroundPage(backgroundPage => backgroundPage.testMethod());
eventsPage.js
const testMethod = () => console.log('test');
Use Message Passing(there are many examples in this link) to communicate with event page.
Since you want to transfer data between popup page and event page, there are many other workarounds, for example, we could use global storage such as chrome.storage to save/load/react to changes.
Really new to HTML, JavaScript, and Jquery, but I am looking to learn and eventually hoping to make a "shoe bot" program by way of a google chrome extension.
Right now, I just want to link my "Go!" button to take the user to google.com.
It works when I load my HTML document into Chrome directly, however when I try to do the same inside the extension I created, nothing happens.
Here's my manifest file:
{
"manifest_version": 1,
"name": "Shoe bot",
"description": "This extension will provides an ATC & purchase service
for shoes",
"version": "1.0",
"browser_action": {
"default_icon": "yeezy.png",
"default_popup": "popup.html"
},
"content_scripts":[{
"js": ["jquery.js","popup.js"],
"matches": ["http://*/*", "https://*/*"]
}]
}
And then my HTML (popup.html):
<!doctype html>
<html>
<head>
<script src="jquery.js"></script>
<script src="popup.js"></script>
<h1>Size</h1>
<p>
</p>
<select>
<option value = "5.0">5.0</option>
<option value = "6.0">6.0</option>
<option value = "7.0">7.0</option>
<option value = "8.0">8.0</option>
<option value = "9.0">9.0</option>
<option value = "10.0">10.0</option>
<option value = "11.0">11.0</option>
<option value = "12.0">12.0</option>
</select>
<p>
</p>
</head>
<body>
<p>
</p>
<button>Go!</button>
</body>
</html>
And then my JavaScript file (popup.js):
$(document).ready(function(){
$("button").click(function(){
window.location.href = "http://www.google.com";
});
});
Any tips/help is appreciated
I agree with the first answer, that HTML should be used instead of JavaScript for redirection purposes:
Go!
However, if you want to use JavaScript for this (since you said you want to learn), I have listed a few issues I noticed in your code:
Head tag
There are user controls inside the head tag, instead of the body tag. Inside the headtag, there should only be stuff invisible to the user, but relevant to the browser, such as title, scriptand link. Therefore, move your </head> and <body>tag up like so:
<head>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.12.2/jquery.min.js"></script>
<title>Size</title>
</head>
<body>
<!--Site content begins here-->
<h1>Size</h1>
External URLs
For any URL to an external website, should always start with "http://" or "https://". Otherwise you will end up on http://example.com/google.com. For the function, change into this:
window.location.href = "http://google.com";
Bonus debugging tips
Use the console in your browser to check any errors. This is usually accessible by hitting F12 in your browser. Check all the red errors and try to make sense of them. In my experience, the best way to learn to code is to analyze the errors.
if the only function of the button is to redirect the user to another page - the have it as a link and style it like a button using css:
Go!
if you are using Bootstrap - you can incoroporate button styling into the link
Go!
Just use html for this. I created a simple working JSfiddle to demonstrate: https://jsfiddle.net/tnw78uvc/
simply wrap an <a> tag with your link around your <button>
There is something about the button tag you'll need to know, it causes a page reload natively. Basically what you're doing with $('#runBot').click(function () { ... }) is assigning a function (event handler) to the event click of the button. You're doing good this far, but button already has an event handler assigned to it by the browser, and it causes the page to reload before your event handler gets its stuff done.
Thankfully browsers provide a way of preventing native event handlers from firing (running themselves)
When you do
$('#runBot').on('click', function () { ... });
That function you're declaring, gets a parameter called the event parameter, which has properties and methods that let you know more about what happened (a click) and manipulate how the browser handles that click, let's call it ev.
$('#runBot').on('click', function (ev) {
window.location = "google.com";
});
ev has a method called preventDefault which makes exactly what you need, prevent the browser from firing it's default event handler for the event you just received.
So you have to do this:
$('#runBot').on('click', function (ev) {
window.location = "google.com";
ev.preventDefault();
});
NOTE ev in jQuery is not the native event from the browser, but it makes things easier fixing incompatibilities between browsers that do whatever they want with the event, specially IE.
Hope you got that! Now let's look on why your code is not working.
Basically window.location is not how you redirect to another page, you have two ways of doing that, let's use one of them window.location.href
$('#runBot').on('click', function (ev) {
window.location.href = "google.com";
ev.preventDefault();
});
This may be as easy as changing your markup:
<button type=button id="runBot">Go!</button>
The default type for a button, "submit" tries to submit a form.
You should write the link completely if not it will only redirect as your local page !
You also linked the jquery file path in <script> tag .Look like that path https:// don't forget also to add .href to anchor link....
$(document).ready(function(){
$('#runBot').click(function() {
window.location.href = "https://google.com";
});
});
I made a small web app that opens when the user clicks a link in a spreadsheet. The link will take them to web app, which makes changes to a spreadsheet.
I want the browser window to close automatically once the code is finished running.
function doGet(e){
// code make changes to spreadsheet
// here I want browser window to close
return HtmlService.createHtmlOutput(uniqueid + " is marked complete");
};
Thanks for help
A distinction needs to be made between a Web App in a browser tab, and a sidebar or dialog box inside of a Google document (Sheet, Form, Doc).
The question is about a Web App in a browser tab. If you want to close a sidebar or dialog box, then just use:
google.script.host.close()
But this question is for a Web App. You can try putting a script tag in the HTML with code that runs automatically when the window is opened.
<script>
window.top.close();
</script>
If you want a delay:
<script>
setTimeout(function(){ window.top.close(); }, 3000);
</script>
You can try using window.onload.
<script>
window.onload=function(){
console.log("This onload did run");
setTimeout(function(){ window.top.close(); }, 3000);
};
</script>
If you don't care whether the user sees anything or not, you could just run Apps Script Content Service.
I came across your question long after you asked it, but in case you or anyone else is looking for the answer, try this:
To get the browser window to close, create a simple HTML file that instructs itself to close with the instruction window.top.close() like this:
Close Window.html
<!DOCTYPE html>
<html>
<script>
window.top.close();
</script>
</html>
You need to return this as an HTML output so that when the Web App runs, it runs on the client side (as Sandy said in the previous answer). In your scenario, you are already returning an HTML output to inform that some "[uniqueid] is marked complete", and it is assumed that you DON'T wish to close this window after the script runs, else the user will not receive the prompt you intended. That is why you need to keep the instruction window.top.close() outside of the same HTML output that gives this alert. To achieve that, just comma-separate both HTML outputs as two return values.
Here are the two other files that I came up with to model a solution for your use case. FYI: I packaged the user-facing HTML in an email to provide a convenient point of execution, and the code will request an email address for delivery.
Code.gs
function doGet(e){
// code make changes to spreadsheet
var ss = SpreadsheetApp.openById([The key for the spreadsheet you wish you modify])
var sheet = ss.getActiveSheet()
var lastRow = sheet.getLastRow()
var params = JSON.stringify(e);
var paramsArray = JSON.parse(params)
sheet.getRange(lastRow + 1, 1).setValue('Code made changes to spreadsheet at ' + Date())
sheet.getRange(lastRow + 1, 2).setValue(paramsArray.parameter.change)
// here I want browser window to close
var uniqueid = "Someuniqueid"
return HtmlService.createHtmlOutputFromFile('Close Window'), HtmlService.createHtmlOutput(uniqueid + " is marked complete")
};
function mailIt() {
var emailAddress = Browser.inputBox('What email address do you want to send the WebApp to?')
var html = HtmlService.createTemplateFromFile('HTML Email to run Web App')
var htmlCode = html.getRawContent()
Logger.log(htmlCode)
MailApp.sendEmail({
name: "Publish WebApp for survey embed Test",
to: emailAddress,
subject: "Publish WebApp for survey embed Test",
htmlBody: htmlCode
})
}
HTML Email to run Web App
<!DOCTYPE html>
<html>
<form action=[Your current Web App URL in quotes]>
Send text to spreadsheet: <input type="text" name="change"><br>
<input type="submit" value="Submit">
</form>
</html>
Obviously, there are some unanswered questions--like why is it a requirement to automatically close a window while your Web App appears to open only a window that carries a message for the user--but I trust your use case is more complicated than that, and you and others can intelligently leverage the principals in this example to your advantage.
Use the following at the end of the script portion of the HTML file to close a currently open HTML window in Google Sheets:
google.script.host.close();
Instead of using two HtmlServices separated with coma in return statement of your doGet(e) function (that has not worked for me - I got only one return value)
you can put window.top.close(); in onClick event of your submit button like this:
Replace:
<input type="submit" value="Submit">
With:
<input type="submit"
onclick="this.value='Magic ...';
google.script.run.withSuccessHandler(closeWindow); ">
and add somewhere in your html:
<script>
function closeWindow() { window.top.close(); }
</script>
I hope this will be of some use to someone somewhere someday :)
Cheers :)
I'm using programmatic injection to inject my extension's code into a page only when the browser action is clicked.
This is what I have on my extension's event page (per the example in the documentation):
chrome.browserAction.onClicked.addListener(function callback(tab){
chrome.tabs.executeScript(null, {file: "content-script.js"});
});
However, the way this works, the script is injected every time the button is clicked.
How can I change it so that the script is not injected on subsequent button presses - so that it is inserted only the first time the button is clicked on that page?
Put a global variable in your contentscript to judge if the contentscript has been executed.
if (something) { return; }
One way I can think of right now (easy and simple) is to use html5webstorage. Since you are running this code from your background or popup page it will be ok.
if(!localStorage.getItem("isAlreadyInjected")){
localStorage['isAlreadyInjected'] = "true";
chrome.browserAction.onClicked.addListener(function callback(tab){chrome.tabs.executeScript(null, {file: "content-script.js"});});}
So, the very first time when storage value "isAlreadyInjected" does not exist, the listener will be added. Afterwards, even when the browser closes and opens again this value will remain stored and so the listener will not be added to your extension.
UPDATE
As your background page loads only once at the beginning, it can keep variable that is not re-initialized with the browser action click. So you can use that variable to do your job!
background.js
var isAlreadyInjected =false;
function isInjected(){
if(!isAlreadyInjected ){
isAlreadyInjected=true;
return false;
}
else
return true;
}
popup.js
var bgpage=chrome.extension.getBackgroundPage();
if(!bgpage.isInjected()){
chrome.browserAction.onClicked.addListener(function callback(tab) {chrome.tabs.executeScript(null, {file: "content-script.js"});});
}
or
var bgpage=chrome.extension.getBackgroundPage();
chrome.browserAction.onClicked.addListener(function callback(tab) {
if(!bgpage.isInjected()){
chrome.tabs.executeScript(null, {file: "content-script.js"});
}});
I know this is an older question but I encountered the issue now that Manifest V3 is out and persistent background pages have been replaced with service workers. I figured I'd give what I used as a solution in case anyone else needs it. Code must be executed within the global context of the content script. Anytime it tries to inject it again, the relevant code will only be executed if the global variable is not defined.
if (typeof hasBeenExecuted === 'undefined') {
// Code that needs to execute only once goes here
}
var hasBeenExecuted = true;
Hopefully this is helpful for someone else who comes across the question.