Using CSS media queries inside a JavaScript file - javascript

I am working on an already existing webpage and want to resize some elements using CSS media queries like width but i only have access to the script file, is there a way to do this without injecting CSS in my js file?
As of now i've tried injecting my css file line by line in my js file

Although less than ideal, it seems that this is possible by creating a MediaQueryList object from window.matchMedia(), and set inline styles by listening to events for changes on it.
Detailed guide on: MDN
Here is a quick example targeting #media (max-width: 640px):
(Open full page and try change window size to see effects)
const body = document.querySelector("body");
const title = document.querySelector("h1");
const media = window.matchMedia("(max-width: 640px)");
body.style.background = media.matches ? "pink" : "lightgreen";
title.innerText = "Open full page and try change window size";
media.addEventListener("change", (event) => {
if (event.matches) {
body.style.background = "pink";
title.innerText = `Matching: ${media.media}`;
}
if (!event.matches) {
body.style.background = "lightgreen";
title.innerText = `Not matching: ${media.media}`;
}
});
<h1></h1>

The best way would be to load a stylesheet that is hosted someplace you control, and then ask JavaScript to load it in the page that you want.
Code would look like:
function loadStylesheet( absPath ) {
const linkElement = document.createElement('link');
linkElement.rel = 'stylesheet';
linkElement.href = absPath;
linkElement.type = "text/css";
const head = document.head || document.querySelector('head');
head.insertAdjacentElement('beforeend', linkElement);
}
Then you would call loadStylesheet() with your stylesheet's URL as the parameter.

Related

Is Facebook blocking my script via some kind of "defense mechanism" (maybe React based)?

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);
}

Print html page with internet explorer

I have to print a table with its style
So I'm using jQuery.copyCSS to clone its CSS,style into another element
(Because I'm testing it on IE 8 and seems like it doesn't preserve CSS after clicking/calling window.print())
https://github.com/moagrius/copycss
I thought to "send" the table to another page and call from there the print.
Thus I need to clone the css into another element (to pass on the second page)
$("#table_report").clone().attr({"id":"table_report_clone", "name":"clone"}).appendTo("body"); // doesn't matter if it appears on the page, I will just pass these one
$('#table_report_clone').copyCSS('#table_report'); // copy CSS from original to clone
var clone = $("#table_report_clone").html();
var w = window.open();
$(w.document.body).html(clone ); //table_report should be the table with its style
w.document.close();
So, passing .html it will parse only the HTML not including the style
How can I do that?
EDIT 1:
Now some icons are being displayed..however the table is really 'raw'
w.document.write(clone); // instead of $(w.document.body).html(clone);
EDIT 2:
Changed adding some tips by #Roljhon
En fact this does work on FF, Chrome, but in IE 8 nope..
(Anyway I'm using JQuery 1.5.1)
dumpCSSText is a function from
https://stackoverflow.com/a/15011387/7370271
since I have to get/join the styles attributes
$("#table_report").inlineStyler();
var table_report = document.getElementById("table_report");
var w = window.open();
var css = "" + dumpCSSText(table_report) //css of your table_content
var style = document.createElement('style');
style.type = "text/css";
style.appendChild(document.createTextNode(css));
w.document.head.appendChild(style);
w.document.body.innerHTML = document.getElementById("table_report").innerHTML
EDIT 3: Now it works
var table_report = $("#table_report").html();
var w = window.open();
w.document.write('<html><head><link rel="stylesheet" href="/i/css/apex.min.css?v=4.2.6.00.03" type="text/css" /><!--[if IE]><link rel="stylesheet" href="/i/css/apex_ie.min.css?v=4.2.6.00.03" type="text/css"/><![endif]--><link rel="stylesheet" href="/i/libraries/jquery-ui/1.8.22/themes/base/jquery-ui.min.css" type="text/css" /></head><body>');
$(w.document.body).html(table_report);
w.document.write('</body></html>');
w.document.close();
w.focus();
w.print();
w.close();
Finally I'm now at 90%..the only problem is CSS in IE8..
Thank you
I tried to create a solution to your problem and this could possibly solve it or give you an idea of what to do to pass your table css.
Option 1
You could create a link to the stylesheet.Create it directly from your javascript. But make sure that the stylesheet content is the exact CSS of the table_content and append this to the head section of the new window and at the same time write your table in the body part of the new window.
var link = document.createElement('link');
link.rel = "stylesheet";
link.href ="table_content_style.css"; // this will have your table_content_clone CSS
w.document.head.appendChild(link);
w.document.body.innerHTML = "<h1>test</h1>"; // your html table markup
Option 2
You could create the stylesheet as well directly from javascript and doing the same thing as above.
var css = "body{background-color: red;}"; //css of your table_content
var style = document.createElement('style');
style.type="text/css";
style.appendChild(document.createTextNode(css));
w.document.head.appendChild(style);
w.document.body.innerHTML = "<h1>test</h1>"; // your html table markup
I hope that helps. See fiddle: https://jsfiddle.net/0vzwzrpq/
The best approach to style elements you'd like to print is to use CSS media queries.
#media print {
body {
background-color: pink;
}
}
There is a very good article here about the subject: https://www.smashingmagazine.com/2011/11/how-to-set-up-a-print-style-sheet/

