An easy way to switch styles between multiple elements in Javascript? - javascript

Let's say I have following situation which I have to switch the style for several elements and switch them back when a specific condition is met.
let div = document.querySelectorAll('div')
document.querySelector('button').addEventListener('click', function() {
div[0].classList.toggle("div1")
div[1].classList.toggle("div2")
div[2].classList.toggle("div3")
div[0].classList.toggle("new1")
div[1].classList.toggle("new2")
div[2].classList.toggle("new3")
})
.div1 {
color: red;
}
.div2 {
color: green;
}
.div3 {
color: grey;
}
.new1 {
color: yellow;
background: grey
}
.new2 {
color: pink;
background: green
}
.new3 {
color: orange;
background: red
}
<div class='div1'>1</div>
<div class='div2'>2</div>
<div class='div3'>3</div>
<button>Click</button>
I am now creating several class and use classList.toggle() to switch between them, it absolutely work but the code looks so massy and I want to make my code more readably, what will a better solution for this kinds of situation.
I have thought of switching between with/without a specific css stylesheet, but I don't think it will work for my situation and I need to consider overwriting problem? (Correct me if I am wrong).
Could anyone suggest me an alternative and better solution of solving this situation like this where you have to assign lots of styles to multiple elements or is there an easy way?
*I know this is a stupid example and in this example, a possible solution is to use a forEach loop and use template literal for the className, but this is just a minimal example I created because of Stack overflow rule, so please don't blame me on this rough example. My actual code contains more different html tags and css styles I have to deal with. By storing them in a variable and keeping switching class between them is too messy and annoying.
My code looks something like this:

So here's what I recommend: You can use conditional styling to change everything in one place. Example:
.a {
/* one set of styles */
}
.b .a {
/* set a different set of styles */
}
This way, you can conditionally set class b on a higher level element (like document.body) and change all your styles automatically.

You can change the classes of the elements in a loop (assuming you have your css pre-formated). For example;
/**
* When the BUTTON with the id named 'clickMe' is click
* Select all the DIV elements
* Foreach DIV selected, if the DIV contains the 'old-class-name'; remove it.
* Add the new class name to the class-list of the selected DIV
*/
document.body.querySelector('#clickMe').addEventListener('click',()=>{
document.body.querySelectorAll('DIV').forEach((D,I)=>{
if(D.classList.contains('old-class-name')) D.classList.remove('old-class-name');
D.classList.add(`new-class-name-${I}`);
});
});
// List of CSS class names.
.new-class-name-0{
color: orange;
}
.new-class-name-1{
color: green;
}
.new-class-name-2{
color: blue;
}
<div class="old-class-name">Div 1</div>
<div class="old-class-name">Div 2</div>
<div class="old-class-name">Div 3</div>
<button id="clickMe">Click</button>

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.

Can I make an element inherit the properties of other elements?

