I'm writing a simple chrome extension which on click inserts some text in Swedish in the DOM. Works fine. So whenever a user clicks the extension, the text in inserted.
Now I want to extend this a little bit. What I want to achieve:
1) When the user clicks the extension the first time, a text in Swedish is inserted
2) When the user clicks a second time, a text in English is inserted instead
So basically I want to "toggle" the language inserted between Swedish and English.
My manifest.json:
{
"name": "TextInserter",
"version": "1",
"manifest_version" : 2,
"description": "Inserts text",
"background" : {
"scripts" : ["background.js"],
"persistent": false
},
"permissions": ["activeTab"],
"browser_action": {}
}
The code in background.js:
chrome.tabs.executeScript(tab.id, {
code: 'var toggle = true;' // How can I toggle this value?
}, function() {
chrome.tabs.executeScript(tab.id, {file: 'content.js'});
});
The code in content.js:
var textarea = document.getElementById('description');
if (textarea && toggle) { // Is always true
textarea.value="Hej";
} else if (textarea && !toggle) {
textarea.value="Hello";
}
toggle = !toggle; // Change the value of toggle so next time text is inserted in other language
This of course doesn't work since I'm setting
var toggle = true;
in background.js.
How can I achieve this toggle effect?
Related
Hi i wanted to create extension that will trigger alert every 30mins to remind me check my posture. But i got stuck. I dont know how to make it so that alert triggers only in tab that im currently in. Now it triggers in every tab i have opened. Can someone help me please? Thanks.
As im thinking right now this way it will start new cycle every time i open new tab right? So im gonna see it in 30min only if i stay in that current tab.
setInterval(function() {
alert("Posture!");
}, 5000);
{
"name": "Posture Checker",
"version": "1.0",
"manifest_version": 2,
"content_scripts": [
{
"matches": [
"<all_urls>"
],
"js": ["posturecheck.js"]
}
]
}
You can check if a tab is in focus by checking if document.hidden.
if (document.hidden) {
// Document/tab/window is hidden
} else {
// Document/tab/window is visible
}
Alternatively, you can also check document.visibiliyState, but it does not return a boolean but a string value that you need to check against:
if (document.visibilityState === 'hidden') {
// Document/tab/window is hidden
} else if (document.visibilityState === 'visible') {
// Document/tab/window is visible
}
I want to make an extension that changes the title on Chrome's built in stopwatch when i start and stop it. I am getting an error trying to retrieve the button's class. The JavaScript works when pasted in the browser console so I think the problem is with the json file.
Here is my JavaScript.
var title = document.querySelector("title");
var button = document.querySelector(".act-desktop-button");
var flip = true;
button.addEventListener("click", function(){
flip =! flip;
console.log(flip);
if(flip === true){
title.textContent = "OFF";
} else{
title.textContent = "ON";
}
});
and here is my json
{
"name": "Stopwatch Tracker",
"version": "1.0",
"description": "works with chromes built in stopwatch feature. Changes the
title of the page to 'ON' or 'OFF' depending on if the stopwatch is
running.",
"permissions":[
"tabs",
"*://*.google.com/search?q=stopwatch*"
],
"content_scripts": [
{
"matches": ["*://*.google.com/search?q=stopwatch*"],
"js": ["background.js"]
}
],
"manifest_version": 2
}
I am receiving this error:
I think, what is the most important part here is to make sure that you have checked the value of button to be not null before adding an event listener.
Just like this:
var button = document.querySelector(".act-desktop-button");
var flip = true;
if (button){
button.addEventListener("click", function(){
...
})};
Try also to check this SO post as this is related to your question.
I am trying to add text to an editable field with a context menu.
I tried to follow this SO but I cannot seem to get it to add the text to the field.
This is my content, which seems to make sense. I believe it is adding the context for what the background script is looking for.
var clickedEl = null;
document.addEventListener("mousedown", function(event){
//right click
if(event.button == 2) {
clickedEl = event.target;
}
}, true);
chrome.runtime.onMessage.addListener(function(request, sender, sendResponse) {
if(request == "getClickedEl") {
sendResponse({value: clickedEl.value});
}
});
And here is what I have for my Background script. This is the part where I am not sure if I am doing it correctly.
function onClickHandler(info, tab) {
if (info.menuItemId.indexOf("context") > -1) {
var type = info.menuItemId.replace('context', '');
theLog = type;
function mycallback(info, tab) {
chrome.tabs.sendMessage(tab.id, "getClickedEl", function(clickedEl) {
elt.value = theLog.value;
});
}
}
}
Your background script runs in a separate hidden page with its own URL and DOM, which cannot access the web page directly, see the architecture overview in the documentation. Simply send the text to the content script, which will then use document.execCommand to insert the value into the active element.
Solution 1.
content script:
chrome.runtime.onMessage.addListener(msg => {
document.execCommand('insertText', false, msg);
});
background script:
chrome.contextMenus.onClicked.addListener((info, tab) => {
if (info.menuItemId.includes('context')) {
const text = info.menuItemId.replace('context', '');
chrome.tabs.sendMessage(tab.id, text, {frameId: info.frameId || 0});
}
}
Note we're sending directly to the frame where the context menu was invoked, which is needed in the general case (maybe not in yours) with the content script running in all iframes which is declared in manifest.json:
"content_scripts": [{
"matches": ["<all_urls>"],
"all_frames": true,
"match_about_blank": true,
"js": ["content.js"]
}]
Solution 2.
However, if this is the only function of the content script, it's better not to declare it in manifest.json at all, but instead inject dynamically in the background script:
chrome.contextMenus.onClicked.addListener((info, tab) => {
if (info.menuItemId.includes('context')) {
const text = info.menuItemId.replace('context', '');
chrome.tabs.executeScript(tab.id, {
frameId: info.frameId || 0,
matchAboutBlank: true,
code: `document.execCommand('insertText', false, ${JSON.stringify(text)})`,
});
}
}
And add the permission in manifest.json that doesn't require a user confirmation on installation (documentation):
"permissions": ["activeTab"]
I am trying to write a Chrome extension which can be disabled with a simple click on the browser action icon. I want to give the users this option because the extension raises a javascript alert when the alarm is triggered. The problem is that even after clearing the alarms, I am seeing alerts being raised. Also the icon click, which I want to work as a switch, isn't working as intended.
I have declared background.js as my background javascript file in manifest.json
"background": {
"scripts": ["alert.js"],
"persistent": true
},
"browser_action": {
"default_icon": "images/green.png",
"default_action": "popup.html",
"default_title": "Toggle Productiwitty"
}
background.js
var ExtensionOn = true;
function SwitchOn(e)
{
chrome.alarms.create("Alarm", {delayInMinutes: 0.1, periodInMinutes: 1} );
}
function SwitchOff(e)
{
chrome.alarms.clear("Alarm");
}
function showpopup()
{
alert("Inside function showpopup");
console.log("alert shown");
}
function click(e)
{
if(ExtensionOn)
{
SwitchOff();
console.log("switched off");
chrome.browserAction.setBadgeText({text: "Off"});
}
else if(!ExtensionOn)
{
SwitchOn();
console.log("switched on");
chrome.browserAction.setBadgeText({text: "ON"});
// Replace 15.0 with user selected time in minutes
}
//Toggle ExtensionOn
ExtensionOn = ~ ExtensionOn;
}
if(ExtensionOn)
{
chrome.alarms.onAlarm.addListener(showpopup);
}
chrome.browserAction.onClicked.addListener(click);
My "default_action": "popup.html" calls popup.js which creates the alarm
chrome.alarms.create("Alarm", {delayInMinutes: 0.1, periodInMinutes: 1} );
The idea is that once the extension is loaded, it should show a popup every 1 minute and if you click the icon, the extension gets disabled temporarily. On clicking the icon again, the same alert will be raised periodically.
ExtensionOn = true
> true
ExtensionOn = ~ ExtensionOn
> -2
Boolean(ExtensionOn)
> true
If you want to toggle a boolean, use !, not ~.
if(ExtensionOn)
{
chrome.alarms.onAlarm.addListener(showpopup);
}
This is only called once, when a page first loads. If the extension isn’t on at that time, the listener won’t be added, and the function will never be called. I’d recommend moving the if test into showpopup.
From https://developer.chrome.com/extensions/management#method-setEnabled
chrome.management.setEnabled(ExtId, true/false, callback)
But i'm not sure disabling the extension is what you actually want to do,
once you disable it you have to go to chrome://extension and manually re-enable it.
Background
I have a Chrome extension with a browser action to launch index.html in a new tab.
I'd like to update the extension to open index.html in a popup first, and then include a button users can click to optionally open the app in a new tab.
I don't want this button to show when it's not a popup (since it wouldn't make sense), which means the content script needs to know whether it is a popup in order to show the button.
Questions
This is a two part question:
How does a Chrome extension popup know it's a popup?
How do I pass that information to a content script before the popup is rendered?
What I've tried
I've tried to use chrome.extension.getViews in background.js to firstly determine if a popup is open. Then, I send a message to the content script which then shows the button. However I haven't gotten it to work - views is always an empty array, and the message doesn't seem to ever be received by the content script.
Here are the relevant parts of my manifest.json file:
"background": {
"scripts": ["background.js"]
},
"browser_action": {
"default_icon": {
"19": "img/icon19.png",
"38": "img/icon38.png"
},
"default_title": "Super Simple Tasks",
"default_popup": "index.html"
}
And here's what I've been trying in my background.js:
// Get all popups
var views = chrome.extension.getViews({ type: "popup" });
// Send a message if there is a popup
if (views.length > 0){
chrome.tabs.query({active: true, currentWindow: true}, function(tabs){
chrome.tabs.sendMessage(tabs[0].id, {action: "popup_open"}, function(response) {});
});
};
And then in my content script, I listen for the message and then add a class to the body:
// Listen for the message
chrome.extension.onMessage.addListener(function(msg, sender, sendResponse) {
if (msg.action === 'popup_open') {
// My code here to show the button
}
});
After talking with a friend I discovered an elegant solution that doesn't involve messaging or even a background.js script at all.
I can specify ?popup=true in manifest.json and check for that parameter in my extension's content script. Here's the code:
manifest.json now looks like this:
"browser_action": {
"default_icon": {
"19": "img/icon19.png",
"38": "img/icon38.png"
},
"default_title": "Super Simple Tasks",
"default_popup": "index.html?popup=true"
}
The following code in my content script (taken from this answer) checks for ?popup=true. Worth noting that this function can handle multiple URL parameters split by the & character.
function getUrlParameter(sParam) {
var sPageURL = window.location.search.substring(1);
var sURLVariables = sPageURL.split('&');
for (var i = 0; i < sURLVariables.length; i++) {
var sParameterName = sURLVariables[i].split('=');
if (sParameterName[0] == sParam) {
return sParameterName[1];
}
}
}
var isPopup;
isPopup = getUrlParameter('popup') === 'true';
Finally, add a class to the body if it's a popup:
$('body').toggleClass('popup', isPopup)
In the manifest file add a hash to the url:
"browser_action": {
"default_popup": "index.html#popup"
}
In JavaScript:
if (location.hash === '#popup')
// do something awesome!
I needed something similar as i wanted to create some cross-compatible code for all script types.
I found that this worked quite well.
const SCRIPT_TYPE = (() => {
if (chrome && chrome.extension && chrome.extension.getBackgroundPage && chrome.extension.getBackgroundPage() === window) {
return 'BACKGROUND';
} else if (chrome && chrome.extension && chrome.extension.getBackgroundPage && chrome.extension.getBackgroundPage() !== window) {
return 'POPUP';
} else if (!chrome || !chrome.runtime || !chrome.runtime.onMessage) {
return 'WEB';
} else {
return 'CONTENT';
}
})();
chrome.tabs.getCurrent(function(tab) {
if(tab == undefined)
document.getElementById('mButton').style.display = 'inline-block';
});
I initially set the button's display: none; if the returned tab is undefined, means it's not a tab (so it is popup) and then I display button. You can reverse it of course.
======
Well the sending parameter also works, which in that case you won't need to add the query string in the manifest, just adding it in button's click listener would suffice.
btn.addEventListener('click', function() {
chrome.tabs.create({url: "index.html?popup=false"});
});
And then the same process (reading the query string and comparing, etc).
======
Alternatively you can make a copy of index.html say index2.html, remove the button from index.html, use index2.html in the manifest and index.html for button click. :)