Javascript: Removing media query from external css file - javascript

How could I remove media query loaded from external css file (<link rel="stylesheet" type="text/css" href="XXXX.css" media="screen">)? Note that I cant disable the entire link tag because other important styles are included out of that media query:
body{...}
.container{...}
...
#media(min-width:XXXpx) {
....
}
...
Thank you!

I strongly recommend pure CSS solution for this problem, such as defining a new stylesheet overwritting rules you don't want.
#selector {
color: #000;
}
.some-classs-you-want-to-reserve {/*..*/}
#media only screen and (min-width: 600px) {
/* unwanted */
#selector {
display: none;
}
}
For example, if you want to mute rules inside #media only screen and (min-width: 600px), simply add a new stylesheet overwritting them to default values:
#media only screen and (min-width: 600px) {
/* unwanted */
#selector {
display: block;
}
}
If you insist on using Javascript, you can access and modify css rules by iterating document.styleSheets.
https://developer.mozilla.org/en-US/docs/Web/API/StyleSheetList
https://developer.mozilla.org/en-US/docs/Web/API/document.styleSheets
This is an instance of StyleSheetList, an array-like object. Each item of it represents an external or inline css stylesheet. All rules including media queries can be found inside it.
A brief tree structure:
- document.styleSheets
|- CSSStyleSheet
|- cssRules
|- media
So here's an example showing how to remove all rules defined inside #media only screen and (min-width: 600px) block:
function removeRule() {
if(typeof window.CSSMediaRule !== "function")
return false; //Your browser doesn't support media query feature
var s = document.styleSheets, r,
i, j, k;
if(!s) return false; //no style sheets found
// walk throuth css sheets
for(i=0; i<s.length; i++) {
// get all rules
r = s[i].cssRules;
if(!r) continue;
for(j=0; j<r.length; j++) {
//If there's a rule for media query
if(r[j] instanceof CSSMediaRule &&
r[j].media.mediaText == "only screen and (min-width: 600px)") {
for(k=0; k<r[j].cssRules.length; k++) {
// remove all rules of it
r[j].deleteRule(r[j].cssRules[k]);
}
return true;
}
}
}
}
removeRule();
Or live fiddle: http://jsfiddle.net/e4h41qm2/

The fact is, if you delete one media query, another one get's activated. So you have to do a loop till no media query is found:
function removeRule() {
if (typeof window.CSSMediaRule !== 'function') {
return false;
}
var styleSheets = document.styleSheets;
var number = 0;
if (!styleSheets) {
return false;
}
for (i = 0; i < styleSheets.length; i++) {
var styleSheet = styleSheets[i];
var rules = styleSheet.cssRules;
if (!rules) {
continue;
}
for (var j = 0; j < rules.length; j++) {
var cssText = rules[j].cssText;
if (cssText.indexOf('#media') === 0) {
number++;
styleSheet.deleteRule(j);
}
}
}
if (number) {
return number;
}
return 0;
}
function removeMediaQueries() {
var num = removeRule();
var total = num;
while (num) {
num = removeRule();
total += num;
}
if (total) {
console.info(total + ' media quer' + (total == 1 ? 'y' : 'ies' + ' removed'));
} else {
console.info('No media queries removed');
}
}
removeMediaQueries();
If you put all this in one line you can generate a bookmark in your browser and have a fast way to test the design without media queries. Very useful for newsletter.
javascript:!function(){function e(){if("function"!=typeof window.CSSMediaRule)return!1;var e=document.styleSheets,n=0;if(!e)return!1;for(i=0;i<e.length;i++){var o=e[i],r=o.cssRules;if(r)for(var t=0;t<r.length;t++){var f=r[t].cssText;0===f.indexOf("#media")&&(n++,o.deleteRule(t))}}return n?n:0}function n(){for(var i=e(),n=i;i;)i=e(),n+=i;n?console.info(n+" media quer"+(1==n?"y":"ies removed")):console.info("No media queries removed")}n()}();

