Stop inheritance of a specific CSS class - javascript

I have to modify an existing HTML5 app that has two different types of themes.
For example, I have something like this:
<body class="theme1 theme2">
<div id="div1">I'm happy with both themes</div>
<div id="div2">I just want theme 2</div>
</body>
This example is overly simplified. I know that I could just apply theme2 to div2. But the point is that those themes classes are in the body and I cannot "easily" change that.
I naively thought that I could just do something like this in JS:
getElementById('div2').classList.remove('theme1');
But this does not seem to work. I think that this only work on classes directly applied to the element. I would prefer a "pure CSS" solution anyway.
The info that I find seems related to preventing inheritance of specific properties. In my case, I want to prevent inheritance of any property under the theme1 class for div2 and all its children.

Properties are inherited, classes are not.
The only way to stop a property being inherited is to assign a value to that property for the given element.

The code doesn't work because 'theme1' is not a class of div 'div2'.
I don't know of a way to selectively remove the inheritance as you asked. The closest solution I can think of is to use nesting to create a more specific selector and override the undesired styles. Simple example:
.theme2 {
font-style: italic;
}
.theme2 #div2 {
font-style: normal;
}
This would "remove" the italic style from div2. You would have to redefine all the undesired styles. From a maintenance standpoint this may or may not be an acceptable solution. But it does solve the problem.