Modifying nonexistant DOM elements in WKWebView

I'm displaying a webpage in a WKWebView. To hide elements like the header or sidebars, I'm applying JavaScript. The problem is I'm using one script for various pages of the same site, and page elements different for different types of pages. If I do something like this:
let scriptURL = NSBundle.mainBundle().pathForResource("myscript", ofType: "js")
let scriptContent = String(contentsOfFile:scriptURL!, encoding:NSUTF8StringEncoding, error: nil)
let script = WKUserScript(source: scriptContent!, injectionTime: .AtDocumentEnd, forMainFrameOnly: true)
config.userContentController.addUserScript(script)
…
document.getElementById("header").style.display = "none";
for a nonexistent element, it errors out and the rest of the JavaScript doesn't get applied.
You'll have to check to see whether or not the element is valid before proceeding. Instead of putting if statements everywhere, you can just define a function as so:
var setElementDisplayStyle = function(id, style) {
var element = document.getElementById(id);
if(element) element.style.display = style;
}
Usage:
setElementDisplayStyle("header", "none");
Did you want to something like this
var header = document.getElementById("header")
if (header) {
header.style.display = "none";
}

Link an external CSS file to a JavaScript File [duplicate]

Is it possible to import css stylesheets into a html page using Javascript? If so, how can it be done?
P.S the javascript will be hosted on my site, but I want users to be able to put in the <head> tag of their website, and it should be able to import a css file hosted on my server into the current web page. (both the css file and the javascript file will be hosted on my server).
Here's the "old school" way of doing it, which hopefully works across all browsers. In theory, you would use setAttribute unfortunately IE6 doesn't support it consistently.
var cssId = 'myCss'; // you could encode the css path itself to generate id..
if (!document.getElementById(cssId))
{
var head = document.getElementsByTagName('head')[0];
var link = document.createElement('link');
link.id = cssId;
link.rel = 'stylesheet';
link.type = 'text/css';
link.href = 'http://website.example/css/stylesheet.css';
link.media = 'all';
head.appendChild(link);
}
This example checks if the CSS was already added so it adds it only once.
Put that code into a JavaScript file, have the end-user simply include the JavaScript, and make sure the CSS path is absolute so it is loaded from your servers.
VanillaJS
Here is an example that uses plain JavaScript to inject a CSS link into the head element based on the filename portion of the URL:
<script type="text/javascript">
var file = location.pathname.split( "/" ).pop();
var link = document.createElement( "link" );
link.href = file.substr( 0, file.lastIndexOf( "." ) ) + ".css";
link.type = "text/css";
link.rel = "stylesheet";
link.media = "screen,print";
document.getElementsByTagName( "head" )[0].appendChild( link );
</script>
Insert the code just before the closing head tag and the CSS will be loaded before the page is rendered. Using an external JavaScript (.js) file will cause a Flash of unstyled content (FOUC) to appear.
If you use jquery:
$('head').append('<link rel="stylesheet" type="text/css" href="style.css">');
I guess something like this script would do:
<script type="text/javascript" src="/js/styles.js"></script>
This JS file contains the following statement:
if (!document.getElementById) document.write('<link rel="stylesheet" type="text/css" href="/css/versions4.css">');
The address of the javascript and css would need to be absolute if they are to refer to your site.
Many CSS import techniques are discussed in this "Say no to CSS hacks with branching techniques" article.
But the "Using JavaScript to dynamically add Portlet CSS stylesheets" article mentions also the CreateStyleSheet possibility (proprietary method for IE):
<script type="text/javascript">
//<![CDATA[
if(document.createStyleSheet) {
document.createStyleSheet('http://server/stylesheet.css');
}
else {
var styles = "#import url(' http://server/stylesheet.css ');";
var newSS=document.createElement('link');
newSS.rel='stylesheet';
newSS.href='data:text/css,'+escape(styles);
document.getElementsByTagName("head")[0].appendChild(newSS);
}
//]]>
Element.insertAdjacentHTML has very good browser support, and can add a stylesheet in one line.
document.getElementsByTagName("head")[0].insertAdjacentHTML(
"beforeend",
"<link rel=\"stylesheet\" href=\"path/to/style.css\" />");
If you want to know (or wait) until the style itself has loaded this works:
// this will work in IE 10, 11 and Safari/Chrome/Firefox/Edge
// add ES6 poly-fill for the Promise, if needed (or rewrite to use a callback)
let fetchStyle = function(url) {
return new Promise((resolve, reject) => {
let link = document.createElement('link');
link.type = 'text/css';
link.rel = 'stylesheet';
link.onload = () => resolve();
link.onerror = () => reject();
link.href = url;
let headScript = document.querySelector('script');
headScript.parentNode.insertBefore(link, headScript);
});
};
Usage:
fetchStyle(url)
.then(
() => console.log("style loaded succesfully"),
() => console.error("style could not be loaded"),
);
Use this code:
var element = document.createElement("link");
element.setAttribute("rel", "stylesheet");
element.setAttribute("type", "text/css");
element.setAttribute("href", "external.css");
document.getElementsByTagName("head")[0].appendChild(element);
In a modern browser you can use promise like this. Create a loader function with a promise in it:
function LoadCSS( cssURL ) {
// 'cssURL' is the stylesheet's URL, i.e. /css/styles.css
return new Promise( function( resolve, reject ) {
var link = document.createElement( 'link' );
link.rel = 'stylesheet';
link.href = cssURL;
document.head.appendChild( link );
link.onload = function() {
resolve();
console.log( 'CSS has loaded!' );
};
} );
}
Then obviously you want something done after the CSS has loaded. You can call the function that needs to run after CSS has loaded like this:
LoadCSS( 'css/styles.css' ).then( function() {
console.log( 'Another function is triggered after CSS had been loaded.' );
return DoAfterCSSHasLoaded();
} );
Useful links if you want to understand in-depth how it works:
Official docs on promises
Useful guide to promises
A great intro video on promises
I know this is a pretty old thread but here comes my 5 cents.
There is another way to do this depending on what your needs are.
I have a case where i want a css file to be active only a while. Like css switching. Activate the css and then after another event deativate it.
Instead of loading the css dynamically and then removing it you can add a Class/an id in front of all elements in the new css and then just switch that class/id of the base node of your css (like body tag).
You would with this solution have more css files initially loaded but you have a more dynamic way of switching css layouts.
Have you ever heard of Promises? They work on all modern browsers and are relatively simple to use. Have a look at this simple method to inject css to the html head:
function loadStyle(src) {
return new Promise(function (resolve, reject) {
let link = document.createElement('link');
link.href = src;
link.rel = 'stylesheet';
link.onload = () => resolve(link);
link.onerror = () => reject(new Error(`Style load error for ${src}`));
document.head.append(link);
});
}
You can implement it as follows:
window.onload = function () {
loadStyle("https://fonts.googleapis.com/css2?family=Raleway&display=swap")
.then(() => loadStyle("css/style.css"))
.then(() => loadStyle("css/icomoon.css"))
.then(() => {
alert('All styles are loaded!');
}).catch(err => alert(err));
}
It's really cool, right? This is a way to decide the priority of the styles using Promises.
To see a multi-style loading implementation see: https://stackoverflow.com/a/63936671/13720928
Here's a one line example, that uses plain JavaScript to inject a CSS link into the head element based on the filename portion of the URL:
document.head.innerHTML += '<link rel="stylesheet" href="css/style.css">';
Most browsers support it. See the browser compatibility.
There is a general jquery plugin that loads css and JS files synch and asych on demand.
It also keeps track off what is already been loaded :)
see: http://code.google.com/p/rloader/
Here's a way with jQuery's element creation method (my preference) and with callback onLoad:
var css = $("<link>", {
"rel" : "stylesheet",
"type" : "text/css",
"href" : "style.css"
})[0];
css.onload = function(){
console.log("CSS IN IFRAME LOADED");
};
document
.getElementsByTagName("head")[0]
.appendChild(css);
Below a full code using for loading JS and/or CSS
function loadScript(directory, files){
var head = document.getElementsByTagName("head")[0]
var done = false
var extension = '.js'
for (var file of files){
var path = directory + file + extension
var script = document.createElement("script")
script.src = path
script.type = "text/javascript"
script.onload = script.onreadystatechange = function() {
if ( !done && (!this.readyState ||
this.readyState == "loaded" || this.readyState == "complete") ) {
done = true
script.onload = script.onreadystatechange = null // cleans up a little memory:
head.removeChild(script) // to avoid douple loading
}
};
head.appendChild(script)
done = false
}
}
function loadStyle(directory, files){
var head = document.getElementsByTagName("head")[0]
var extension = '.css'
for (var file of files){
var path = directory + file + extension
var link = document.createElement("link")
link.href = path
link.type = "text/css"
link.rel = "stylesheet"
head.appendChild(link)
}
}
(() => loadScript('libraries/', ['listen','functions', 'speak', 'commands', 'wsBrowser', 'main'])) ();
(() => loadScript('scripts/', ['index'])) ();
(() => loadStyle('styles/', ['index'])) ();
var elem = document.createElement('link');
elem.rel = ' stylesheet'
elem.href= 'style.css';//Link of the css file
document.head.appendChild(elem);
This function uses memorization. And could be called many times with no conflicts of loading and running the same stylesheet twice. Also it's not resolving sooner than the stylesheet is actually loaded.
const loadStyle = function () {
let cache = {};
return function (src) {
return cache[src] || (cache[src] = new Promise((resolve, reject) => {
let s = document.createElement('link');
s.rel = 'stylesheet';
s.href = src;
s.onload = resolve;
s.onerror = reject;
document.head.append(s);
}));
}
}();
Please notice the parentheses () after the function expression.
Parallel loading of stylesheets:
Promise.all([
loadStyle('/style1.css'),
loadStyle('/style2.css'),
// ...
]).then(() => {
// do something
})
You can use the same method for dynamic loading scripts.
I'd like to share one more way to load not only css but all the assets (js, css, images) and handle onload event for the bunch of files. It's async-assets-loader. See the example below:
<script src="https://unpkg.com/async-assets-loader"></script>
<script>
var jsfile = "https://code.jquery.com/jquery-3.4.1.min.js";
var cssfile = "https://cdnjs.cloudflare.com/ajax/libs/materialize/1.0.0/css/materialize.min.css";
var imgfile = "https://logos.keycdn.com/keycdn-logo-black.png";
var assetsLoader = new asyncAssetsLoader();
assetsLoader.load([
{uri: jsfile, type: "script"},
{uri: cssfile, type: "style"},
{uri: imgfile, type: "img"}
], function () {
console.log("Assets are loaded");
console.log("Img width: " + assetsLoader.getLoadedTags()[imgfile].width);
});
</script>
According to the async-assets-loader docs
Answer from future.
In 2022, we have import assertions api for import css file.
import mycss from "./style/mycss.css" assert { type: "css" };
document.adoptedStyleSheets = [sheet];
shadowRoot.adoptedStyleSheets = [sheet];
Browser support:
till september 2022, only chromium based browsers supported.
Read more at:
v8 import assertions post
tc39 github
t39 import assertions proposal
var fileref = document.createElement("link")
fileref.setAttribute("rel", "stylesheet")
fileref.setAttribute("type", "text/css")
fileref.setAttribute("th:href", "#{/filepath}")
fileref.setAttribute("href", "/filepath")
I'm using thymeleaf and this is work fine. Thanks
use:
document.getElementById("of head/body tag")
.innerHTML += '<link rel="stylesheet" type="text/css" href="style.css">';