You're probably better off overriding it instead of trying to delete it, but if you must:
style = document.styleSheets[0]; // or whatever stylesheet you want
[].forEach.call(style.cssRules || [], function(rule, i) {
if (!rule.cssText.indexOf('#media(min-width:XXXpx') {
style.deleteRule(i);
}
});
Untested.

Related

How to get all image urls from a web page using javascript?

There are couple of ways to load image src urls using javascript, like using document.images or by selecting all img tags and get srcs.
However I can't figure out a way to image urls used within css.
For example, if the webpage has following css code, it loads bg.png, but I can't get that url using methods I mentioned above.
.bg {
background-image: url('bg.png');
}
Anyone has an idea how to get all these urls used within css?
The Resource Timing API collects data on outbound requests, should leave capacity to collect images in both CSS and inline styles performantly.
Haven't tested this, but something akin to this should help you get started:
if ( !('performance' in window) ||
!('getEntriesByType' in window.performance) ||
!(window.performance.getEntriesByType('resource') instanceof Array)
) {
alert('unsupported');
} else {
window.addEventListener('load', function() {
var resources = window.performance.getEntriesByType('resource');
for(var index in resources) {
for(var properties in resources[index]) {
console.log(properties);
console.log(resources[index][properties]);
}
}
});
something like this:
Loop all stylesheet rules
grab the document element from the stylesheet
find the background Image
var sSheetList = document.styleSheets;
for (var sSheet = 0; sSheet < sSheetList.length; sSheet++)
{
var ruleList = document.styleSheets[sSheet].cssRules;
for (var rule = 0; rule < ruleList.length; rule ++)
{
if (rule.style.cssText.match(/background/)) {
var selectorText = ruleList[rule].selectorText );
var img = document.getElementsByClassName(selectorText);
var style = img.currentStyle || window.getComputedStyle(img, false);
if( style.backgroundImage ) {
var bg = style.backgroundImage.slice(4, -1).replace(/"/g, "");
//add to array here or whatever.
}
}
}
}

JavaScript if statement alternative syntax

I want to check the size of the screen and toggle a class depending on the size of the screen.
Html
<div id="item" class="test"></div>
Script
window.addEventListener("load", toggleClass);
window.addEventListener("resize", toggleClass);
function toggleClass() {
var w = window.innerWidth;
item = document.getElementById("item");
if ( w > 700 ) {
item.classList.remove("test");
}else {
if ( item.classList.contains("test")) {
}else {
item.classList.add("test");
}
}
}
You don't need to test for whether test is included in the classList first - you can just add it unconditionally. Also, avoid implicitly creating global variables - always declare a new variable name with var (or, preferably, const, or let):
function toggleClass() {
var w = window.innerWidth;
var item = document.getElementById("item");
if ( w > 700 ) {
item.classList.remove("test");
}else {
item.classList.add("test");
}
}
You can also use the Conditional (ternary) operator
function toggleClass() {
var item = document.getElementById("item");
(window.innerWidth > 700) ? item.classList.remove("test") : item.classList.add("test");
}
A different approach - rather than adding a class on the smaller size - use a media query to apply the styling that you want - obviously if you are using the class for a purpose other than styling - this approach may not work - but if all you are doing is styling an element based on the width of the screen - then a media query is your friend.
The benefit of this approach (if its purely styling changes you are doing) is that there is no js required - the browser will automatically use whatever styling the media query matches. This is better for performance because the re is no js to run.
#media screen and (max-width: 699px) {
p {
font-size: 14px
}
}
#media screen and (min-width: 700px) {
p {
font-size: 16px
}
}

Get CSS style sheet property using Javascript (and media query)

