CSS: specificity not behaving for js injected stylesheets? - javascript

As part of a mobile-first build, i am loading the 'desktop' css dynamically in a blocking fashion
<script type="text/javascript">
var mq = window.matchMedia("(min-width: 640px)");
if(mq.matches){
var stylesheet = document.createElement('link');
stylesheet.href = '<?php echo $src ?>';
stylesheet.rel = 'stylesheet';
stylesheet.type = 'text/css';
document.getElementsByTagName('head')[0].appendChild(stylesheet);
}
</script>
However, webkit and ff give more power to the css loaded pre-injection.
Even if SetTimeout 3 seconds before loading mboxes.css, the browsers still favour the css rules that were not injected.
How can i get the css specificity rules to behave?

The problem is the ordering of the CSS that is being loaded, it is loading the values of event_core as the master so to override this you have 2 easy options without rewriting the js.
You can execute the Javascript before the event_core.css or after depending what you have at the moment.
Or the easiest way but depends realistically how much CSS you have in mboxes.css would to use !important within the CSS which will tell the browsers these are most important and become the new master.
For example
.mob p {
color:white!important;
}
Or make the edit the rules so that both are the same, for example .content > .mbox p

Ok, worked this out.
For the CSS to behave "normally" it needs to be injected into the <body> not the <head>.
document.getElementsByTagName('body')[0].appendChild(stylesheet);

The problem has nothing to do with the way the css files is loaded and more to do with the rules of css precedence. Basically, the more specific the selector, the higher priority the rule has. In your case both selectors are the same priority (class and element selector), so some browsers will choose one, other the other the last loaded should override the first, but it's not something I would count on.
The fix is to make the rules in mboxes.css more specific like .mbox.main_content p.
A second option is to add !important after each style like (like color: white !important), but I would recommend against that.

Related

Use jQuery to get css properties of a class/id that that doesn't exist in the page

As it said in the title. I want this javascript...
$("#mrNotAppearing").css("background-color");
to return "red" based on this css...
#mrNotAppearing {
background-color: red;
}
given that there are no elements in the document that actually have the id mrNotAppearing
I'm using media query checks with jQuery to get window widths as seen here and I thought it might be nice to use some "dummy" css that definitely won't get in the way of anything.
I'm also open to other suggestions that achieve the same result.
Plan B, I'll just go with actual css or add some dummy property to body?
Updating for clarity:
It can be difficult to sync javascript that requires particular window widths with media query widths in the css, which can cause layout problems.
Instead, you can query the status of the css itself. As so:
body {
background-color: blue;
}
#media (min-width: 42em) {
body {
background-color: red;
}
}
Then, in the javascript:
if($(body).css("background-color")==="red"){
// we know down to the pixel that it's safe to trigger the javascript
// because the media query went off.
}
All I'm trying to do is add a dummy entry in the css that will be used solely for triggering the javascript. I could use an existing property--and may have to--but I'd like to make it explicit what I'm doing. Or I'm at least toying with the idea.
I apologize for the confusion. I was going for brevity.
P.S. the whole point of the question is to use a style that will 100% not be appearing in the document. And will never change, even if the layout does.
EDIT: Ha, okay, final answer. em does indeed return as px. So...
I'm going to answer my own question because I'm pretty sure it isn't making sense to anyone. Also, I don't know if this is a good idea, but it seems to work for my purposes. So, my solution:
Style the <style> tag. It's in the DOM, it's not structural, and jQuery can get css properties from it. Like so...
style {
width: 672px;
}
and then...
$("style").css("width");
will return 672px
I'm probably over-thinking this. And still probably not making sense. And I have no idea if this works on any browser but Chrome or if it's a terrible idea for some reason, but I think it's kind of appealing, semantically.
Any other thoughts?
You have access to all css rules through document.styleSheets, there is no need to apply to an element.
https://developer.mozilla.org/en-US/docs/Web/API/CSSStyleSheet
Here is another answer on how to get the style based on a class name:
How do you read CSS rule values with JavaScript?
EDIT
Although, it would be a lot easier to render the element off canvas for a brief moment:
var $dummy = $('<div>').addClass('class1 class2 class3').css({position: fixed, left: 100%}).appendTo('body');
// collect all info you need here;
$dummy.remove();

