To simplify the scenario, lets say I'm working on an extension that just contains: alert("Hello") whenever you load a page in example.com. Relevant manifest.json part:
"content_scripts":
[
{
"matches": ["*://*.example.com/*"],
"js": ["script.js"]
}
]
When I first visit the website, it works fine. But the problem is some of the links in the website don't reload the page, they just manipulate the DOM. So a link for example will take you to example.com/foo, the script doesn't run. Even when I return to the home page, it doesn't run again, and all the edits that were made the first time are removed.
How do I make the add-on recognize that the page has changed, and rerun the script?
After spending hours on this, I was finally able to achieve what I want, though not in the way I expected it would be. This is my solution:
document.addEventListener("click", function(){
editStuff();
});
This works just fine for the website I'm making the add-on for. There is some wasted computational power, as some clicks don't really require the function to work again, but its minimal in my use case.
Related
I'm new to making chrome (or even browser) extensions am trying to make one that applies some custom CSS rules to certain page elements. It seems to work generally as expected, with a few slight annoyances.
Namely, when I apply any CSS style rules using JS, there seems to be a delay between the page fully rendering in the browser to when my extension's CSS rules are applied.
One way I've found to overcome this is to add the CSS file I want to be applied instantly to the manifest file, under the content_scripts, like so:
"content_scripts": [
{
"run_at": "document_start",
"all_frames": true,
"matches": ["<all_urls>"],
"js": ["filter.js"],
"css": ["filter.css"]
}
],
But the issue now is that I want to check if the user has pressed the 'enable' button on the pop up for my extension before applying this. To do this, in the filter.js and background scripts, I check the chrome storage etc. to see if the user has the enabled flag set to true.
I then use the chrome.tabs.insertCSS method to insert my CSS file(s).
In the case where the user has pressed disable on the extension, the browser still renders the page with the effects of filter.css until it runs the JS to remove the effects. By the time this happens, the user has already seen the effects of filter.css which I don't want.
What I want is the browser to instantly apply or not apply my styles (depending on if the user has enabled/disabled) before the page is displayed to the user.
The methods of injecting the CSS thus far have all led to a delay. It must be possible to add it in or remove the CSS without a delay, as I've used extensions such as Dark Reader which seem to be able to apply their styles immediately without ever showing the browser content without their CSS.
Ideally, there would be a way to do conditional checking in the manifest, but I know this isn't possible. What else can I do?
Any help would be appreciated!
Thanks for reading!
Finally managed to fix my issue. The problem wasn't with any of the functions to insert the CSS, it was just that I was running my code to inject the CSS in the window.onload function. :facepalm:
I am struggling to access the DOM of the web page for my Chrome Extension.
In one extension I made, my extension parses the DOM from the content.js file without issue. This happens as the page loads. The user does not need to interact/open the extension at all, it just needs to be running in the backgorund.
Now I'm trying to trigger this from a button. This means the user will click the extension icon in the browser, and the popup.html will show some HTML (including the button).
This is where the problem lies for me. When I now try access the DOM (via click event of the button), it shows the popup.html's DOM, not the web page (The active tab).
So, a quick look through the docs (which I'm open to admit I struggle with) show that it could be a permissions issue. In my manifest.json file, I added
"permissions": [
"activeTab"
],
This didn't help :(
So in this new extension, I'm not using the background.js nor content.js .. I guess this is the problem, as the javascript I'm calling is embeded in the HTML pop up! This makes sense to me (as to the behaviour I'm getting).
How do I access the DOM of the active tab from the HTML pop up
The only way of accessing a page's DOM is by using a content script. Since you've set the activeTab permission you can use chrome.tabs.executeScript to inject a content script into the active tab by omitting the first parameter (the tabId).
Here is an example:
chrome.tabs.executeScript({ file: "content.js" });
I have had this same issue, I click on the button to open an popup, then how do I access the contents of the popup. Yes, you will need to use content scripts, but a trick I done to accomplish this, was when the popup is open, use window.name and get the name of that window. Then you can reference that popup window by var test = window.name('', 'name of that window'). Then you can reference the dom elements of the popup from test. Worked for me, let me know if I need to include some code to better explain.
First of all, I am a newbie in chrome extension development. I am developing a chrome extension to collect user inputs from a form on a specific webpage.
In my manifest.json, I have added javascripts to be used for this purpose inside "content_scripts" and they are invoked when the matched URL loads. In my javsacript, I have added eventlisteners for triggering functions when a button is clicked.
The problem I am facing is that, evenlisteners work fine and the required functions get executed when the button is clicked for first time.
But when the button is clicked for second time, it does not trigger the listener.
Can anyone please recommend me a way to overcome this issue?
Content script part of my manifest.json:
"content_scripts": [
{
"matches": ["*://domain/pathtofile*"],
"all_frames": true,
"js": ["myscript.js"],
"run_at": "document_end"
}
]
And in "myscript.js", I use this:
document.getElementById("elementid").addEventListener("click", functiontocall);
Thank you
Sujith
------ Edit ------
Extension I am developing is for a specific client and this is an unlisted extension in chrome store. So I am not able to provide the URL due to NDA with client.
The URL (example: "http://www.domain.com/urlpath/form.php") has a DIV inside it, which contains an HTML form. So when this URL is loaded, it matches with the manifest and loads a javascript "myscript.js". In "myscript.js", I have written a code:
document.getElementById("formbuttonelement").addEventListener("click", getFormData);
The function "getFormData()" just contains basic javascripts to take values from each form element and prepare a query string and send to another PHP script ("collectForm.php") using AJAX.
After the form button is clicked, the DIV element will close. In the web page, there is a button to open this DIV (HTML form) again, and user can input data in form and click "formbuttonelement" again to submit form.
So, my problem is that, when I open the URL "http://www.domain.com/urlpath/form.php" for the first time, everything works as expected, including the addEventListener part and I can get the form data in "collectForm.php". And, as mentioned earlier, the DIV element will close.
But when the DIV is opened again (which does not load the URL again), addEventListener is not working. I presume that it is because the URL is not reloading, thereby not invoking "myscript.js" again.
I am looking for an option to restart event listener even if the web page is not reloaded.
I hope I have explained the situation.
Thanks & Regards
Sujith
I've had a good look, but I can't seem to find and answer to this question (well, one that works for me anyway).
I've made a Chrome extension that should run the code that's in my content script on click of the icon only, but it always runs as soon as the page loads. Is there a way to prevent this from happening? None of the possible strings I can enter for run_at really cater for this.
Here is example code in both scripts:
Content Script:
function runIt() {
console.log('working');
}
runIt();
background.js:
chrome.browserAction.onClicked.addListener(function(activeTab) {
chrome.tabs.executeScript(null, {file: "content.js"});
});
It will log 'working' as soon as the page loads, and for each button click after that. Is there a way to stop it running as soon as the page loads?
Thanks in advance for all contributions.
The browserAction.onClicked code in your background page does exactly what you want. If you want to stop content.js from running as content script on page load, simply don't include it as a content script in your manifest.
Specifically, in your manifest.json file, you have some lines that look something like this:
"content_scripts": [
{
"matches": ["*://*/*"],
"js": ["content.js"]
}
],
Simply remove those lines, and the script will stop running on page load, while your click listener code will continue working.
I am writing a very simple Google Chrome extension, but have faced such a multitude of issues that it's a bit overwhelming for a project of such a miserable scale.
The extension is a very simple one: upon arrival on any page (say, google.com) the page contents is hidden and the user is faced with a question (s)he has to answer correctly... or be redirected to some other page that provides the correct answer. In other words, the user cannot access pages on the Internet unless (s)he answers the questions correctly.
To hide the page contents I decided to go for a simple overlay using the following approaches:
Approach #1
I tried appending to the current document's body a simple opaque div with position: fixed;, z-index: 2147483647; and width/height at 100%. That worked, but:
The page's CSS kept interfering with the elements inside of my div.
Flash content occasionally appeared on top of it (at least on Windows XP).
Chasing embeds all over the page and setting wmode to
"transparent" didn't help, offsetting to -10000px or setting
display:none; simply alleviated but didn't solve the problem. See also this question.
Approach #2
I tried sandboxing the GUI in an iframe that is created and injected into the page to behave exactly as the div in the above approach. It perfectly solves the issues of the first approach, but:
Apparently there's no way to access the contents of the iframe because of cross-origin policy. And that access – I need it to assign handlers to the input field where the user is typing the answer, I need to remember who's stealing the focus from my answer input field to give it back once the question is answered, etc. etc. etc.
Using Message Passing didn't work for me and I'm not even sure if I should make it work because messaging makes the entire thing overly complex and prohibits me from using the application as a simple webpage (i.e. not as an extension). Why even bother?
So... where am I wrong with my approaches? Is there a third or a fourth one that I'm not aware of?
I appreciate but don't really need code as an answer. A hint or a push to the right direction would be just as good.
P.S. I suppose that at some point somebody will ask if I have code to share. I do, but there's a bunch of it. Which part specifically would you like to see?
Approach #2
Concern #1
Apparently there's no way to access the contents of the iframe because
of cross-origin policy. And that access – I need it to assign handlers
to the input field where the user is typing the answer, I need to
remember who's stealing the focus from my answer input field to give
it back once the question is answered, etc. etc. etc
Yes you access contents of iframe(s) for matter all the code of a Web Page, no CSP etc.
Content script injecting an iframe.
I suggest this is best approach , you can inject script to dynamic generated iframes as shown here and fetch content
Sample Implementation
manifest.json
{
"name": "Iframe",
"description": "",
"version": "1",
"manifest_version": 2,
"content_scripts": [
{
"matches": [
"<all_urls>"
],
"js": [
"myscript.js"
],
"run_at": "document_end"
},
{
"matches": [
"<all_urls>"
],
"js": [
"anotherscript.js"
],
"all_frames": true
}
],
"permissions": [
"<all_urls>"
]
}
myscript.js
var iframe = document.createElement("iframe");
iframe.setAttribute("src", "https://www.facebook.com/plugins/like.php?href=http://allofrgb.blogspot.in/");
iframe.setAttribute("style", "border:none; width:150px; height:30px");
iframe.setAttribute("scrolling", "no");
iframe.setAttribute("frameborder", "0");
document.body.appendChild(iframe);
anotherscript.js
iframes = document.getElementsByTagName("iframe");
for (iframe in iframes){
console.log(iframes[iframe].src);
}
console.log("In another Script");
If you observe console logged messages you observe messages are logged twice (document log + iframe log + [any number of optional iframes in pages]*)
anotherscript.js which runs during document idle states does execute in dynamic generated iframe, how ever you can re-run content script through chrome.tabs.executeScript() any time.
Concern #2
Using Message Passing didn't work for me and I'm not even sure if I
should make it work because messaging makes the entire thing overly
complex and prohibits me from using the application as a simple
webpage (i.e. not as an extension). Why even bother?
What is you want to achieve?
Totally forgot about this question... In the end I went with approach 2 using message passing to communicate with the iframe and it works pretty fine. Here's a repo of the extension for those who are interested in reading the code: https://github.com/olegskl/invasive-kanji