How can a chrome extension click on a button on active page?
There is banal page on the Web. There is simple element of button type with specific ID on the page. Also there is Chrome extension with a button. I'd like to click to extension button and the button in turn clicks to page button.
var someElement = document.getElementById('someElement');
someElement.addEventListener('click', function () {
// here I'd like to click on a button with specific ID.
});
The very first thing you want to do is to read the Overview page, especially the Architecture part. Read it thoroughly, and it will answer many questions you have.
Your problem can be split into two parts.
How to trigger something with a click on your extension.
How to click something inside the active tab.
Before I proceed, I'll reiterate what wOxxOm said: there's a great small example in the docs that does nearly what you want. But if you want to be someone taught to fish, not given a fish, read on.
How to trigger something with a click on your extension
It depends on what kind of UI you're using; the simplest is a Browser Action button.
Simplest button:
If you add a browser action to the manifest without specifying a popup:
"browser_action": {
"default_icon": { "38": "icon38.png" }
},
then clicking on it will raise chrome.browserAction.onClicked event to your extension's pages. The only page open at any time you need it is a background page, the role of which is usually the central dispatch for extension events. So, you need a background page that listens to that event:
"background": {
"scripts": ["background.js"]
},
and
// background.js
chrome.browserAction.onClicked.addListener(function(tab) {
// Okay, the actual action should go here
// And look, we already have the required Tab object for free!
});
Variations (exercises for the reader):
If you do specify a "default_popup" in your manifest, then chrome.browserAction.onClicked will not trigger. Instead, a small popup page will open with the HTML file you specify; you can add UI/logic there as you wish, the principle will be the same as normal webpages but with Chrome API access, except:
You'll need to query for the current tab yourself.
You'll need to be mindful of Chrome's extension CSP.
In case you run into problems, you need to know how to debug them.
If your extension targets only a few specific pages, consider using Page Actions instead of Browser Actions.
As noted in the documentation, Event pages are preferable to Background pages. In this case, you can use an Event page easily without any side effects, but in general this may require some thinking.
You could inject your own UI into the page itself with Content scripts; this is an advanced topic and will not be covered here.
How to click something inside the active tab
Since you've read the Architecture overview, you already know that the only part of the extension that can interact with the DOM of an open page is a Content script.
Content scripts can either be specified in the manifest (and then they will automatically be injected and ready for you when a matching page is opened), or they can be manually injected into the page.
In your case, you want to do something simple, and only when clicked. This is a perfect job for programmatic injection, so we'll stick with that.
Assuming the solution from the previous section, you are in a context of a background page and already have the current tab as the tab variable:
// background.js
chrome.browserAction.onClicked.addListener(function(tab) {
// Do something with tab
});
Programmatic injection is done with chrome.tabs.executeScript method. At a minimum, you need to specify the tab you want to inject to, and the code that will be run:
// background.js
chrome.browserAction.onClicked.addListener(function(tab) {
chrome.tabs.executeScript(tab.id, {
code: "document.getElementById('#specificId').click()"
});
});
That's not all yet though. The extension must have permissions to execute code in an open tab.
You could use "host permissions" that are defined by a match pattern to give access to specific pages, but remember that we are only triggering it when the user clicks the extension.
For that specific case, there's a special permission "activeTab". It is sufficient to do a lot of things with the currently active tab when the extension is explicitly invoked, and clicking its button is explicit enough.
So, add to manifest:
"permissions": ["activeTab"],
And that should be all you need for this to work.
Extra credit:
While not necessary for this simple purpose, you may want more complicated code than a single line to be executed in the tab. Then it makes sense to use a separate file and invoke executeScript with "file" instead of "code".
Just triggering a click on a button does not require you to directly interact with JavaScript running in the page itself, as DOM events are shared. However, it's important to understand that normally, content scripts can't interact with the page's own scripts, which is called "isolated world". There are ways to bypass it if you really need it, but it's an advanced topic better explained elsewhere.
Sometimes you need the content script to persist, answer some commands and maybe send its own queries to the extension's pages. In that case, it's probably better to auto-inject through the manifest and use Messaging instead of executeScript.
Related
So, I've got a situation where I want a background and content script to be run everytime the browser extension icon is clicked. The ideal behaviour is that extension icon is clicked, the script runs, and the popup will open, displaying the data that was grabbed by the script (but this should happen quickly, the script runs and gets the data very fast). Since chrome.pageAction.onClicked will not work if there is a default_popup defined in manifest.json, I think this leaves me with two options:
Use default_popup and figure out that the extension icon has been clicked some other way. I found this solution in another stack overflow post, but the workaround is to use default_popup: "popup.html", and then the popup.js that is defined in popup.html will send a message saying that the icon has been clicked, then when background.js receives this message, it executes the script. I implemented this idea and it worked... kinda. The trouble is, the popup will always come up before the script is fully executed, so you can't actually display the data grabbed by the script in the popup until the next click. I'm not sure there's any way to get the behaviour I desire using this method, so on to the next:
The other solution I can possible think of is to use onClicked, and then make the popup come up some other way, besides using default_popup in manifest.json. I'm also not sure if this is possible, I have looked on stackoverflow and haven't found anything similar.
Is the second method possible? Can the first method work somehow?
Your option #1 is correct, I think all that is needed is a loading screen when the user first sees the popup, and add some code that updates the popup as soon as it hears from the backend. Might need to see some code to better help there.
Option #2 will not really work, unless you opened the popup in a new tab (or just made it a whole new HTML page). I say this because there is a note here from the Chrome Dev team they will not support opening a popup unless it is from a user gesture -- https://stackoverflow.com/a/10484764/4875295.
If you wanted to go that route it would probably look something like:
Delete from your manifest.json browser_action.default_popup
In your background script add something like:
chrome.browserAction.onClicked.addListener(() => {
const data = dataMaker();
chrome.tabs.create({
url: `${chrome.runtime.getURL("app.html")}?data=${data}`
});
});
Then in your html file have some JS that reads the query string and updates the page accordingly.
Though, that's a different approach than you asked for and I think your original route may still be the best bet (with some added JS around loading).
Below doesn’t work:
window.open(‘javascript:{do something here}’)
Some security error and asking for unsafe-inline keywordenter code here
I need to open a new window and navigate to a url and find a button and click it.
All i have are urls(hundreds of em) I’m looping and using promises for each url. The problem is the script doesn’t work because the page is reloaded as the link is clicked. Therefore it needs to be opened in new tab then I can run the script (of clicking button to download) as the link is opened.
var lk=[
{
"key": "www.someurl.com",
"value": "somefile"
},
{
"key": "someurl",
"value": "somefilename"
}];
p=Promise.resolve();
for(i=0;i<lk.length;i++){
p=p.then(new Promise(_=>{
var link = document.createElement("a");
link.download = lk[i].value;
link.href = lk[i].key;
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
delete link;
setTimeout(()=>{
_();
},30000);
}));
console.log('Completed '+i);
}
Above script stops working as page is reloaded on link.click() in chrome console
I tried puppeteer, it has download issues. Any suggestions are appreciated.
Thanks
For security reasons, browsers isolate JavaScript code running on different origins (an origin is the combination of protocol, domain and port). While you can open a page on a different origin (by redirecting, opening a new window or adding a frame on the current page), you can't directly interact with it. So it's impossible for code on one page to open a different page and click on the button on that page.
The only way to work around this in a browser is to write a browser extension or user script. Browser extensions and user scripts are both higher privileged and able to interact with pages that are not under their control, but they need to be installed in the browser, and access to sites needs to be approved by the user (usually during installation).
I am creating another answer because you changed the question.
Don't create a link and then click on it, it will surely break the script due to reloading. instead, use ajax or https://www.npmjs.com/package/fetch. these will call the given URL in the background. but then you will not be able to click the button.
So I would suggest, you create an iframe with the URL, and then maybe try clicking the button.
If this would be possible, it would be a huge security issue. You could use it to lure somebody to your page, then open their online bank in new tab and make a transaction for example.
If you have control over the page you are opening in the new tab then you could pass some query parameter and listen to that parameter in the new page and invoke some javascript if this parameter is set.
If you want to run a script that will trigger a button on a new tab, and that tab is under your control, then you can achieve this by supplying your tab with the following script:
window.onload = function() {
document.getElementById('my-button').click();
}
I would suggest using Jquery Ready Function because it will be triggered once everything on the page is loaded.
Maybe open the URL into an Iframe and then you can control its content. Something like this: Control iframe content with javascript/html
Then you can not only click the button, but you have complete access to its DOM. but make sure X-Frame-Options is not set for that website. otherwise, this option will not work.
https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Frame-Options
and if that doesn't work:
Then you may want to try:
https://github.com/puppeteer/puppeteer
https://devexpress.github.io/testcafe/
https://www.cypress.io/
These tools are used for automated testing, but you can use these for your purpose.
These tools work like a human click on any button. They themselves open a browser, click on the button and let you know, or download the item or anything.
I want to make a left-click menu. How can I make it on my Chrome extension?
This is my right-click menu:
chrome.runtime.onInstalled.addListener(function() {
chrome.contextMenus.create({
title: 'Dolar',
id: 'dolar',
contexts: ['all'],
});
});
chrome.contextMenus.onClicked.addListener(function(info, tab) {
if (info.menuItemId === "dolar") {
alert("Hello");
}
});
For making it left-click, how do I change it?
There is no special Chrome extension API to create a menu that opens on left-click. To make a menu that opens on left-click, you will need to inject a content script in any page in which you desire to have it used. In that content script you will need to listen for the click event with the left button. You will then need to insert into the DOM the elements that make up your menu. You will need to do all of this manually, or find a library which will abstract the problem.
In general, this will be inconsistent with the user interface which is used on most machines. This will be counter to most user's expectations and may cause user confusion. In addition, web pages will expect to be able to use left-click for whatever purpose they desire, if any, beyond the normal clicks on links (e.g. activating drop-down/pop-out menus, etc.). Your use of it will conflict with using left-click for any interaction within the page. Unless you have a specific use case, having a left-click menu that is generally available is probably a bad idea.
Having a UI element which opens a menu when the user left-clicks on it is a different situation. In such case, the user is clicking on a UI element which is expected to do something. However, you should consider carefully if loading a UI into every page is appropriate. Sometimes it is. Doing so can place a heavy burden on pages where your add-on is completely unused. You can minimize this impact by loading just the minimal amount into the page to show the beginning of the UI. Then, loading the rest of your code/libraries/UI only when the user begins interacting with your extension's UI. Needing a UI in the page is much more likely to be the case when your extension is for a limited set of pages (i.e. modifying the UI on a particular domain/page). However, for most extensions, general interaction with your add-on will begin with a browser/page action button click, a hotkey, a context menu entry, or your add-on detecting something and opening a UI for the user (i.e. not through adding a full UI to every page).
I'm currently developing a chrome extension, that contains background.js and content_scripts.js. In content_scripts, I've added some "event.preventDefault"
in all elements and also added some elements on the page. But when user clicks my extension for the second time, I'd like to "close" my extension and cancel all changes my extensions have done.
Is there a easy way to do that or do I have to treat all changes that I've made individually?
No, without reloading the page, there is no easy way to generically undo all your changes. Yes, if you want all your changes undone, then you have to undo them yourself. There is no magic recording being made of your changes which will back them out for you if your extension is disabled, or you call a particular API.
There are some obvious strategies for doing so:
Remember the changes you made and undo them one by one (undo changes, remove event listeners, etc). What is required depends on what your extension did and the state you desire to leave it in. You can easily send a message to your content script with chrome.tabs.sendMessage() which indicates that it should disable itself and/or back out any changes.
Reload the page
Store the initial state of at least portions of the page and restore it. This may difficult to do, and could be impossible on generic pages. You would have to know all of the event listeners and potentially other state information which has been set by JavaScript in the page.
Try this.
if(localStorage.getItem('firstClick')==='true'){
localStorage.setItem('firstClick', 'false');
chrome.tabs.executeScript(null, {file: "contentscript.js"});
}
else {
localStorage.removeItem('firstClick');
localStorage.set('firstClick', 'true')
}
I was looking at the chrome.commands API for adding some functionality to m y extension.
Basically what I want to do is to listen for specific key combinations, say CTRLALTS, to display the default popup of my extension.
I understood how that could be included in the manifest. Reading further, I found this:
In your background page, you can bind a handler to each of the
commands defined in the manifest (except for '_execute_browser_action'
and '_execute_page_action') via onCommand.addListener.
What I understand is that I need to have a background page that listens for these key combinations and takes actions appropriately. All good.
and there is this paragraph, too:
The '_execute_browser_action' and '_execute_page_action' commands are
reserved for the action of opening your extension's popups. They won't
normally generate events that you can handle. If you need to take
action based on your popup opening, consider listening for an
'onDomReady' event inside your popup's code.
The difficulty that remains is :
How do I make the default popup visible? :)
Basically, that remark says "you don't need to (and can't) handle these actions yourself".
They will open the popup regardless, and listeners don't get fired.
So, for a minimal example a background page is not needed at all.
In the manifest:
"commands": {
"_execute_browser_action": {
"suggested_key": {
"default": "Ctrl+Alt+S",
}
}
}
And then Ctrl+Alt+S will mimic clicking your browser action.
If a popup is set, it will open it.
If not, chrome.browserAction.onClicked will fire.
The chrome.commands.onCommand even will not fire in either case.