How to hide CSS background images that use !important

I'm trying to create a bookmarklet that will hide images on any page I'm currently viewing. It works for removing <img> tags and it creates a CSS style that tries to hide all background images. The issue I keep encountering are background images that are specified with !important. I can't figure out how to hide them.
Here is a codepen demonstrating the issue: http://codepen.io/Chevex/pen/kbDcv
If you remove the !important from the background image then it all works fine. Any ideas?
Make sure your CSS occurs after that CSS on the page and put the !important override on your CSS. Also, since you specify that you are using JavaScript, you can add your CSS as inline CSS on the actual element and use !important. The only thing that overrides inline important is user agent user important style sheets.[reference][example]
As others have pointed out, you can use the newer (IE9+) querySelectorAll function:
function hideImages() {
var d = document,s = window.getComputedStyle;
Array.prototype.forEach.call(
d.querySelectorAll("body *"),
function(el){
if (s(el).backgroundImage.match(/url/) ||
el.tagName === 'IMG'){
el.style.visibility="hidden";
}
}
);
}
$('button').click(hideImages);
http://codepen.io/anon/pen/rBnIx
Updated to include background-images set via CSS.
Though, you could probably lose a lot by turning anything with a background-image invisible. You might have better luck just turning that property off. You could either check the computed style of each element like above or just set them all like below. The function below uses setProperty to override !important which is also IE9+.
function hideImages() {
var a=document.querySelectorAll("body *"),i=a.length;
function _f(n){
if (n.tagName === 'IMG') n.style.visibility="hidden";
n.style.setProperty("background-image", "none", "important");
};
while(--i) _f(a[i]);
}
http://codepen.io/anon/pen/tnrdH
you can hide divs with backgrounds in the same manner as you do img tags in the linked code:
var imgs=document.querySelectorAll("div[style*='background']");
for (var i=0;i<imgs.length;i++) {
imgs[i].style.visibility="hidden";
}

How to change .css priority with javascript

I need to change priority of loaded .css files using javascript. I found that i can do this just by replacing position of < link rel="stylesheet".... >tag in the head element. This solution works fine in all browsers except IE. I am using the following code for this:
var firstCss = styleSheets[0].ownerNode;
var defaultCss = styleSheets[1].ownerNode;
firstCss.parentNode.insertBefore(defaultCss, firstCss);
So is there a better solution for such issue, or maybe somebody know how to make this code work under IE.
I solve the problem maybe not in the best way but it works. I wrote the following code:
if (document.createStyleSheet) {
document.createStyleSheet(defaultCss.href,0);
defaultCss.cssText = "";
} else {
firstCss.ownerNode.parentNode.insertBefore(defaultCss.ownerNode, firstCss.ownerNode);
}
I use "defaultCss.cssText = "";" to cleanup old CSS file, cause you can't get ownerNode of styleSheet under IE7-8(but you can do it in IE9) and delete old style sheet. So if you even load your CSS file to first position you will still have the same on the last position and it will be prioritized. I check it works under IE 7 - 9 and other borwsers, also stylesheet doesn't loaded from server again IE take it from cache.
hope it will be useful for someone.
The order in which you load your CSS files has very little influence in how styles are applied. What styles are applied to a certain element is determined by the specificity of the selectors used in the CSS rule. A higher specificity overrules a lower specificity, even if the style with the lower specificity is declared later.
The specificity can be seen as a combination of four digits in the form (a,b,c,d) where a takes precedences over b and b over c and c over d. So (0,0,0,2) has a higher specificity than (0,0,0,1) and (0,0,1,0) has a higher specificity than (0,0,0,2).
The order of style declaration (i.e. the order in which style sheets are loaded) is only important if selectors are used with exactly the same specificity.
For Example:
A: h1
B: #content h1
C: <div id="content">
<h1 style="color: #fff">Headline</h1>
</div>
The specificity of A is 0,0,0,1 (one element), the specificity of B is 0,1,0,1 (one ID reference point and one element), the specificity value of C is 1,0,0,0, since it is an inline styling.
Since
0001 = 1 < 0101 = 101 < 1000,
the third rule has a greater level of specificity, and therefore will be applied. If the third rule didn’t exist, the second rule would have been applied.