Generally, when I'm not using JQuery, I use the following utility function to get properties from a stylesheet using Javascript. This works nicely to get values from CSS style sheets:
<!DOCTYPE html>
<html>
<head>
<style>
.foo {
color: red;
}
</style>
<script type = "text/javascript">
function getcss(selector, property)
{
var len = document.styleSheets.length;
for (var idx = 0; idx < len; ++idx)
{
var sheet = document.styleSheets && document.styleSheets[idx];
if (sheet)
{
var r = sheet.rules ? sheet.rules : sheet.cssRules;
if (r)
{
var i = r.length;
while (i--)
{
if (r[i].selectorText && r[i].selectorText.toLowerCase() === selector.toLowerCase())
{
return (r[i].style[property]);
}
}
}
}
}
return null;
}
function exec()
{
alert(getcss(".foo", "color"));
}
</script>
</head>
<body onload = "exec()">
</body>
</html>
The problem is that it doesn't take into account media queries. If I replace the <style> section of the above code with:
.foo {
color: red;
}
#media screen and (max-width: 600px) {
.foo {
color: green;
}
}
...it still outputs red if I shrink the window to smaller than 600px.
Is this correct WC3 behavior? Or is this a browser bug? (I'm testing with Firefox 12 on Ubuntu).
Is there anyway to correct this, so that Javascript "sees" the correct style sheet as required by the media query?
Your code is simply looking through the rules of a stylesheet, and only the first list of rules at that. The rules are a object representation of the stylesheet -- they don't change just because you've resized your browser.
In your code you're never going to see the green rul because you're only iterating over the items in the first CSSRuleList. The trick here is you need to recursively loop over any additional rules that are CSSMediaRule and contain their own CSSRuleList. For you, the sheet.rules contains both a single CSSSyleRule (which is your .foo { color:red; }) and a CSSMediaRule (which is your media query). That second rule then its own CSSRuleList, which you can traverse to find your green color.
In this case, here's where your data lies:
// Assuming sheet 0 is your stylesheet above
var sheet = document.styleSheets[0];
// First rule is ".foo { color: red; }"
console.log(sheet.cssRules[0].cssText);
// Second Rule is your "#media" and its first rule is ".foo { color: green; }"
console.log(sheet.cssRules[1].cssRules[0].cssText);

Accessing CSS media query rules via JavaScript/DOM

I've been using a number of libraries (including my own) to dynamically load assets based upon media queries I've outlined in CSS files. For example:
In CSS:
#media screen and (max-width: 480px) {
.foo {
display: none;
}
}
And using an asset loader; require.js, modernizr.js etc or using window.matchMedia and associated addListener() functions:
if (function("screen and (max-width: 480px)")){
// Load several files
load(['mobile.js','mobile.css']);
}
Declaring them twice is awkward/silly and as far as I can find, all JS helper libraries and asset loaders require you to repeat the media queries rather than locating them programmatically from JS/DOM.
So, I've been exploring the ability to access the values programmatically via document.stylesheets, but I'm not sure if they're accessible and there seems very little documentation to suggest they are.
The furthest I've got is looking for CSSMediaRule and using console.dir(document.stylesheets) amongst others to explore the stylesheet object.
But no references are made (within document.stylesheets) to the actual media query rules used in CSS - only the classes to be applied as a result of the media queries... What I'm trying to locate, programmatically, is:
"screen and (max-width: 480px)"
Is there any way of accessing such CSS Media query rules via JavaScript/DOM?
For get rules use cross browser variant:
var styleSheet = document.styleSheets[0];
var rules = styleSheet.cssRules || styleSheet.rules; // IE <= 8 use "rules" property
For detect CSSMediaRule object in rules list use (not work in IE <= 8, because "CSSMediaRule" class available only in IE >= 9):
var i = 0;
if (rules[i].type == 4)
{
// Do something
}
Some functions for get styles from current DOM (not works in IE <= 8):
function getCssRulesFromDocumentStyleSheets(media)
{
var resultCssRules = '';
for (var i = 0; i < document.styleSheets.length; i++)
{
var styleSheet = document.styleSheets[i];
if (isRuleFromMedia(styleSheet, media))
resultCssRules += getCssRulesFromRuleList(styleSheet.cssRules || styleSheet.rules, media);
}
return resultCssRules;
}
function getCssRulesFromRuleList(rules, media)
{
var resultCssRules = '';
for (var i = 0; i < rules.length; i++)
{
var rule = rules[i];
if (rule.type == 1) // CSSStyleRule
{
resultCssRules += rule.cssText + "\r\n";
}
else if (rule.type == 3) // CSSImportRule
{
if (isRuleFromMedia(rule, media))
resultCssRules += getCssRulesFromRuleList(rule.styleSheet.cssRules || rule.styleSheet.rules, media);
}
else if (rule.type == 4) // CSSMediaRule
{
if (isRuleFromMedia(rule, media))
resultCssRules += getCssRulesFromRuleList(rule.cssRules || rule.rules, media);
}
}
return resultCssRules;
}
function isRuleFromMedia(ruleOrStyleSheet, media)
{
while (ruleOrStyleSheet)
{
var mediaList = ruleOrStyleSheet.media;
if (mediaList)
{
if (!isMediaListContainsValue(mediaList, media) && !isMediaListContainsValue(mediaList, 'all') && mediaList.length > 0)
return false;
}
ruleOrStyleSheet = ruleOrStyleSheet.ownerRule || ruleOrStyleSheet.parentRule || ruleOrStyleSheet.parentStyleSheet;
}
return true;
}
function isMediaListContainsValue(mediaList, media)
{
media = String(media).toLowerCase();
for (var i = 0; i < mediaList.length; i++)
{
// Access to mediaList by "[index]" notation now work in IE (tested in versions 7, 8, 9)
if (String(mediaList.item(i)).toLowerCase() == media)
return true;
}
return false;
}
Functions usage example:
<style type="text/css">
#media screen and (max-width: 480px) {
p { margin: 10px; }
}
#media screen and (max-width: 500px) {
p { margin: 15px; }
}
#media print {
p { margin: 20px; }
}
</style>
<!-- ... -->
<script type="text/javascript">
alert(getCssRulesFromDocumentStyleSheets('print'));
alert(getCssRulesFromDocumentStyleSheets('screen and (max-width: 480px)'));
// For IE (no space after colon), you can add fix to "isMediaListContainsValue" function
alert(getCssRulesFromDocumentStyleSheets('screen and (max-width:480px)'));
</script>
Here is a JS Fiddle for it: https://jsfiddle.net/luisperezphd/hyentcqc/
This is how I do it:
In css create classes to expose or hide content at various breakpoints.
This is a handy utility anyway. These are already available in Twitter Bootstrap for example.
<style type="text/css">
.visible-sm, .visible-md, .visible-lg{
display:none;
}
#media (max-width: 480px) {
.visible-sm{
display: block;
}
}
#media (min-width: 481px) and (max-width: 960px) {
.visible-md{
display: block;
}
}
#media (min-width: 961px) {
.visible-lg{
display: block;
}
}
</style>
In all your documents add empty spans with these classes.
They won't show up on the page if you keep the spans inline.
<span id="media_test">
<span class="visible-sm"></span>
<span class="visible-md"></span>
<span class="visible-lg"></span>
</span>
Add this short jquery extension to your script file.
This sets a new class in the body tag that matches the current media query.
(function ($) {
$.fn.media_size = function () {
//the default port size
var size = 'lg';
//the sizes used in the css
var sizes = ['sm','md','lg'];
//loop over to find which is not hidden
for (var i = sizes.length - 1; i >= 0; i--) {
if($('#media_test .visible-'+sizes[i]).css("display").indexOf('none') == -1){
size = sizes[i];
break;
};
};
//add a new class to the body tag
$('body').removeClass(sizes.join(' ')).addClass(size);
}
}(jQuery));
$(document).media_size();
Now you have an automatic integration with your css media queries Modernizr style.
You can write javascript (jquery) that is conditional on based on your media queries:
how big is this viewport?
<script type="text/javascript">
$('.sm a').click(function(e){ alert('Media queries say I\'m a small viewport');});
$('.lg a').click(function(e){ alert('Media queries say I\'m a large viewport');});
</script>

