Difference between className and classList - javascript

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.

Related

Change color in a number of different divs that all share the same class

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');

EventListener Only Firing Once

still learning the basics of JS, working with EventListeners now. Trying to make a button that changes the color of text back and forth but I think I'm misunderstanding the nature of the method, or using it incorrectly. I don't believe it's a syntax issue.
I have the text and the button, both with Id's. I created variables for both elements. I add an event listener to the button, and defined the if else statement in the function. The "if" portion of the function executes without issue, but that's where it ends. Sorry in advance for the formatting I wasn't sure what made the most sense. Thanks!
Here's the HTML:
<h1 id="header"> Here's some text </h1>
<button id="button"> Change the Color </button>
CSS:
#header {
color: red;
}
And the JavaScript:
var header = document.getElementById("header");
var button = document.getElementById("button");
button.addEventListener("click", function() {
if (header.style.color = "red")
{header.style.color = "blue";}
else if (head.style.color = "blue")
{header.style.color = "red";
}
})
In JavaScript (and other languages) you need to use == to check for equality.
However, in JavaScript there is also ===. === is the strict equality operator, meaning it does not do type conversion. What does that mean? It means:
"5" == 5 // true, since "5" as a number is equal to 5, the literal number
"5" === 5 // false, since a string cannot equal a number
So in your if statements you should use == or === instead of just =.
Others have mentioned the use of = vs == vs === - which is definitely your problem, but you're also going to have other problems with comparing styles the way you are doing.
The style property is unique and cumbersome. You have the "style" property which is a property of the DOM node (just like href for anchors or type for inputs). Then you have styles which are applied from a stylesheet - either a <style> tag or external stylesheet file. Sometimes the two different styles sources are in conflict.
For style properties, you read the node.style.color property like you are doing. To get the actual color being applied to the node, you must use window.getComputedStyle(). Let me explain the difference by example:
const div = document.getElementById('foo')
div.style.color; //-> red
window.getComputedStyle(div).color; //-> rbg(0, 255, 0) - this is green!
#foo { color: green !important }
<div id="foo" style="color: red">Hello!</div>
Notice how we set red on the node itself, but green !important in the stylesheet. The !important will win, which is why the text is green. Furthermore, the browser converts the color name green to its RGB equivalent rgb(0, 255, 0). This can be tedious to reconcile. What I usually recommend is having multiple class names and switching between those on click:
var header = document.getElementById("header");
var button = document.getElementById("button");
button.addEventListener("click", function() {
if (header.classList.contains("red")) {
header.classList.remove("red")
header.classList.add("blue")
} else if (header.classList.contains("blue")) {
header.classList.remove("blue")
header.classList.add("red")
}
})
.red { color: red }
.blue { color: blue }
<h1 id="header" class="red"> Here's some text </h1>
<button id="button"> Change the Color </button>

remove styles from all nodelist element and add only to clicked element Vanilla JS

I have multiple divs that when clicked adds a border and scales them up a little. I am looping through all elements using foreach and on click i remove every element's border and scale property except the clicked element, to which i add a border and scale.
My code is completely logical and is supposed to work but for some reason i cant seem to grasp, it only applies the styles to clicked elements but not removing from the rest of the elements (like my code says it should).
JS
document.querySelectorAll('.projcolorpick div').forEach(el => {
el.onclick = (e) => {
el.style.border = "none"
el.style.transform = "scale(1)"
e.target.style.border = "2px solid #fff"
e.target.style.transform = "scale(1.2)"
projcolor = e.target.style.background
}
})
}
give something like this a try... each element needs an id attribute for this to work (the filter part - if there is a unique attribute...)
const list = Array.from(document.querySelectorAll('.projcolorpick div'));
list.forEach(el => {
el.addEventListener('click', (e) => {
//code that affects the element you click on
el.style.border = "2px solid #fff"
el.style.transform = "scale(1.2)"
projcolor = e.target.style.background;
list.filter(x=>x.id!=el.id).forEach(otherEl=>{
//code that affects the other elements you didn't click on
otherEl.style.border = "none"
otherEl.style.transform = "scale(1)"
});
});
});
```
edit:
fixed some typos.
forEach only applies to Arrays unless you configure it otherwise.
querySelectorAll does not return arrays, but array-like objects (NodeLists)
To allow looping over NodeLists, add the following code:
if (window.NodeList && !NodeList.prototype.forEach) {
NodeList.prototype.forEach = Array.prototype.forEach;
}
var nL = document.querySelectorAll('*');
console.log(nL instanceof NodeList); // true
You don't really need an id attribute on each div and I would advocate using class-assignments instead of changing their individual attributes. You can compare the actual DOM elements with each other like c==ev.target, as you can see in my code below:
// === populate the page first ... ============================= START =
const cont=document.getElementById('container');
cont.innerHTML=
[...Array(3)].map(cp=>'<div class="projcolorpick">'+
[...Array(8)].map(d=>{
let hsl= "hsl("+Math.floor(Math.random()*360)+",100%,80%)";
return ' <div style="background-color:'+hsl+'">'+hsl+'</div>'}).join('\n')
+'</div>').join('\n');
// === populate the page first ... =============================== END =
// now, do the action:
cont.onclick=ev=>{
if ( ev.target.parentNode.classList.contains('projcolorpick')
&& ev.target.tagName=='DIV'){
[...ev.target.parentNode.children].forEach(c=>c.classList.toggle('selected',c==ev.target));
ev.target.parentNode.style.backgroundColor=ev.target.textContent;
}
}
.projcolorpick {border: 2px solid #888}
.selected {border: 2px solid #fff; transform:scale(1.2);}
div {margin:6px; padding:4px}
.projcolorpick div {width:200px; height:20px}
<div id="container"></div>
The action happens here:
cont.onclick=ev=>{
if ( ev.target.parentNode.classList.contains('projcolorpick')
&& ev.target.tagName=='DIV'){
[...ev.target.parentNode.children].forEach(c=>c.classList.toggle('selected',c==ev.target));
ev.target.parentNode.style.backgroundColor=ev.target.textContent;
}
}
I use a delegated event-attachment to the parent .container div. The first if statements makes sure that only clicks on .projcolorpick>div elements are processed.
If you want to include more than one generation between them you need to use something like ev.target.closest('.projcolorpick') instead ...
Now, inside the if block two things happen:
Using toggle() on all DOM elements in ev.target.parentNode.children the class "selected" is either
assigned or
removed.
The text found in the clicked div is applied as background-color to the parent .projcolorpick container.

How to select HTML DOM elements which have :hover subclass?

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;
});

How to dynamically change a class css styling?

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>

Categories

Resources