I am trying to make a chrome extension that changes the css of a website. The css is loaded from pastebin with Ajax. The problem is that the old website is shown before the new css is shown- how can I make it so that only the website with changed css is shown?
Content.js:
var s = document.createElement('style');
s.setAttribute("type", "text/css");
var r;
$('head').ready(function() {
$.get("https://pastebin.com/raw/css-file", function(r) {
s.innerHTML = r;
document.head.appendChild(s);
});
});
manifest.json (relevant):
{
"permissions": [
"https://scratch.mit.edu/*",
"https://pastebin.com/raw/*"
],
"content_scripts": [
{
"matches": [
"https://scratch.mit.edu/*",
"http://scratch.mit.edu/*"
],
"js": ["jquery-2.2.2.min.js", "content.js"]
}
]
}
One option is to prevent the body from being visible until the new style is loaded, like this:
$('body').hide();
var s = document.createElement('style');
s.setAttribute("type", "text/css");
var r;
$('head').ready(function() {
$.get("https://pastebin.com/raw/css-file", function(r) {
s.innerHTML = r;
document.head.appendChild(s);
$('body').show();
});
});
But this has a side problem in that you don't know for sure how long the $.get() call will take, and if it takes a long time to return, the page will just be blank.
So instead of hiding the body, a better solution might be to temporarily replace the body html, something like this...
// Save the original body, so we can put it back later
var originalBody = $('body').html();
// Replace the body with a please wait message (use whatever you want)
var pleaseWaitBody = '<div style="text-align: center; margin-top: 100px;">Loading, please wait...</div>';
$('body').html(pleaseWaitBody);
var s = document.createElement('style');
s.setAttribute("type", "text/css");
var r;
$('head').ready(function() {
$.get("https://pastebin.com/raw/css-file", function(r) {
s.innerHTML = r;
document.head.appendChild(s);
// Now that the new style is applied, put the original body back
$('body').html(originalBody);
});
});
EDIT:
The code above won't work for Chrome extensions. However, there is a nother StackOverflow answer that can help. Please see How to hide everything before page load with Chrome Extension
Related
I tried to run the following script with Tampermonkey to hide all images in Facebook for a more minimal usage experience, yet no image is hidden in any webpage there.
document.querySelectorAll("img").forEach(function(el) {
el.style.display = "none";
});
There are no errors in Tampermonkey.
The script is on at each relevant webpage under facebook.com.
Maybe Facebook's React has a way of filtering such a script? If so, I ask what can be done from my end, if at all.
Update:
Sadly all 3 answers didn't work. Try them (originals) in a script targeting all Facebook webpages if you don't believe me.
Try this
var images = document.getElementsByTagName('img');
for (var n = images.length; n--> 0;) {
var img = images[n];
img.setAttribute("src", "");
}
Your code is correct, but the problem is that the images are constantly being generated so you need to continually set the display.
setInterval(() => {
document.querySelectorAll("img").forEach(el => {
el.style.display = "none"
}
}), 30);
A better way would probably be to inject a global style 1.
function addGlobalStyle(css) {
var head, style;
head = document.getElementsByTagName('head')[0];
if (!head) { return; }
style = document.createElement('style');
style.type = 'text/css';
style.innerHTML = css;
head.appendChild(style);
}
I'm trying to change the attribute of the headline entries in any Google search by a Google Chrome extension.
By headline entries I mean these red-underlined:
Image: http://postimg.org/image/sgsyccbpf/
Looking at the HTML code of a random Google search with the Mozilla Firefox inspector:
Image: http://postimg.org/image/gsywhsmkj/
My idea was to obtain every element by looking for class name "rc". Maybe it's not a good idea, but I think it would work.
In order to develop the Chrome extension, I've written these files:
manifest.json
{
"name": "Test 1",
"version": "1.0.0",
"manifest_version": 2,
"content_scripts": [
{
"matches": [
"https://*/*",
"http://*/*",
"<all_urls>"
],
"js": ["content_scripts.js"],
"run_at": "document_start",
"all_frames": true
}
]
}
content:scripts.js
var doFilter = function() {
var classR = document.getElementsByClassName("rc");
for(var i=0; i < classR.length; i++) {
classR[i].setAttribute("style", "background-color:green");
classR[i].setAttribute("align", "center");
}
}
window.addEventListener("DOMContentLoaded", function() {
doFilter(document.body);
});
Here is a demonstration of how my extension worked in my own html page:
Image: postimg.org/image/bdi02zvfl (This is a link to a image but the system don't allow me to post more than two of them)
However, while searching normally in Google it does not work. Every "headline entry" should be green-backgrounded and centered as in the demonstration.
What am I missing?
Thanks!
Since the Google fetches the results asynchronously, you could use a MutationObserver to catche changes in the DOM and act accordingly. See this answer for a more detailed explanation and sample code.
Below is the code from the above question with a few modifications to achieve what you want. Editing the modifyElem() function, it should be easy to realize just about any modification.
content.js:
console.log("Injected...");
/* MutationObserver configuration data: Listen for "childList"
* mutations in the specified element and its descendants */
var config = {
childList: true,
subtree: true
};
/* Traverse 'rootNode' and its descendants and modify '.rc' elements */
function modifyElems(rootNode) {
var nodes = [].slice.call(rootNode.querySelectorAll('.rc'));
if (rootNode.className === 'rc') {
nodes.push(rootNode);
}
while (nodes.length > 0) {
var st = nodes.shift().style;
st.backgroundColor = 'green';
st.textAlign = 'center';
}
}
/* Observer1: Looks for 'div#search' */
var observer1 = new MutationObserver(function(mutations) {
/* For each MutationRecord in 'mutations'... */
mutations.some(function(mutation) {
/* ...if nodes have beed added... */
if (mutation.addedNodes && (mutation.addedNodes.length > 0)) {
/* ...look for 'div#search' */
var node = mutation.target.querySelector("div#search");
if (node) {
/* 'div#search' found; stop observer 1 and start observer 2 */
observer1.disconnect();
observer2.observe(node, config);
/* Modify any '.rc' elements already in the current node */
modifyElems(node);
return true;
}
}
});
});
/* Observer2: Listens for element insertions */
var observer2 = new MutationObserver(function(mutations) {
mutations.forEach(function(mutation) {
if (mutation.addedNodes) {
[].slice.call(mutation.addedNodes).forEach(function(node) {
if (node.nodeType === Node.ELEMENT_NODE) {
modifyElems(node);
}
});
}
});
});
/* Start observing 'body' for 'div#search' */
observer1.observe(document.body, config);
Your doFilter() function only runs once when the page initially loads, which means that if Google loads in any results using AJAX (as it often does), your code will not affect them.
How about having your extension add a <style> element to the page head with the styles you want?
<style>
.rc { background-color: green; text-align: center; }
</style>
This has the added benefit of not blasting away any style attributes that the target elements might already have.
$(window).load(function() {
doFilter(document.body);;
});
Instead of:
window.addEventListener("DOMContentLoaded", function() {
doFilter(document.body);
});
solved my problem.
I also had to download jquery.js library file and refer it within the manifest here:
"js": ["jquery.js", "content_scripts.js"],
There is a lot of confusion dealing with jQuery functions. As far as I could read, $(window).load() is the one which is executed once the page is fully created.
Reference: jQuery - What are differences between $(document).ready and $(window).load?
Thank you #JLRishe for the response.
Okay, so I've been at this for a while.
In the red box, I want to have some angular functionality (ng-repeats, data binding, etc...) This red box appears when text on any webpage is double clicked. However, I can't seem to find out how to actually get angular wired/hooked up to the text box example in the red popup.
It seems fairly trivial to use angular in a badge popup in chrome, as well as on options pages, etc...but I can't seem to get it working in this instance.
inject.js (which is included as a content script in manifest, below)
var displayPopup = function(event) {
var mydiv = document.createElement('div');
var $div = $('#divid').closest('.sentence');
mydiv.innerHTML = getSelectionText();
mydiv.innerHTML += currentSentence.innerHTML;
//Next line is where I want to apply some angular functionality
mydiv.innerHTML += '<div ng-app="myApp" scroll-to-me><input type="text" ng-model="data.test"><div ng-model="data.test">{{data.test}}</div></div>';
mydiv.id = "popup";
mydiv.style.position = "fixed";
mydiv.style.top = event.clientY + "px";
mydiv.style.left = event.clientX + "px";
mydiv.style.border = "4px solid #d00";
mydiv.style.background = "#fcc";
$("body").append(mydiv);
$.getJSON('http://local.wordly.com:3000/words/definitions/test', function(data) {
console.log(data);
});
}
and my manifest.json content script array looks like:
"content_scripts": [
{
"matches": [
"https://www.google.com/*"
],
"css": [
"src/inject/inject.css"
]
},
{
"matches": [
"http://*/*",
"https://*/*"
],
"js": [
"js/angular/angular.js", "app.js", "js/jquery/jquery.js", "src/inject/inject.js"
]
}
]
and app.js, also included in manifest, for just some skeletal app to get up and running.
var myApp = angular.module("myApp", []);
myApp.factory('Data', function(){
//return {message: "I'm data from a service"};
});
myApp.controller("SecondCtrl", function($scope, $http){
});
You need to bootstrap manually if you’re injecting the markup after the page loads. Angular will only run ng-app if it’s present when the document is loaded. Afterwards, you pass the module name to angular.bootstrap:
angular.bootstrap(mydiv, ['myApp'])
Example.
i looked everywhere trying to find an answer to this question.
i want my extension to either disable all javascript on the page BUT to allow the insertion of a cotent script that will work. (so chrome.contentSettings.javascript is not a valid option for now)
Alternatively i want a way to remove all script tags before any of them fire (which is kinda the same thing)
i tried inserting content scripts to runat:document_start but the dom is not fully there at the time. itried adding a conte t s ript on tabs.onUpdate when state is loading but that is too late and as well as content scripts at document_end (all of which who try to remove script tags) but it is still too late.
in an act of desperation i tried altering the behavior of the getters and setters of element.innerHTML to. remove the tags but that did not work as well
i am trying to avoid sending an xhr request to location.href and parse and re_set the content as that is too intensive.
any ideas?
After seeing your comments I think this might suit your needs. It works by getting the page's source, render it to a DOM, disable all the JS and then put it back into the page. Not exactly what you wanted but should suit your case well...
mainfest.json
{
"name": "Reload and Kill JS - Using a content script",
"version": "1.0",
"permissions": [
"tabs", "<all_urls>" , "storage"
],
"background": {
"scripts": ["background.js"]
},
"content_scripts": [
{
"matches": ["<all_urls>"],
"js": ["injectedCode.js"],
"run_at" : "document_start"
}
],
"minimum_chrome_version" : "20",
"manifest_version" : 2
}
background.js
chrome.storage.local.set({"blockhttp://paez.kodingen.com/":true});
injectedCode.js
reloadAndKillJS = function() {
document.documentElement.innerHTML = 'Reloading Page...';
var xhr = new XMLHttpRequest();
xhr.open('GET', window.location.href, true);
xhr.onerror = function() {
document.documentElement.innerHTML = 'Error getting Page';
}
xhr.onload = function() {
var page = document.implementation.createHTMLDocument("");
page.documentElement.innerHTML = this.responseText;
var newPage = document.importNode(page.documentElement,true);
var nodeList = newPage.querySelectorAll('script');
for (var i = 0; i < nodeList.length; ++i) {
var node = nodeList[i];
if (node.src) {
node.setAttribute('original-src', node.src);
node.removeAttribute('src');
}
node.innerText = '';
}
document.replaceChild(newPage, document.documentElement);
delete page;
// Do your thing here
}
xhr.send();
}
chrome.storage.local.get("block"+window.location.href,function(items)
{
if (items["block"+window.location.href]){
window.stop();
reloadAndKillJS();
}
});
Well, the only way to truly prevent scripts is with contentSettings. So you need to put your code somewhere else, in another domain, since contentSettings rules can be applied for specific URL's.
Put you content script to run at document start.
contentScript.js:
window.stop();
document.all[0].innerHTML = "\
<html>\
<body>\
<iframe src=\"chrome-extension://ID/inject.html?url="+encodeURIComponent(location.href)+"\"></iframe>\
</body>\
</html>";
inject.html:
<html>
<head>
<script>
var frame = document.querySelector('iframe');
frame.src = location.search.replace('?url=', '');
frame.onload = function() {
//Your stuff here
}
</script>
</head>
<body>
<iframe></iframe>
</body>
<html>
Now your code is in a parent frame and in another domain, but it may cause some CORS issues which you can try found some workarounds later.
Give a try, then tell me if there's something to fix.
For instance, if I input:
http://www.google.com/
It would return:
http://www.google.com/images/logos/ps_logo2.png
Using javascript/jquery. These sites would all be external. Thank you!
Since that is a Google Chrome extension, you are not bound to same origin policy.
Basically, you would need content scripts to fetch all the images within a page, and check each image's size within the DOM to know if its larger the last fetched image.
You can use Message Passing, to communicate from the Content Script to the popup/background page.
For example, I will show you how to get the largest image from a page and show it within the popup. We use all the techniques shown above and you will see the largest image within the popup if you activate it. (should show I believe :))
manifest.json (snippet)
...
"permissions": [
"tabs",
"http://*/*"
],
"content_scripts": [
{
"matches": ["http://*/*"],
"js": ["images.js"],
"run_at": "document_start",
"all_frames": true
}
]
...
popup.html
<!DOCTYPE html>
<html>
<head>
<script>
function getImage() {
chrome.tabs.getSelected(null, function(tab) {
chrome.tabs.sendRequest(tab.id, {method: "getImage"}, function (response) {
var text = document.getElementById('image');
var image = response.data;
text.src = image ? response.data : 'no_image.gif';
});
});
}
</script>
</head>
<body>
<button onclick="getImage(); ">Get Largest Image</button>
<img src="no_image.gif" id="image"/>
</body>
</html>
images.js (content script)
function getMaxImage() {
var maxDimension = 0;
var maxImage = null;
// Iterate through all the images.
var imgElements = document.getElementsByTagName('img');
for (var index in imgElements) {
var img = imgElements[index];
var currDimension = img.width * img.height;
if (currDimension > maxDimension){
maxDimension = currDimension
maxImage = img;
}
}
// Check if an image has been found.
if (maxImage)
return maxImage.src;
else
return null;
}
// Listen for extension requests.
chrome.extension.onRequest.addListener(function(request, sender, sendResponse) {
if (request.method == "getImage") {
sendResponse({data: getMaxImage()});
} else {
sendResponse({}); // snub them.
}
});
This is more web-scraping than JavaScript/jQuery.
However, given an assumption that you've received the HTML, and that it is available somehow in a JavaScript string, then something like the following might suffice for finding the maximum dimension image:
var sHTML = getHTMLSomehow(sURL);
var nMaxDim = 0;
var $pageDOM = $(sHTML);
var $objMaxDimImage;
$pageDOM.("img").each(function(){
var $this = $(this);
var nDim = parseFloat($this.width()) * parseFloat($(this).height());
if (nDim > nMaxDim){
$objMaxDimImage = $this;
nMaxDim = nDim
}
});
alert("Max dim is:" nMaxDim);
alert("Image Source:" $objMaxDimImage.attr("src"));
due to the same origin policy you cant access an external site with javascript. maybe you can write a server-side script that downloads the page (for example using wget), search for img-tags in the html code and load all found images to check the size.