controlling css with javascript works with Mozilla & Chrome however not with IE

Im having problems with this function applying css(using a text variable) working with Internet Explorer but it works in Firefox & Chrome.
the code:
/*! addCssStyle() applies the text value $CssText$ to the the specified document
$Doc$ e.g. an IFrame; or if none specified, default to the current document,
*/function addCssStyle(CssText, Doc){
//Secure $Head$ for the current $Doc$
Doc = Doc||document; var head = Doc.getElementsByTagName('head')[0];
if(!head || head == null){
head = Doc.createElement('div'); Doc.body.appendChild(head);
} if(!head || head == null){return false;}
//createElement('style')
var PendingStyle = Doc.createElement('style');
// if (is_gecko){PendingStyle.href = 'FireFox.css';}//???needeed???
PendingStyle.type = 'text/css';
PendingStyle.rel = 'stylesheet';
// PendingStyle.media = 'screen';//???needeed???
PendingStyle.innerHTML = CssText;
head.appendChild(PendingStyle);
}/*___________________________________________________________________________*/
the use of the function:
var NewSyleText = //The page styling
"h1, h2, h3, h4, h5 {font-family: 'Verdana','Helvetica',sans-serif; font-style: normal; font-weight:normal;}" +
"body, b {background: #fbfbfb; font-style: normal; font-family: 'Cochin','GaramondNo8','Garamond','Big Caslon','Georgia','Times',serif;font-size: 11pt;}" +
"p { margin: 0pt; text-indent:2.5em; margin-top: 0.3em; }" +
"a { text-decoration: none; color: Navy; background: none;}" +
"a:visited { color: #500050;}" +
"a:active { color: #faa700;}" +
"a:hover { text-decoration: underline;}";
addCssStyle(NewSyleText);//inserts the page styling
var style = document.createElement('style');
Adding new stylesheets and scripts by creating elements using DOM methods is something that has always been dicey cross-browser. This won't work in IE or WebKit.
style.rel = 'stylesheet';
style.href = 'FireFox.css';
There's no such properties on an HTMLStyleElement. <style> contains inline code. For external stylesheets, use a <link>. By luck, it happens this does work:
var link= document.createElement('link');
link.rel= 'stylesheet';
link.href= 'something.css';
head.appendChild(link);
But doesn't give you a convenient way to insert rules from script.
You can also add new rules to an existing stylesheet (eg. an empty style in the <head>) by using the document.styleSheets interface. Unfortunately, IE's interface doesn't quite match the standard here so you need code branching:
var style= document.styleSheets[0];
if ('insertRule' in style)
style.insertRule('p { margin: 0; }', 0);
else if ('addRule' in style)
style.addRule('p', 'margin: 0', 0);
I know this is a old thread but i was looking for a solution to insert CSS styles dynamicly that works with all common/major browsers. I want to share my solution with you. The solution of david doesn't work well (sorry). I have made a tooltip javascript/jQuery class that can work with inline styles for example but can also work with CSS styled styles. (offtopic: Also the class can auto align tooltips like the default tooltips).
Maybe you wonder what the benefits are when you insert CSS like the example above. Well, you don't need an extra CSS file with the styles and you can dynamicly add styles from script and when you working with images you can dynamicly change the path to the images if you want (so you don't need to change any file). Also you can insert the styles ABOVE other stylesheets/style rules and the aplied style rules can be modified in the other stylesheets below (this is not possible when you use inline styles or when placing the inserted rules BELOW any existing stylesheet).
This function works with Opera/Firefox/IE7/IE8/IE9/Chrome/Safari (without any hack applied!):
function addHtmlStyles( sCss, oBefore )
{
var oHead = document.getElementsByTagName('head')[0];
if( !oHead || oHead == null )
{ return false; }
var bResult = false,
oStyle = document.createElement('style');
oStyle.type = 'text/css';
oStyle.rel = 'stylesheet';
oStyle.media = 'screen';
if( typeof oStyle.styleSheet == 'object' )
{ // IE route (and opera)
try{ oStyle.styleSheet.cssText = sCss; bResult = true; }
catch(e) {}
}
else {
// Mozilla route
try{ oStyle.innerHTML = sCss; bResult = true; }
catch(e) {};
if( !bResult )
{
// Webkit route
try{ oStyle.innerText = sCss; bResult = true; }
catch(e) {};
}
}
if( bResult )
{
try
{
if( typeof oBefore == 'object' )
{ oHead.insertBefore(oStyle, oBefore ); }
else { oHead.appendChild(oStyle); }
}
catch(e) { bResult = false; }
}
return bResult;
}
It returns true when ok or false when fail. For example:
var sCss = '#tooltip {background:"#FF0000";}';
// get first stylesheet with jQuery, we don't use $('head') because it not always working
// If there is no stylesheet available, it will be added at the end of the head tag.
var oHead = $(document.getElementsByTagName('head')[0]),
oFirst = oHead.find('[rel=stylesheet]').get(0);
if( addHtmlStyles( sCss, oFirst ))
{ alert( 'OK' ); }
else { alert( 'NOT OK' ); }
That's all. Hope you like the solution.
Greetz,
Erwin Haantjes
This has been tested to work on all major browsers (Chrome/Safari/FF/Opera/IE) including IE6,7+8:
function createCSS(css, doc) {
doc = doc || document;
var style = doc.createElement("style");
style.type = "text/css";
if (!window.opera && 'styleSheet' in style && 'cssText' in style.styleSheet) {
// Internet Explorer 6-8 don't support adding text nodes to
// styles, so use the proprietary `styleSheet.cssText` instead
style.styleSheet.cssText = css;
}
else {
// Otherwise use the standard method
style.appendChild(doc.createTextNode(css));
}
// Note the `|| document.body` as getting the
// head doesn't always work on e.g. Safari 1.0
var head = doc.getElementsByTagName("head")[0] || doc.body;
// Add the new style of higher priority than the last
// ones if there's already other elements in the head
if (head.firstChild) {
head.insertBefore(style, head.firstChild);
}
else {
head.appendChild(style);
}
}
As that code is written, it is relative to the document being served so may need to be modified to make it relative to another path, or you could use absolute image paths in the CSS.
EDIT: Removed all the innerHTML references in favour of using the more standard createTextNode when possible and cleaned various things up.
#GlassGost: Weird is not working for you because i test it in several browsers (also on the mobile ones). Maybe it helps to add css when the DOM is ready:
$(document).ready( function()
{
.......
});
Also sometimes it helps to change the order of loading scripts.
Greetz,
Erwin Haantjes
This works fine for me on all current browsers with any type of document (which I assume you must be dealing with multiple documents, or otherwise you wouldn't have a Doc parameter):
function add_css_style(css_rules, document) {
document = document || self.document;
var style = document.createElementNS("http://www.w3.org/1999/xhtml", "style");
style.type = "text/css";
style.appendChild(document.createTextNode(css_rules));
document.documentElement.appendChild(style);
}
If you want to use the CSSOM instead:
function add_css_style(css_rules, document) {
document = document || self.document;
var stylesheet = document.documentElement.appendChild(
this.createElementNS("http://www.w3.org/1999/xhtml", "style")
).sheet;
stylesheet.insertRule("#media all{" + rules + "}", 0);
}

Categories