No, that's not how CSS works. If properties are getting applied to a descendent because of a class on an ancestor, and you cannot change that class or the properties, then you have to override the properties (via a more specific selector) that you don't want inherited.
e.g.
body.theme2 div { background: #000; }
#div2 { background: transparent; }

Let's start with some common.js:
//<![CDATA[
var doc = document, bod = doc.body, IE = parseFloat(navigator.appVersion.split('MSIE')[1]);
bod.className = 'js';
function gteIE(version, className){
if(IE >= version)bod.className = className;
}
function E(e){
return doc.getElementById(e);
}
function removeParentClassName(childId, removeClassName){
var cn = new RegExp('(\s+)?'+removeClassName, 'i'), pn = E(childId).parentNode;
pn.className = pn.className.replace(cn, '');
}
//]]>
Now on your current page:
removeParentClassName('div2', 'theme1');

One way you could do this with pure CSS would be to set up you CSS rules like so
.theme1 .div1,
.theme2 .div1,
.theme1 .div2,
.theme2 .div2 {font-style:normal;} /* apply theme1 and theme2 styles here */
.theme1.theme2 .div2 {font-style:italic;} /* apply theme2 only rules here */

Related

Can I add a classname to a CSS variable?

Is it possible to add a classname to a CSS variable or is there some other way to set it up so that I don't have to manipulate each individual variable directly via javascript? I'd like to keep all my styles in CSS and simply turn on relevant classes with JS. For example, If something like this was possible in CSS:
:root.white{ --bgcol:#FFF; --col:#000; }
:root.black{ --bgcol:#000; --col:#FFF; }
Then I could then just toggle the .black or .white class from javascript to trigger all vars to change. What's the best approach for this type of setup?
That's frankly the best (as in most idiomatic) approach — the use of class names, if not altogether separate stylesheets (as has been tradition for many, many years), to theme entire layouts via custom properties. It's the most "fundamentally CSS" approach with JavaScript merely being the glue that makes the theme switching work. You really can't do much better than that.
For those unaware what :root means and wondering where exactly the class names are being applied, it's the html element (the parent of body). So there is nothing special going on here — you're simply switching class names on the html element. It just happens that global custom properties are conventionally defined for the document root element since it's at the top level of the inheritance chain.
If you have any theme-agnostic custom properties, as well as style properties (i.e. not custom properties) that apply to the root element, keep them in their own unqualified :root rule, separate from your themed custom properties, so they won't be affected by theme switching. Here's an example:
const root = document.documentElement;
// Default theme - should assign declaratively in markup, not JS
// For a classless default theme, move its custom properties to unqualified :root
// Again, keep it separate from the other :root rule that contains non-theme props
// Remember, the cascade is your friend, not the enemy
root.classList.add('white');
document.querySelector('button').addEventListener('click', function() {
root.classList.toggle('white');
root.classList.toggle('black');
});
:root {
--spacing: 1rem;
color: var(--col);
background-color: var(--bgcol);
}
:root.white {
--bgcol: #FFF;
--col: #000;
}
:root.black {
--bgcol: #000;
--col: #FFF;
}
p {
margin: var(--spacing);
border: thin dashed;
padding: var(--spacing);
}
<button>Switch themes</button>
<p>Hello world!
Using :root selector is identical to using html, except its specifity is higher, thus there is no issues in using this approach.
For example:
:root {
--bg: red;
}
:root.blue {
--bg: blue;
}
// ...
div {
background: var(--bg);
}
Later, you should just change html's class and variables will change.
You can see an example in this fiddle.

How to find all css classes and its css attributes inside a certain div?

i would like to find all classes and ids inside a certain div ! and these css attributes!
Example :
<div class="demo">
<div class="new_class">
<p id="para">This is Demo Paragraph</p>
<a style="background:#ccc">HyperLink</a>
</div>
</div>
<style>
.demo{
height:100px; width:100px; background:#FF0;
}
.new_class{height:40px; width:40px; background:#999;}
#para{color:#E1E1E1;}
</style>
Now The question is that: i would like to find all classes and ids which are used inside demo class ! and Their css values too(which style applying now. ).
I would like to find result as below :
<style>
.demo{
height:100px; width:100px; background:#FF0;
}
.new_class{height:40px; width:40px; background:#999;}
#para{color:#E1E1E1;}
a{background:#ccc;}
</style>
OP, not sure what your purpose is, but in general, this can be useful. I had a project where I needed to embed a fancy template from one site onto a page on a different site with a very different, and conflicting stylesheet. I used some code similar to the following to grab every applied style from the original content, via document.styleSheets, then reapplied them all as inline styles, so I could put it onto the "parent" site without the stylesheets conflicting.
Fiddle
JS
var selector,rule;
var result=[];
var sheets = document.styleSheets;
for (var i in sheets) {
//rules or cssRules, depending on the browser
var rules = sheets[i].rules || sheets[i].cssRules;
//iterate over every css rule in the document
for (var r in rules)
{
selector=rules[r].selectorText;
rule=rules[r].cssText;
//select demo itself, as well as all of its children
$('.demo, .demo *').each(function () {
//console.log($(this),selector);
//for each element, see if it matches the current rule. add if it does
if ($(this).is(selector))
{
result.push(rule);
}
});
}
}
console.log(result);
//result[0] .demo { height: 100px; width: 100px; background: none repeat scroll 0% 0% rgb(255, 255, 0); }
//result[1] .new_class { height: 40px; width: 40px; background: none repeat scroll 0% 0% rgb(153, 153, 153); }
//result[2] #para { color: rgb(225, 225, 225); }
Granted, you will have to tweak this on your own to do things like, removing duplicate styles that would occur if you were to apply this to a larger block of HTML, and for dealing with inline styles (which this does not attempt to do, but you can get them from the style attribute and work from there...), and possibly the computed style, which you can get with getComputedStyle, as indicated by the #Derek's answer. but this should get you started.
To find all existing id, try:
var ids = [];
$(".demo *").each(function(){ this.id && ids.push(this.id); });
console.log(ids);
Do the same thing for class or anything else.
However, to get your expected output, you must first acquire the defined CSS style for each element. Which one should be included? p by default gets margins and paddings. Do you include those too? You will also need to dig into all the CSS declarations just to find the style that are applied, which is almost impossible to do.
For example,
<div class="yellow"></div>
<style>
div.yellow:not(.blue){
background: yellow;
}
</style>
How do you get the background of the <div> tag? .style.background? Nah, it returns "". Well now you will have to reach into the CSS declaration with document.styleSheets to see which one applied. How do you even check if the rule div.yellow:not(.blue) matches your element? Good luck doing that. (There might be libraries that does this kind of thing, or maybe you can even utilize jQuery's internal selector engine with .is, though it will not be the same as in CSS) Another thing you can do is try getComputedStyle. It gives you every single computed styles that aren't even in your declaration. So what you are trying to do is not possible to do. (I don't even know what you are doing something like this.)

Inherit all css properties from parent div to child div

My html,
<div id="parent">
<div id="child">
</div>
</div>
css looks like,
#parent{
height:100px;
width:200px;
any-aother-property1:something;
any-aother-property2:something;
any-aother-property3:something;
}
Is there is any way to inherit all the properties to child at once , means can I do like,
$('#child').properties= $('#parent').properties
If you really want to dynamically inherit all CSS properties given to the parent at runtime, you can do the following.
Caution: This overrides the default value for all properties. Generally speaking, it is wise to assume that defaults are correct until proven otherwise. Some properties are inherit by default and some are not for various reasons. So you probably should only override specific properties that need to be changed.
#child {
all: inherit;
}
is this enough?
#parent, #parent > div {
/* your properties */
}
Copy inline:
$('#child').get(0).style = $('#parent').get(0).style;
But better if you find a CSS way as stated in the other answers.
UPDATE:
Get all styles:
$('#child').get(0).style = window.getComputedStyle($('#parent').get(0), null);
As far as I know there is no such a thing but you can always do something like this:
#parent, #child{
height:100px;
width:200px;
any-aother-property1:something;
any-aother-property2:something;
any-aother-property3:something;
}
Add both id's to have the same properties.
I have added a function in my library, custags.js which will help you doing that.
This is the extendcss() which requires the query selectors of the elements or the other methods to function.
You can do this with any type of elements, let it be equals or parent-child.
Here is the working demo:-
const parent = document.querySelector('.parent')
const child = document.querySelector('.child');
Ω('#button').on('click', ()=>{
Ω('document').extendcss(parent, child);
});
.parent{
background-color: teal;
color: yellow;
}
<script src="https://obnoxiousnerd.github.io/custags.js/custags.min.js"></script>
<h1 class = "parent">Parent</h1>
<h1 class = "child">Child</h1>
<button id="button">Give child some css</button>
Since children inherit most of their parent's styles by default, you can focus on clearing the child's styles rather than setting them equal to the parent's.
In Chrome and Opera, you can do so with one line of code:
$('#child')[0].style.all= 'unset';
This works whether child's CSS properties are in a style sheet or are created dynamically.
To clear dynamically-created CSS only, you can do this in all modern browsers:
$('#child')[0].style.cssText= '';
That will restore the style sheet properties.
The cross-browser solution to your problem may be the following:
var cs= getComputedStyle($('#child')[0]);
for (var i=0 ; i<cs.length; i++) {
$('#child').css(cs[i], 'inherit');
}
This iterates through all of child's styles, setting them to be inherited from the parent.
You can test each of these methods in different browsers at this Fiddle:
http://jsfiddle.net/9c3sy2eb/7/

Grouping changes of different element's styles

Is it possible in Javascript to set different element's styles at once, in such way that only one reflow is triggered? For example, is it possible to set at once the color style for different elements as in the below code snippet, in a way that just one reflow is triggered instead of three reflows?
document.getElementById("elem1").style.color = '#000';
document.getElementById("elem2").style.color = '#fff';
document.getElementById("elem3").style.color = '#abc';
I am familiar with techniques (as explained here) that minimize reflows/repaints such as using document fragments or using css classes instead of manipulating css styles through javascript, but I don't see how they can be applied on this case.
EDIT: the three elements on the example are siblings but there might exist, or not, other sibling elements between them, meaning that we cannot assume that they are defined necessarily by that order in the html structure. For example, its possible that we have a structure like this:
<div id="parent">
<div id="elem1">elem1</div>
<div id="elem2">elem2</div>
<div id="elem4">elem4</div>
<div id="elem3">elem3</div>
</div>
Much appreciated for any help!
Cheers
As far as I am aware the is no way to set the class of multiple elements at once. However, the browser may actually batch these changes for you anyway. Providing you don't read styles as well as writing them I believe this should hold true.
This article provides some insight into how reflow and repaint are triggered http://www.phpied.com/rendering-repaint-reflowrelayout-restyle/
You can prepare class like this :
.color1{
color : #000;
}
.color2{
color : #abc;
}
And set to your elements like this :
document.getElementById("elem1").className = document.getElementById("elem1").className + " color1";
document.getElementById("elem2").className = document.getElementById("elem2").className + " color2";
Depending on your element structure. For example assuming those elements are sibling DIVs, you can define CSS as:
div.myclass {
color:#000
}
div.myclass + div {
color:#fff
}
div.myclass + div + div {
color:#abc
}
Then a single JS command:
document.getElementById("elem1").className = "myclass";
Would set color for all 3: http://jsfiddle.net/PjZ77/1/
If it makes sense in your case, use css classes and swap the container class.
HTML structure could be :
<div id="container1">
<div id="elem1" class="clsA">A</div>
<div id="elem2" class="clsB">B</div>
<div id="elem3" class="clsC">C</div>
</div>
and in CSS:
#container1 .clsA { color: #000; }
#container1 .clsB { color: #111; }
#container1 .clsC { color: #222; }
#container1.mystate .clsA { color: #DDD; }
#container1.mystate .clsB { color: #EEE; }
#container1.mystate .clsC { color: #FFF; }
You can set document.getElementById("container1").className with mystate class (or empty class, or any class name that makes sense you defined in the css.
Class change occurs for only one element (the container), so the elem(n) child items will be refreshed at the same moment.

Adding another class to an element, but as the first class

I want to add another class to a element, but some of those elements already have an existing class, however the class I am adding I want it to be the first class added in the class field and don't want to append it; because from what I know, in general, rules in the last listed class will overwrite rules in the former classes, correct!?
I know of a jquery method as below..
$("p").addClass("myClass");
However from what I understand this just appends the class and you can't choose where to put it.
Is there any way to do this easily or will I have to start removing classes and re-adding them?
Not sure if there is any need to do something like this, I personally like advice of #bwoebi just change specifity.
But still if you need it, this might help
HTML
<div class="second">Lorem text</div>
jQuery
var $div = $('div'),
classes = $div.attr('class');
$div.attr('class', 'first' + ' ' + classes);
You can put your variable which contains class's name in place of 'first'
Demo
You add multiple classes to an element by simply separating the classes using a space. For example:
<p class="key_paragraph dark no_border">Your text here</p>
<p class="key_paragraph dark">Your text here</p>
The order in which the classes are listed in the html attribute does not matter. What matters is the stylesheet.
In your stylesheet (css file), if you want any of the classes to override another you could change the order of the rules in the stylesheet. For example, if the class 'dark' has a border and the class 'no_border' does not, you must place 'no_border' after 'dark' in your stylesheet to ensure 'no_border' overrides 'dark' for the first paragraph. Like so:
.dark {
border: 2px solid red;
}
.no_border {
border: none;
}
Secondly, you could add specificity regardless of the order of the css rules. For example, combining classes to say 'if an element has the classes dark and red' is more specific than either of the two above rules. See here for more info on specificity. Thus, the following rule would ensure there is no border on elements with the classes 'dark' and 'no_border' regardless of the order in which they appear on the stlesheet:
.dark.no_border {
border: none;
}
.dark {
border: 2px solid red;
}
A third option is to use the css :not selector to target all elements with the class 'dark' that do not also have the class 'no_border'. Again, the order of rules in the stylesheet will not matter here. This is done as follows:
.no_border {
border: none;
}
.dark:not(.no_border) {
border: 2px solid red;
}
There are other ways to do this, but hopefully I have explained three easy ways in a manner you can understand. There is a more jquery-specific demo here (see the third example). Let me know if you have any questions.
Just for fun in case you wanted to add the new class at a specific index inside the current classes:
http://jsfiddle.net/ckzVk/
function addClassAtIndex(elementId,classNameToAdd,index){
var element = document.getElementById(elementId);
var elementClasses = element.className.split(" ");
elementClasses.splice(index,0,classNameToAdd);
var newClasses = elementClasses.join(" ");
element.className = newClasses;
}
To add class "man" to div:
<div id="stuff" class="cool bro sweet">The div.</div>
JS:
addClassAtIndex("stuff","man",1");
Result:
<div id="stuff" class="cool man bro sweet">The div.</div>

Categories

Resources