Detecting when styles disabled

What's the best way to detect, with JS, if the user has disabled your stylesheets? Is there a reliable way even?
How about asking them?
<div style="display:none">This site relies on CSS, please go to our CSS free version of this site</div>
Something easy would be to check the body background color for instance.
However, how likely is it someone disables CSS and not Javascript? (dunno what you use it for obviously)
I would have a small, empty div sit on the screen. When the page loads, use JS to check the 'display' property of that div. If it's 'none', then your css has successfully been loaded. If not, they may have to turned off / changed your styles.
If you're in control of the stylesheet you can have a "calibration" style.
Have a classname that applies some CSS property to an element. A good cross-browser safe property can be background-color.
When loading your JS try to dinamically create an element and apply the classname to it. Check if the properties match (the one on the element with the one you're expecting).
BoltClock's comment comes close. You can use window.getComputedStyles(calibrationElement, null) but that will fail in older IE browser versions.
See documentation for getComputedStyles
Feel free to remove the "calibration" node after you've checked it.
Assuming your primary external or inline stylesheet is loaded before the script, you can use this:
if (document.styleSheets.length){} // stylesheets are disabled
https://developer.mozilla.org/en-US/docs/DOM/document.styleSheets
It's IE5+ compatible too as per: http://www.jr.pl/www.quirksmode.org/dom/w3c_css.html
The caveat is that, if styles are turned off after the window has loaded (which only causes a browser repaint), the document.styleSheets object won't change on the fly. Additionally, as noted in the comments below, this will not work for Firefox when using the View -> Page Style -> No Style feature, for which styles are still loaded, just not applied to the view.
To detect the initial state across browsers, or changes on window.onresize, it can be done with a style reset on the body, with the following code placed after <body> or in a DOMContentLoaded event:
if (document.body.clientWidth !== document.documentElement.clientWidth) {
// Styles are disabled or not applied
}
This works if you use body { margin: 0; } in your stylesheets (with no particular custom width), because it makes the body element the same width as documentElement (a.k.a. the <hmtl> element) while styles are active.
When styles are turned off or disabled, the body.clientWidth will revert to the browser's default body width, which always has a margin (8px by default in CSS 2.1 major browsers ) and therefore different from documentElement.clientWidth.
Should your site design use a specific margin other than 8px for the body, here is an alternative option:
if (document.body.clientWidth === document.documentElement.clientWidth-16) {
// user styles are disabled or not applied (IE8+ default browser style applies)
}
At least in Safari, part of the difficulty is that with CSS off the elements still report CSS attributes. But if you test on the actual rendering of a property then you can tell. Width is probably the simplest (and most common) property you can test on.
Below is a sample script (it uses jQuery, but could easily be un-jQueryfied) that will test for CSS. We just load an empty div on the page, give it a width of 3px using CSS, and then test that div's width. If the width is not 3 then CSS is disabled. Obviously you have to make sure that this doesn't colide with any other styles you might have that could cause the width to be other than 3. But it gives the general idea.
<html>
<head>
<title>Test</title>
<style type="text/css">
#testCSS {width: 3px;}
</style>
</head>
<body>
<div id="testCSS"></div>
<div id="message"></div>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<script type="text/javascript">
jQuery(document).ready(function(){
if (jQuery("#testCSS").width() != 3) jQuery("#message").html("CSS Disabled");
});
</script>
</body>
</html>
Edit: sorry about the messy code example. It doesn't seem to like my code tags. Here's a JSfiddle with the code. Obviously you won't be able to disable CSS and test there, but you can pull the code from it: http://jsfiddle.net/3FvdL/1/

Quickly repaint array of unicode symbols in JavaScript

I want to change background/foreground color of many symbols with the same CSS class. Right now I'm doing it with jQuery — like $('back_COLORED').css('background-color', '#00FF00'), but this approach is slow when there are many elements with such class (>900).
Seems it's because jQuery don't change CSS rules itself, but finds all elements one-by-one and applies inline styles to them. At least, this is what I see in inspector. So, the question is:
How can I change the CSS rules itself?
Will it be much faster?
Can I make it cross-browser (IE6 doesn't count)?
UPD: I'm trying to make some kind of color scheme editor. The source is at http://github.com/kurokikaze/cinnabar/. Don't mind PHP things, editor is fully client-side (with just some libraries fetched from the net).
UPD2: Tried canvas approach, still slow. Canvas branch is at http://github.com/kurokikaze/cinnabar/tree/canvas.
The most cross-browser friendly way to override a class definition is to write a new rule and add it to the end of the last stylesheet in the document. You can edit an existing style rule, but even some recent browsers can make it difficult.
function newRule(selector, csstext){
var SS= document.styleSheets, S= SS[SS.length-1];
// this example assumes at least one style or link element
if(S.rules){
S.addRule(selector,csstext,S.rules.length);
}
else if(S.cssRules){
S.insertRule(selector+'{'+csstext+'}'),S.cssRules.length)
}
}
newRule('.someclass','background-color:#0f0');
You can add as many 'property:value;' bits in the csstext as you need.
Remember to prefix a '.' to a class name or a '#' to an id,
and the css must be written as a style rule (with-hyphens, not camelCase).
Of course, it will not override inline styles, and it is overkill for small, local changes.
It also may make the redrawing of the page more obvious than changing one element at a time,
but it may be just what you need here.
There are different ways depending on which browser you are dealing with. This is documented on Quirks Mode.
Some libraries provide an abstraction layer, such as YUI's StyleSheet utility.
There should be a significant performance boost since you aren't using JS/DOM to cycle through all the elements.
Another approach would be to predefine your styles:
body.foo .myElements { … }
And then edit document.body.className
If you can select the parent div by id, maybe you could select by tag inside it? Or are there elements of the same kind that should change color and that should not, inside the parent?
It would be nice to have an idea of what you're building here. 900+ objects seems to be a lot... maybe a completely different approach could be used? Canvas, SVG?
Try hiding the items you want to change before changing them, make the change and then display them again. This is common practice to speed up things as you minimize the repaint events in the viewport. In this case when you only setting one css property it might not be that of a benefit but it´s worth a try I say.
Try:
$('back_COLORED').hide();
$('back_COLORED').css('background-color', '#00FF00');
$('back_COLORED').show();
or
$('back_COLORED').hide().css('background-color', '#00FF00').show();
I would stick in trying changing a CSS property, instead of parsing the DOM.It is about the CSS engine vs. DOM+JS here, and the winner is clear.
It happens I just uploaded a tiny library that replaces CSS by Javascript: jstyle
This is may be an overkill, but you will find in the source code of jstyle.js all the code you need to update cross browser the CSS properties of your page.
I think a better solution would be to write a more specific CSS rule (that would override the normal colour) that can be activated by simply changing one element's css class.
So for example if you had the following structural markup:
<div id="container">
<span class="colored">Test 1</span>
<span class="colored">Test 2</span>
</div>
And CSS:-
.colored { background-color: red; }
.newcolor .colored { background-color: blue; }
Then in your jquery you add the .newcolor class to the container div:-
$('#container').addClass('.newcolor');
When you do that the second CSS rule will override the first because it is more specific.
Inject the css code into a style tag:
var style = $('style').attr({
type:"text/css",
media:"screen",
id:'changeStyle'
}).html('.tempClass { color:red } .tempClass p { background:blue }').prependTo('body');
and on every changes on your color with color picker you only rewrite the html inside of #changeStyle tag.
Have no idea if it works (didn't tested) but you should give a try.
This is jQuery pluggin for work with css rules: http://flesler.blogspot.com/2007/11/jqueryrule.html
not sure about its performance, but worth a try.

Categories

Resources