Goal
In my program I want to do both things with jquery/javascript:
Change styling of css classes dynamically
Add/remove classes to elements
Problem
To do the first thing I use $(".className").css() method, but it changes style only for those elements that already have className class, i.e. if I later add className to an element its style won't be new. How can I solve this?
Example
See it also at jsfiddle.
$("p").addClass("redclass");
$(".redclass").css("color", "darkRed");
$("span").addClass("redclass");
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<p>I want to be red! And I am.</p>
<span>I want to be red too but I'm not :'(</span>
Result:
A more shorten format:
$("<style/>", {text: ".redclass {color: darkRed;}"}).appendTo('head');
The snippet:
$("<style/>", {text: ".redclass {color: darkRed;}"}).appendTo('head');
$("p").addClass("redclass");
$("span").addClass("redclass");
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<p>I want to be red! And I am.</p>
<span>I want to be red too but I'm not :'(</span>
While other (working) answers have been supplied, they don't actually answer your question - namely, they don't change the specified css class, but instead override it by adding another rule later in the document.
They achieve this, basically:
Before
.someClass
{
color: red;
}
After
.someClass
{
color: red;
}
.someClass
{
color: white;
}
When in many cases, a better option would see the color attribute of the existing rule altered.
Well, as it turns out - the browser maintains a collection of style-sheets, style-sheet rules and attributes of said rules. We may prefer instead, to find the existing rule and alter it. (We would certainly prefer a method that performed error checking over the one I present!)
The first console msg comes from the 1 instance of a #coords rule.
The next three come from the 3 instances of the .that rule
function byId(id){return document.getElementById(id)}
window.addEventListener('load', onDocLoaded, false);
function onDocLoaded(evt)
{
byId('goBtn').addEventListener('click', onGoBtnClicked, false);
}
function onGoBtnClicked(evt)
{
alterExistingCSSRuleAttrib('#coords', 'background-color', 'blue');
alterExistingCSSRuleAttrib('.that', 'color', 'red');
}
// useful for HtmlCollection, NodeList, String types (array-like types)
function forEach(array, callback, scope){for (var i=0,n=array.length; i<n; i++)callback.call(scope, array[i], i, array);} // passes back stuff we need
function alterExistingCSSRuleAttrib(selectorText, tgtAttribName, newValue)
{
var styleSheets = document.styleSheets;
forEach(styleSheets, styleSheetFunc);
function styleSheetFunc(CSSStyleSheet)
{
forEach(CSSStyleSheet.cssRules, cssRuleFunc);
}
function cssRuleFunc(rule)
{
if (selectorText.indexOf(rule.selectorText) != -1)
forEach(rule.style, cssRuleAttributeFunc);
function cssRuleAttributeFunc(attribName)
{
if (attribName == tgtAttribName)
{
rule.style[attribName] = newValue;
console.log('attribute replaced');
}
}
}
}
#coords
{
font-size: 0.75em;
width: 10em;
background-color: red;
}
.that
{
color: blue;
}
<style>.that{color: green;font-size: 3em;font-weight: bold;}</style>
<button id='goBtn'>Change css rules</button>
<div id='coords' class='that'>Test div</div>
<style>.that{color: blue;font-size: 2em;font-weight: bold;}</style>
#synthet1c has described the problem. My solution is:
$("head").append('<style></style>');
var element = $("head").children(':last');
element.html('.redclass{color: darkred;}');
What you are having issue with is that when you use the jQuery selector $('.redclass').css('color', 'darkRed') you are getting all the elements that currently have that class and using javascript to loop over the collection and set the style property.
You then set the class on the span after. Which was not included in the collection at the time of setting the color
You should set the class in your css file so it is distributed to all elements that have that class
console.log($('.redclass').length)
$("p").addClass("redclass");
console.log($('.redclass').length)
// $(".redclass").css("color", "darkRed");
$("span").addClass("redclass");
console.log($('.redclass').length)
.redclass {
color: darkRed;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<p>I want to be red! And I am.</p>
<span>I want to be red too but I'm not :'(</span>
Related
I have a site with a lot of different div. The thing they have in common is all share (besides their unique classes) a shared class. Lets just call it .changeClass.
What I am looking for is code with a button (or radio buttons) and by clicking the button, the background instance of all these divs will get the same one (which the .changeClass has). So the .changeClass will just be active when the button is toggled/clicked.
I am looking for a way to do this with pure javascript and no Jquery.
Sorry for being a noob :-)
In the solution below, clicking the <button> element will add/remove the class style .changeClass to all elements that have the class style .apply applied.
let button = document.getElementById('change');
let containers = document.getElementsByClassName('apply');
function changeButtonText() {
if(button.innerHTML === "Add")
button.innerHTML = "Remove";
else
button.innerHTML = "Add";
}
button.addEventListener('click', function() {
for(let index = 0 ; index < containers.length ; ++index)
containers[index].classList.toggle('changeClass');
changeButtonText();
});
div {
margin-top: 25px;
}
.apply {
border: 3px solid black;
}
.changeClass {
background-color: black;
color: white;
border: 3px solid red;
margin-top: 25px;
}
<button id="change">Add</button>
<div class="apply">1</div>
<div class="apply">2</div>
<div class="apply">3</div>
<div class="apply">4</div>
<div class="apply">5</div>
First lets get all divs that are on the DOM
const divs = document.getElementsByTagName("div");
You will have array of all the divs that are on the DOM. Then add your class to all of it. In order to do that, lets loop it.
divs.forEach(div => div.className += div.className + " changeClass");
Could this be what you are looking for?
In html:
<button onclick="changeColor('blue');">blue</button>
In JS
function changeColor(newColor) {
var elem = document.getElementsByClassName("changeClass");
elem.style.color = newColor;
}
The HTML color can be any color you would like it to be, just change they name from blue to any color or input a hex code.
We have multiple divs with the same class value
We have given a function to the button that we want the event to happen when it is clicked, using the onclick method. Now when we click the button, the function called myFunction will run.
HTML:
<div class="changeClass">Im Example Div</div>
<div class="changeClass">Me Too</div>
<button type="submit" onclick="myFunction()">Click To Change Div BgColors !
</button>
We must define myFunction as Javascript and change the background color.
We have defined a function called myFunction.
With the getElementsByClassName selector in our function, we got all the data with the class value changeClass in object format.
To add a background (or any css property) to all of these objects; We put the object in a for loop and now we split our elements.
We can now define a background color for our elements with the style.backgroundColor parameter.
JavaScript:
function myFunction(){
var divs = document.getElementsByClassName('changeClass');
for(var i=0; i< divs.length; i++){
divs[i].style.backgroundColor = 'red';
}
}
For more detailed information, you can refer to the resources: https://www.w3schools.com/jsref/met_document_getelementsbyclassname.asp
Don't be sorry for being new at something and wanting to learn more!
So what you are saying is that the divs you want to change all have a common class of "changeClass". If this is the case then you want a function is passed an argument value of the color you want to be changed. Since all of your divs are static and you probably don't plan on changing, declare a variable outside of this function that has the following code
const divs = document.getElementsByClassName("changeClass")
Then, inside of the function, loop through all of the divs collected inside the variable "divs", or whatever you want to call it. Since "getElementsByClassName" returns a collection, it does not have the built in "foreach" and "map" methods. So you have to use a for loop preferably the following.
const divs = document.getElementsByClassName("changeClass");
function changeColor(color) {
for (let element of divs) {
element.style.backgroundColor = color;
}
}
I may have interpreted this wrong but I hope it helps
You may find using a CSS variable helpful.
For example:
function bg(color) {
document.body.style.setProperty('--bg', color);
}
body {
--bg: cyan;
}
.container {
display: flex;
gap: 1vw;
}
.container div {
width: 100px;
height: 100px;
background-color: black;
}
.container div.changeClass {
background-color: var(--bg);
}
<body>
<button onclick="bg( 'red');">Red</button>
<button onclick="bg( 'green');">Green</button>
<button onclick="bg( 'blue');">Blue</button>
<button onclick="bg( 'black');">Black</button>
<div class="container">
<div class="changeClass"></div>
<div class="changeClass"></div>
<div class="changeClass"></div>
<div></div>
<div class="changeClass"></div>
</div>
</body>
Then when one of the radio buttons is clicked it sets the variable --bg.
Here's a simple snippet:
First of all - thank you for all your replies. And yes I should have included code. I tried so many things that i just gave up at som point - got confused what was right code and what was just rubbish. So I appreciate so much that you all took time to answer me. This was my first post so now I know for the future. The answers I got all was possible ways to solve my problem - so thank you all. I will do better next time. You are awesome...
BTW - All solutions seems to work - but can only checkmark one of them as you know.
You can add or remove a class to change the colours of different div:
document.queryselector('.className').classList.add('classNamethatyouwanttoadd');
document.queryselector('.className').classList.remove('classNamethatyouwanttoadd');
Which one of the following should be preferred under what circumstances?
btnElement.classList.add('btn');
btnElement.className = 'btn';
Using "classList", you can add or remove a class without affecting any
others the element may have. But if you assign "className", it will
wipe out any existing classes while adding the new one (or if you
assign an empty string it will wipe out all of them).
Assigning "className" can be a convenience for cases where you are
certain no other classes will be used on the element, but I would
normally use the "classList" methods exclusively.
And "classList" also has handy "toggle" and "replace" methods.
https://teamtreehouse.com/community/difference-between-classlist-and-classname
ClassList as the name suggest is the list of classes in an element.
If you have multiple classes on an element and you want to add/remove one without altering the rest you should use classList.
classList also provides methods like toggle which are really useful.
function toggleClass(){
let txt = document.querySelector("h2");
txt.classList.toggle("changebg");
}
.font-style {
font-family: sans-serif;
}
.changebg {
background-color: lightcoral;
}
<h2 class="font-style" >Hello World!</h2>
<button onclick='toggleClass()'>Toggle Background Class</button>
Using "classList", you can add or remove a class without affecting any others the element may have. But if you assign "className", it will wipe out any existing classes while adding the new one (or if you assign an empty string it will wipe out all of them)
classList
Using classList, you can add or remove a class without affecting any other classes the element may have.
So this is helpful for adding additional classes to an element that contain other classes.
classList has some handy methods like toggle and replace.
if (clicked) {
button.classList.add('clicked');
} else {
button.classList.remove('clicked');
}
Here if the button was clicked it will add the clicked class along with other classes the element may have and it will remove only the clicked class from the element.
className
If you use className, it will wipe out any existing classes while adding the new one (or if you assign an empty string it will wipe out all of them).
Using className can be convenience when you know this element will not use any other classes.
if (clicked) {
button.className = 'clicked';
} else {
button.className = '';
}
In this case, className will wipe all the classes the element may have and add clicked class to it. The empty string('') will wipe all the classes.
Conclusion
the recommendation would be to use className whenever possible.
Use classList when you need classList methods like toggle, replace, etc.
context https://dev.to/microrony/difference-between-classlist-and-classname-45j7
You can see the changes in JavaScript to apply same difference one with use of classList and other with className .
It will be clear from 1st btn only that classList add extra name in class while className replaces the whole class (only .border is applied) .
Further are different function of classList which cannot be achieved by className and at last 4 line of code is reduced to 1 liner with use of toggle .
So you should look to your needs : Like, if you want to completely replace the class property names than use className else you can use classList property with different methods .add() .remove() .replace() .toggle() to only have changes in specific without hampering all names of class
Instruction for below snippet : Reload the snippet when you click one button so that clear differences can be seen on next btns
var classList1 = document.getElementById("part1")
var classname2 = document.getElementById("part2")
function funcAdd() {
classList1.classList.add("border");
classname2.className = "border";
}
function funcRemove() {
classList1.classList.remove("color");
classname2.style.color = "black";
}
function funcReplace() {
classList1.classList.replace("background", "background1");
classname2.style.backgroundColor = "lightgreen";
}
function funcToggle() {
classList1.classList.toggle("color1");
if (classname2.style.color == "gold") {
classname2.style.color = "blue";
} else {
classname2.style.color = "gold";
}
}
.background {
background-color: red
}
.background1 {
background-color: lightgreen
}
.color {
color: blue
}
.font {
font-size: 24px;
}
.border {
border: 10px solid black
}
.color1 {
color: gold;
}
<div id="part1" class="background color font">classList</div>
<br><br><br>
<div id="part2" class="background color font">className</div>
<br><br><br>
<button onclick="funcAdd()">Add a border class</button>
<button onclick="funcRemove()">Remove a color class</button>
<button onclick="funcReplace()">Replace a background class</button>
<button onclick="funcToggle()">Toggle a color class</button>
<br><br>
I have just known one thing difference between className and classList. className returns string within they are names of the current element and classList also returns names but as an array.
I'm trying to make a page that has 3 buttons that make the background change color. I know how to do this, theoretically. I've been trying to build the methods for changing the background color but whenever I change the color, it's because the new style element overlaps the old one(s), so I'm looking for a way to delete the previous one when the new one is created but I've yet to find it.
var backgroundColor = {
red: function backgroundRed() {
var sheet = document.createElement('style');
sheet.setAttribute('id', 'redBG');
sheet.innerHTML = "body {background-color: red;}";
document.body.appendChild(sheet);
},
blue: function backgroundBlue() {
var sheet = document.createElement('style');
sheet.setAttribute('id', 'blueBG');
sheet.innerHTML = "body {background-color: blue;}";
document.body.appendChild(sheet);
},
green: function backgroundGreen() {
var sheet = document.createElement('style');
sheet.setAttribute('id', 'greenBG');
sheet.innerHTML = "body {background-color: limegreen;}";
document.body.appendChild(sheet);
},
deletePrevious: function() {
// ???
},
};
var applyColor = {
applyRed: function() {
//a method that when applying a new background color deletes the previous one
backgroundColor.red();
},
applyBlue: function() {
backgroundColor.blue();
},
applyGreen: function() {
backgroundColor.green();
}
}
This is the code I've written so far. The thing is, when I run it, this is what happens: Overlapping elements
How can I make a method that deletes the previous elements? Should I nest the elements within a div?
Edit: Turns out I'm wildly overthinking this. I'm been learning JS for about 2 months now, still have a long way to go. Andrew Lohr's comment effectively replaces all the backgroundColor functions I created. I'm also new to StackOverflow so I haven't found a way to upvote his comment yet. I need to get more acquainted with the DOM and easier ways to modify it.
Thank you all for your responses and your help.
You look like you're familiar with JS, so tell me if you need a example.
Make a style tag with the 'themeCSS'. Then, every time you want to add/replace the CSS, use:
themeCSS.innerHTML = "so { and: so; }";
That way, it'll always replace the previous CSS :)
Instead of changing the entire CSS <style> tag just set a value in the class` attribute that sets the overall theme and all of your CSS is based on that theme.
body.theme-red {
background-color: red;
}
body.theme-blue {
background-color: blue;
}
body.theme-green {
background-color: green;
}
.theme-red h1 {
color: black
}
.theme-blue h1 {
color: yellow;
}
.theme-green h1 {
color: red;
}
Or, break your CSS into three files and only load the correct one based on the theme.
Or, break you CSS into three files and use Alternate Style Sheets.
CSS
p:hover { ... }
div { ... }
.something { ... }
#content a:hover { ... }
HTML
<div id="content">
<p>
Test
</p>
</div>
I need to select all elements, which have defined :hover subclass in CSS. For this example, it would be <p> and <a> elements.
Is it possible to do it in JavaScript ?
At first I didn't think it was possible, but after some thinking I came up with this. I wrote it with ES2015 syntax because a couple of things (like using forEach on non-arrays) is easier with it but it could be written in ES5 syntax too if needed.
let getElementsWithHoverRule = () => {
let getSelectors = rule => {
// get everything upto the first curly bracket
let selectorText = rule.cssText.match(/^[^{]+/)[0];
// in case a rule has multiple selectors,
// we will want to filter them separately
// so we don't capture elements that share
// styling but have different selectors
return selectorText.split(',');
},
selectors = [],
rxHover = /:hover/;
// loop through all the style sheets
[...document.styleSheets].forEach(sheet => {
// and all of the rules in those style sheets
let rules = sheet.cssRules || sheet.rules;
if (rules !== null) {
[...rules].forEach(rule => {
let ruleSelectors = getSelectors(rule);
selectors = selectors.concat(ruleSelectors);
});
}
});
// find all of the rules that contain hover
selectors = selectors.filter(selector => rxHover.test(selector));
// remove the :hover from the selectors so we can select them without the user
// hovering their mouse over them
selectors = selectors.map(selector => selector.replace(rxHover, ''))
return document.querySelectorAll(selectors.join(', '));
};
let hoverElement = getElementsWithHoverRule();
console.log(hoverElement);
// put red box around matched elements when the page is clicked
document.addEventListener('click', () => {
[...hoverElement].forEach(el => el.style.border = '5px outset #f00');
}, false);
p:hover { background: #eef }
span, a:hover { background: #000; color: #fff; }
div { color: #000; }
.something { color: #00f }
#content a:hover { color: #ff0 }
<div id="content">
<p>
Test non-link text
</p>
</div>
<p>another <span>paragraph</span>. A link that is not inside of content</p>
<br>
<br>
<br>
What it does is use document.styleSheets to get a list of all the style sheets and then loops through all the rules in them extracting their selectors. It then filters out the rules that don't contain :hover and then removes hover from the ones that do and uses those new rules to select the elements.
Edit
In the original code, if a rule had multiple selectors such as .foo, #bar:hover, it would return both .foo and #bar. I've updated the code so it will only return #bar since that is the only selector for the rule that contains :hover
There is a set of functions, the so-called "selectors api" https://developer.mozilla.org/en/docs/Web/API/Document/querySelector, https://developer.mozilla.org/en/docs/Web/API/Document/querySelectorAll and similar. But, nearly the only thing you can't do with these functions, is selecting pseudo classes (such as :hover).
I'm afraid you will have to monitor the mouseover and maybe mouseleave events and store the currently hovered element in a separate variable. By using its parentNode property (and its parent's parentNode property), you will have access to the parent chain.
I would suggest something like this:
var hoveredElement = null;
document.addEventListener( "mouseover", function(e){
hoveredElement = e.target;
});
i am trying to make a colour change when a button is clicked and i managed to do this however i want to change the colour of not just the main content container but more containers how do i do this?
function changeblackandwhite(objDivID) {
if(document.getElementById(objDivID).style.color=='black'){
document.getElementById(objDivID).style.color='white';
document.getElementById(objDivID).style.backgroundColor='black';
}
else if(document.getElementById(objDivID).style.color=='white'){
document.getElementById(objDivID).style.color='black';
document.getElementById(objDivID).style.backgroundColor = 'white';
}
else{
document.getElementById(objDivID).style.color='black';
document.getElementById(objDivID).style.backgroundColor='white';
}
}
<img src="images/colour.jpg" title="Change Text/Backgroud Colors">
There are dozens of ways you can accomplish this.
You could change the argument of your function to be an array of strings. You could also reduce the complexity of your function as well
<script type="text/javascript">
changeblackandwhite = function() {
for( var idx=0; idx < arguments.length; idx++) {
var tgtDiv= document.getElementById(arguments[i]);
if(tgtDiv.style.color=='black'){
tgtDiv.style.color='white';
tgtDiv.style.backgroundColor='black';
}
else{
tgtDiv.style.color='black';
tgtDiv.style.backgroundColor='white';
}
}
};
</script>
<img src="images/colour.jpg" title="Change Text/Backgroud Colors">
As another reader questioned - you can do this with jQuery in a single line.
With jQuery, you can declare the elements in question to have a class attribute.
Using jQuery, you can then do something like:
$('div.someClass').css({'color': 'black', 'background-color': 'white'});
The argument to jQuery can be a class based selector, an id based selector, or any other selector you choose.
If you are open to jquery and you assign 1 class in common with these two divs you can do the following:
This should get you started (see this jsfiddle): I changed the fiddle to include a neater solution where clicking on the button adds and removes classes on the containers which allows you to set multiple attributes including the text color in one quick call.
<div class='container'>
</div>
<div class='container'>
</div>
<button id="changeColor" type="button">Change Color </button>
<script type="text/javascript">
$(document).ready( function() {
$('#changeColor').click( function() {
if ($('.container').hasClass("blackContainer")){
$('.container').addClass("whiteContainer");
$('.container').removeClass("blackContainer");
} else {
$('.container').removeClass("whiteContainer");
$('.container').addClass("blackContainer");
}
});
});
</script>
//CSS
.blackContainer {
background-color: black;
color: white;
}
.whiteContainer {
background-color: white;
color: black;
}
I made a jsfiddle for you to play around with jsfiddle
I also did the javascript/jQuery in a similar way as the OP since it usually helps them understand.
As stated above, there are several different ways to do this, I've done but one.
The document.ready function sets up an event listener for the object to be clicked, most of the time this is how you'll see events coded. So when the link is clicked, it calls the function with the string name of the object the listener is for.
$(document).ready(function() {
$("#changeit").click(function(){
changeblackandwhite("Maincontainer");
})
});
After the event listener is assigned, it will call the function below when the link is clicked on.
// Here's your function, put the current color in a var, check if it's black
// if black, change colors, else make it black.
function changeblackandwhite(objDivID) {
var curColor = $("#" + objDivID).css("color");
if( curColor == 'rgb(0, 0, 0)'){
$("#"+objDivID).css({'color':'white','background-color':'black'});
} else {
$("#"+objDivID).css({'color':'black','background-color':'ghostwhite'});
}
}