I'm trying to make a chrome extension that will allow the user to choose the background color or font on the page and based on his choice a CSS file will be generated. Then the file would be injected into the page.
I've already tried to write some separate functions, every function for different elements and it's working great but 1) changes are not saved on the page, 2) functions are redundant, 3) I have no idea how to generate a CSS file from this.
This are chunks of code from content script I've already written (every function looks kinda similar):
colorPicker.onchange = function() {
chrome.tabs.query({active: true, currentWindow: true}, function(tabs) {
chrome.tabs.executeScript(
tabs[0].id,
{code: 'document.body.style.backgroundColor = "' + colorPicker.value + '";'}
)
})}
I also know that to inject CSS to page from chrome extension I can use:
chrome.tabs.insertCSS(tabId, {
file : "mystyle.css"
});
I really want to generate a CSS file instead of changing document properties one by one; but... how?
Any help would be greatly appreciated.
Related
After some googling and looking at tutorials, I have code along these lines:
File: background.js:
chrome.tabs.onUpdated.addListener( function (tabId, changeInfo, tab) {
if (changeInfo.status == 'complete' && tab.active) {
chrome.tabs.executeScript(tab.ib, {
file: 'inject.js'
});
}
})
File: inject.js
(function() {
function remove_by_class(className) {
var elem = document.getElementsByClassName(className)[0];
elem.parentNode.removeChild(elem);
}
function remove_by_title(itemTitle) {
var elem = document.querySelector('[title = itemTitle]');
elem.parentNode.removeChild(elem);
}
if (url.includes('SOME_URL_SUBSTRING')){
remove_by_class('CLASS_NAME');
remove_by_title('ITEM_TITLE');
}
})();
This removes certain elements, usually a second or two after I see them load in. And if I understand correctly, it can only remove elements from the page as it 'initially' exists, when it is first loaded - elements created later (by future Javascript actions on the page) are unaffected, because my extension's code is only injected & executed once.
What I'm looking for instead is some sort of 'always-on' extension that proactively watches for the loading of certain elements. Basically, I want to have a function which is called every time an HTML element is loaded/created in the page, and only allow the element to actually be placed if the function returns 'true'.
What is the easiest way to accomplish something like this?
EDIT: as an example, say I wanted to block the YouTube logo (class name style-scope ytd-topbar-logo-renderer from loading). I guess I would like a MWE that stops it from loading.
(For context: I am completely new to both Chrome extensions in particular and Javascript in general, but otherwise somewhat familiar with programming. I am mostly just curious/playing around right now, but there is a vague goal of making a kind of 'productivity tool' for myself, allowing me to use Facebook, Youtube etc for exactly what I need them for, with distracting or extraneous (to me) elements, such as Recommended Videos, redacted.)
In an electron application, I am trying to display an image from the local disc in a window
that opens when a button in the main window is clicked.
The image's path is known at runtime only.
My first approach was to generate a new window that loads a template html-file
when the click event on that button is triggered and then set the
src-attribute of the img-element in this html template,
but I did not succeed in getting a handle on this img-element defined in
"imgView.html" (the following is from the renderer-process):
newWindowBtn.addEventListener('click', function(event){
let win = new BrowserWindow({
webPreferences: {
nodeIntegration: true
},
width: 500, height:500});
win.loadFile("imgView.html");
// how can the img-element defined in the template imgView.html
// be accessed here to set the src-attribute?
win.show();
win.on('close', function(){win=null})
})
I want to be able to have several windows open at the same time, with different images loaded.
My second approach was to dynamically generate the html to be loaded:
var html = [
"<body>",
"<p>Sample image: </p>",
"<img src=\"img.png\" style=\"width:90%\">",
"</body>"
].join("")
and replace the win.loadFile command in the newWindowBtn.addEventListener event-function
from above with
win.loadURL("data:text/html;charset=utf-8," + encodeURIComponent(html),
{baseURLForDataURL: __dirname + "/imgDir/"}
)
but apparently the image is not found (the rest of the page is loaded ok).
I played around with the format of baseURLForDataURL, but nothing worked.
Any ideas?
In some cases, I viewed that data:text/html;charset=utf-8," + encodeURIComponent(html) is not sufficient and you need to encode the html with base64: data:text/html;base64, + btoa(html).
Pay Attention
If this isn't a solution, this means that Electron doesn't allow to you to call the image because the page you'd like to load is considered an external page and you must encode the image with base64, too.
When I launch my page, the css is totally messed up because my js is supposed to dynamically load css on click (mobile or standard website css). Currently, it just loads them both. Here's the code:
function loadjscssfile(filename, filetype)
{
if (filetype=="css")
{
var fileref = document.createElement("link");
fileref.rel= "stylesheet";
fileref.type = "text/css";
fileref.href = filename;
document.getElementsByTagName("head")[0].appendChild(fileref)
}
}
loadjscssfile("HCSS.css", "css")
I have two links on the site. One loads the mobile css, the other loads the standard website css. I have it linked like this:
load hcss
<br/>
load mobile
What you are after is swapping css files, not just loading a new one. In jquery it would probabaly look something like this (code not tested):
function swapCssFiles(fileToLoad, fileToUnload) {
$('head link[href="'+fileToUnload'"]') // select the tag with css to unload
.attr('href', fileToLoad); // swap the href attribute with the file to load
}
This is off course possible with 'pure' javascript, but I'm to much a jQuery addict to tell you how. If you see how easy the syntax is, you can probably tell why.
Your links would look something like this:
load hcss
I hope this is helpfull.
Note however that this is not the way I would approach this. If you want to target mobile devices with specific css, I would use mediaqueries to detect screensize, and not javascript.
I am building a firefox extension and need to insert some elements and css into the doc.
I tried following How can a Firefox extension inject a local css file into a webpage? and Inserting CSS with a Firefox Extension, but had no luck.
I know am missing some silly point but I cant really make out what it is,and would really appreciate if some one can point it out to me.
Heres my chrome.manifest:
content helloworld content/
overlay chrome://browser/content/browser.xul chrome://helloworld/content/overlay.xul
locale helloworld en-US locale/en-US/
skin helloworld classic/1.0 skin/
And my overlay.js:
var fileref = gBrowser.contentDocument.createElement("link");
fileref.setAttribute("rel", "stylesheet");
fileref.setAttribute("type", "text/css");
fileref.setAttribute("href", "resource://helloworld/skin/global.css");
gBrowser.contentDocument.getElementsByTagName("head")[0].appendChild(fileref);
I even tried this inside my overlay.js
var sss = Components.classes["#mozilla.org/content/style-sheet-service;1"]
.getService(Components.interfaces.nsIStyleSheetService);
var ios = Components.classes["#mozilla.org/network/io-service;1"]
.getService(Components.interfaces.nsIIOService);
var uri = ios.newURI(url, null, null);
sss.loadAndRegisterSheet(uri, sss.USER_SHEET);
No luck again.
What am I missing? I seriously can't figure out.
Tried using the console,shows nothing
When I copy and paste my href "chrome://helloworld/skin/global.css", I can see my css file in the browser.
You should set the javascript.options.showInConsole, restart, then check the Error Console.
The nsIStyleSheetService snippet should be the simplest to get working.
What's the url in it? The other snippets/comments you posted contradict each other -- the chrome.manifest and your comment "When I copy and paste my href ..., I can see my css file in the browser" imply you're using chrome://helloworld/skin/global.css, but your other snippet shows you use a resource:// URL, which is a different beast.
How do you determine the snippet is not working? Could you have your stylesheet wrong? Did you try something simple like * {color:red !important;} as your CSS?
P.S. If you use nsIStyleSheetService, please note the comment on MDC about taking care not to register the same sheet multiple times.
Also note that when using nsIStyleSheetService you should be careful not to make your styles apply to the pages you didn't intend to modify. #-moz-document can be very useful for that.
I'm not sure if this will solve your problem, but you should listen for load events in all tabs changing your overlay.js to something like:
window.addEventListener('load', function () {
gBrowser.addEventListener('DOMContentLoaded', function (event) {
if (event.originalTarget.nodeName == '#document' &&
event.originalTarget.defaultView.location.href == gBrowser.currentURI.spec)
{
var document = event.originalTarget;
// Your css injection here
}
}, false),
true);
Forget the overlay.js file and the overlay.xul file, you don't need it. Use the style instruction for chrome.manifest instead, like so:
style chrome://browser/content/browser.xul resource://helloworld/skin/global.css
No js req'd
I need to background load some WAV files for an HTML page using AJAX. I use AJAX to get the details of the WAV files, then use the embed tag, and I can confirm that the files have loaded successfully because when I set autostart to true, the files play. However, I need the files to play only when the user clicks on a button (or an event is fired). The following is my code to preload these files:
function preloadMedia() {
for(var i = 0; i < testQuestions.length; i++) {
var soundEmbed = document.createElement("embed");
soundEmbed.setAttribute("src", "/media/sounds/" + testQuestions[i].mediaFile);
soundEmbed.setAttribute("hidden", true);
soundEmbed.setAttribute("id", testQuestions[i].id);
soundEmbed.setAttribute("autostart", false);
soundEmbed.setAttribute("width", 0);
soundEmbed.setAttribute("height", 0);
soundEmbed.setAttribute("enablejavascript", true);
document.body.appendChild((soundEmbed));
}
}
I use the following code to play the file (based on what sound file that user wants to play)
function soundPlay(which) {
var sounder = document.getElementById(which);
sounder.Play();
}
Something is wrong here, as none of the browsers I have tested on play the files using the code above. There are no errors, and the code just returns.
I would have left it at that (that is - I would have convinced the client to convert all WAV's to MP3 and use MooTools). But I realized that I could play the sound files, which were not dynamically embeded.
Thus, the same soundPlay function would work for a file embeded in the following manner:
<embed src="/media/sounds/hug_sw1.wav" id="sound2" width="0" heigh="0" autostart="false" enablejavascript="true"/>
anywhere within the HTML.
And it plays well in all the browsers.
Anyone have a clue on this? Is this some sort of undocumented security restriction in all the browsers? (Please remember that the files do get preloaded dynamically, as I can confirm by setting the autostart property to true - They all play).
Any help appreciated.
Hmm.. perhaps, you need to wait for the embed object to load its "src" after calling preloadMedia() ?
Are you sure that the media file is loaded when you call soundPlay() ?
i know your question got a bit old by now, but in case you still wonder...
soundEmbed.setAttribute("id", testQuestions[i].id);
you used the same id twice, yet getElementById returns only one element, or false if it doesn't find exactly one matching element.
you could try something like this:
soundEmbed.setAttribute("id", "soundEmbed_"+testQuestions[i].id);
always keep in mind that an id must be unique
Just a tip for more compatibility:
I read here that width and height need to be > 0 for Firefox on MacOS.