Open new window with css and pictures

I am doing a simple print option that when click I call a print function. The function copies over the relevant (not all of it) html.
function print() {
var printWindow = window.open("", "Print", "status=no, toolbar=no, scrollbars=yes", "false" );
var toInsert = $("div.book").html();
$(printWindow.document.body).html(toInsert);
}
The problem I have is that this new window doesn't seem to be able to reference my css stylesheet or my pictures that are within the folder. Any ideas? Just focusing on the css issue, would it be possible to insert a <link ... /> into the head of the new window?
Thanks!
function Print() {
var printWindow = window.open("", "Print", "status=no, toolbar=no, scrollbars=yes", "false" );
$("link, style, script").each(function() {
$(printWindow.document.head).append($(this).clone())
});
var toInsert = $("div.book").html();
$(printWindow.document.body).append(toInsert);​
}
DEMO
It's a totally new window. It has to have its own CSS etc.
When you write a document into it, you have to write in the <link> tags, <script> tags, and everything else like that.
To dynamically insert a link to an existing CSS stylesheet into the head of the new window this worked well for me:
var cssNode = document.createElement('link');
cssNode.type = 'text/css';
cssNode.rel = 'stylesheet';
cssNode.href = 'http://www.somedomain.com/styles/FireFox.css';
cssNode.media = 'screen';
cssNode.title = 'dynamicLoadedSheet';
printWindow.document.getElementsByTagName("head")[0].appendChild(cssNode);
Source: Totally Pwn CSS with Javascript - Has some other interesting tricks around direct manipulation of a stylesheet

Categories