We're providing a pre-populated WordPress site for multiple members to use on their servers. The theme has a selector where they can customize their heading colours and fonts. Each member will have different colours based on their branding.
The site also includes a knowledgebase plugin and it uses its own colours which can be customized within its interface. Instead, we'd like to be able to set the knowedgebase colours/properties based on the theme's colour palette. (Ultimately, we'd like the member to only have to change the colors for the theme in one place rather than customize every single plugin.)
So, if H1 is globally set to blue, we'd like to be able to tell the knowledgebase's element (.kb-header) to be the same colour as H1.
Is this at all doable via CSS or Javascript or something?
Many thanks!
You can make an element to inherit the properties of a parent element. For example:
p { color: red; }
a { color: inherit; }
<p>Paragraph with link</p>
But definitely this won't work for what you're asking if your .kb-header is not a child of your h1.
Instead you could use different approaches to get the desired result. For example with custom properties (a.k.a CSS variables)
:root {
--user-color: red;
}
h1 {
color: var(--user-color);
}
.kb-header {
color: var(--user-color);
}
<h1>Title</h1>
<header class="kb-header">This is the header</header>
This way you could, for example, output your :root selector defining all your custom properties in your <head> tag using PHP. And your CSS would be totally independent from it.
If you want to inherit (Object Oriented Programing like) if the html tags doesn't have any relationship, you can use SCSS, for example SASS, it's a text pre processor that generates the CSS with a CSS-like coding with the addition that you can use variables and much more.
For more details visit SASS lang webpage i think this can help you a lot for mostly all your projects.
If you don't want to use SASS, you can even use javascript (with or without libraries) to reach this job, as plain CSS is not capable to conditionally apply styles between non-related tags.
I've written a miniature version of what you're looking for. Hope this helps :P
<html>
<style>
h1 {
background-color: blue;
}
</style>
<body>
<h1>Hey!</h1>
<div class="kb-header">I'm a div :)</div>
<script src="https://code.jquery.com/jquery-3.2.1.min.js"></script>
<script type="text/javascript">
$(document).ready(function() {
var bgColor = $("h1").css("background-color");
$(".kb-header").css({"background-color": bgColor});
});
</script>
<body>
</html>
In CSS you can combine selectors with a comma and have a single block of rules for both:
.a, .b { property: value; }.
And still style one or another selector with other declarations (example below).
When selectors are unrelated, it causes maintenance problems but if it's generated by a theme generator, that should be OK (you don't want to style those given components but have a clear documented list of what has to be styled with a set of colors).
h1,
.kb-header {
color: darkred;
}
h1 {
font-size: 2rem;
}
#media (min-width: 961px) {
.kb-header {
border: 1px solid tomato;
}
<h1>Title</h1>
<p>Unstyled paragraph</p>
<header class="kb-header">This is the header</header>
You can also add a utility class to all elements that are to be styled with a border, a background color and a color:
/* Theme 1
Primary color: #0055D0
Secondary color: #080;
*/
.c-primary {
color: #0055D0;
}
.bd-secondary {
border: 1px solid #080;
}
/* Common styles */
h1 {
font-size: 2rem;
}
.kb-header {
padding: 1rem;
}
<h1 class="c-primary">Title</h1>
<header class="kb-header bd-secondary c-primary">This is the header</header>
Other answers talking about a Sass environment (or other preprocessors like PostCSS and LESS) and CSS "Variables" (if you don't have to support old browsers like IE11 and Edge ~15 + Saf (iOS and OS X) 9.2 https://caniuse.com/#search=custom%20prop are also fine solutions to your problem IMHO.

How to define multiple styles for one CSS class?

I am implementing a dark mode on my site, and trying to do it in the cleanest way possible (no boiler plate code).
So I want to make .darkmode class in CSS, define styles with it, and when the user enables darkmode, javascript simply adds the darkmode class to the <body>.
How could I do something like this with CSS?
.darkmode {
.content{
background-color: black;
}
input{
background-color: black;
}
}
So my questions is, how can I make CSS change different elements on the page when adding this class to the <body>?
The code that you posted would be valid SCSS/LESS. But in plain css you can simply do that by using
.darkmode .content { /* CSS */ }
.darkmode input { /* CSS */ }
So yes, you always have to specify the .darkmode in front of every selector.
Let's suppose you have a selector, like
.mydiv .myanchor
You can override/add attributes using
body.darkmode .mydiv .myanchor
is much more specific and therefore the rules will override the default rules.
To achieve that in normal CSS you would have to use the CSS child selector;
body.darkmode .content {
/* Put styles here */
}
body.darkmode input {
/* Put styles here */
}
Basically the logic there says: "get the body element with the class darkmode and find it's child .content/input"
With CSS selectors, having two element selectors seporated by a space finds all of the second elements inside the first elements; div p would find all of the <p> tags inside all <div> tags.

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.

Can I prevent a CSS style to be overwritten?

I'd like to apply a CSS to some linkbuttons on page load but one of them <a id="lb1">logoff</a> must keep its style, no hover nor other event must change its style.
The linkbuttons have no class and the css applied to all of them is done to tags, this way:
a
{
//style
}
a:hover
{
// style
}
Is it possible?
No, you can't.
You can use more specific selectors (or even inline CSS with the style attribute) so that they are less likely to be overridden accidentally.
You can use the (eugh) sledgehammer of !important so they will only be overridden by another !important rule.
There is no way to prevent them being overridden though.
Please please please please please avoid using !important whenever possible. You will run into SO many annoying problems and issues from using this. I consider it a very lazy hack.
What you want to do is append a class to the link that you don't want overwritten. Classes are given a higher priority than general selectors (such a, p, b). So if you append this class to the link, the CSS will override the default CSS you have set for a.
CSS:
a {
color: red;
}
a:hover {
color: blue;
}
.derp:hover { /*you can add everything you want to preserve here, essentially make it the same as the link css. you can also change it to #lbl:hover, although there's no good reason to be using an ID as a CSS selector*/
color: red;
}
HTML:
this will turn blue on hover
<a class="derp" href="#">this will stay red on hover</a>
Here's a fiddle to show you. The second link has a class appended that preserves the original style: http://jsfiddle.net/p6QWq/
Why not add a class to all the link buttons you want to change, and not add it to the one you don't want to change.
Then you can call:
$(".myClass").css("backgound-color", "blue");
This would change the background color for every element with a class of myClass to a blue background.
Or you could add a whole new class to the link buttons that have a class of myClass:
$(".myClass").addClass("myExtraClass");
This would then make the class attribute of your link button class="myclass myExtraClass"
Seeing your code posted makes it a little more clear on what you want to do. Try this:
a {
text-decoration: none;
color: orange;
}
a:hover {
text-decoration: underline;
color: blue;
}
This would apply a default style to all <a> elements. Now you could overwrite this default style by providing a specific style for the anchor with the id you gave above:
#lb1 {
color: black;
text-decoration: none;
}
#lb1:hover {
color: black;
text-decoration: none;
}
I mocked this up in a quick and dirty jsFiddle. See if this gives you the desired result. IDs take precedence over classes and default element styling. So if you have one that you want to keep the same, apply and ID and style the particular element accordingly. This would also help you by preventing you from having to apply a class to several elements. It's less coding to apply one ID than to apply twelve classes. (Just an exaggerated example. I don't know how many links you have.)
Hope this helps.
css is cascading by definition, so any style you apply to a tags will apply to this specific one, except if you overwrite it.
You'll have to either assign a class to all the other buttons or overwrite all the default properties for this specific button.
Also, do not forget the pseudo-classes :visited and :active.
You should use !important in your css like :
a {
/* style */
background: #FFF !important;
}
a:hover {
/* style */
background: #FFF !important;
}
You could always overwrite your css by simply creating another stylesheet and place it at the END of your stylesheet links in the head of your html.
<head>
<link rel="stylesheet" href="location/location/first_stylesheet.css">
<link rel="stylesheet" href="location/location/revised_stylesheet.css">
</head>
This is not the most productive method of overwriting your css however; one would be well advised to eliminate the necessity for this separate stylesheet by simply appending elements with a class attribute. The class attr will allow you to modify basic html elements, tags and overlay a final layer to "rule them all". Enjoy!

Categories

Resources