How do I add classes to an unspecified amount of elements - javascript

I feel like this should be simple enough but I tend to overthink things. I simply want to add new classes to only the container-1 elements. The classes need to be numbered to differentiate them. Bonus for multiple methods & explaining in detail as I'd like to learn how to do this properly.
Example:
<div class="scroller">
<div class="container-1 newClass1"></div>
<div class="container-1 newClass2"></div>
<div class="container-2"></div>
<div class="container-1 newClass3"></div>
<div class="container-2"></div>
</div>
As the title says, the amount can be random. So it needs to read & apply to all container-1 within the scroller.

Here is some js, which get all elements by class name "container-1". After that in for loop, it will add class "newClass" with counter mark to each of the element with this class.
var elements = document.getElementsByClassName("container-1");
for(var i = 0; i < elements.length; i++){
elements[i].classList.add("newClass" + (i+1));
}

Well, one method I can think of is:
let selectedContainers = Array.from(document.querySelectorAll('.container-1'));
selectedContainers.map((container, i)=>{
container.classList.add(`newClass${++i}`)
})
What's going on here is that you're selecting all the divs with the container-1 class and creating an array from the NodeList. Then you're mapping through the array to access each element. Using template strings, you add a class name with a dynamic variable being the index of the container. You could use other variables and just increment them after, but I think the index is the easiest solution. Also, the reason I used ++i is to avoid getting a class with a zero index e.g newClass0

You can create an array of all the elements having class name 'container-1 using document.getElementsByClassName and the spread operator(...). Spread operator is used here because getElementsByClassName returns a HTMLCollection. So to iterate over it easily using a forEach we need to convert it to an array. Once converted, we could iterate over it and add newClass class to every element. To get the number to be added to the end of each newClass we use the 2nd argument to forEach which is index of the current element.
const containerOneDivs = [...document.getElementsByClassName('container-1')];
containerOneDivs.forEach((div, i) => div.className += ` newClass${i+1}`);

You can use querySelector (just to teach people who still use jQuery) to query for DOM elements.
You can then use Array.forEach to iterate over the list, and use $node.classList.add to append a class name.
Just for the purpose of the example:
window.onload = function onload () {
document.getElementById('btn-add').addEventListener('click', add, true);
document.getElementById('btn-remove').addEventListener('click', remove, true);
}
function add(e) {
updateFocus(e.target);
const list = document.querySelectorAll('.container-1');
list.forEach((item, i) => item.classList.add(`newClass-${i}`));
}
function remove(e) {
updateFocus(e.target);
const list = document.querySelectorAll('.container-1');
list.forEach((item, i) => item.classList.remove(`newClass-${i}`));
}
function updateFocus(el){
document.querySelectorAll('.active').forEach((i) => i.classList.remove('active'));
el.classList.add('active');
}
.scroller {
height: 100%;
width: 100%;
}
div[class*="container"] {
display: inline-block;
border: 1px solid blue;
width: 20px;
height: 20px;
margin: 2px;
}
div[class*="newClass-"] {
border: 1px solid yellow;
}
.active {
border: 3px solid red;
font-weight: bold;
}
<div class="scroller">
<div class="container-1"></div>
<div class="container-1"></div>
<div class="container-2"></div>
<div class="container-1"></div>
<div class="container-2"></div>
</div>
<button id="btn-add">add newClass-* to container-1</button>
<button id="btn-remove">remove newClass-* to container-1</button>
Otherwise it is just one line of code
document.querySelectorAll('.container-1');
.forEach((item, i) => item.classList.add(`newClass-${i}`));

Related

Why are div elements not re-indexed after removing one of them with JavaScript?

I want to remove a div with the index == 0 each time I click on one of the elements with the class .slide.
It works the first time, but when I try again, the code tries to remove the element that has been previously removed, but I can still log the element to the console.
I thought that index "0" will be assigned to the next div sharing the same class, so the next time I click, this next div would be deleted.
What am I missing here?
Here is my code:
let slides = document.querySelectorAll(".slide")
slides.forEach((el) => {
el.addEventListener("click", () => {
// remove the first div
slides[0].remove()
// the element above has been removed, but I still can log it out (?)
// and it seems to keep the index [0]
console.log(slides[0])
})
})
This will do exactly what you expect - and will only get from the live HTMLCollection that getElementsByClassName returns:
let slides = document.getElementsByClassName("slide")
for (const slide of slides) {
slide.addEventListener("click", () => {
// remove the first div
slides[0].remove()
console.log(slides[0])
})
}
<div class="slide">0</div>
<div class="slide">1</div>
<div class="slide">2</div>
<div class="slide">3</div>
<div class="slide">4</div>
<div class="slide">5</div>
I believe you have deleted the element from the DOM, but have not removed it from the array. Try using the shift array method to remove the first element that you are deleting.
slides.forEach((el)=>{
el.addEventListener("click",()=>{
slides.shift().remove();
})
}
Based on your feedback, and the research I did ( on live and static node lists ) I came up with the following solution to my question. As you can see I am not using a querySelection nodelist anymore as this type is static. Instead, I went for a getElementsByClassName HTML collection. What do you think of this? Thanks for your precious support!
let sliderCollection = document.getElementsByClassName("slider") // HTML COLLECTION
let arrayCollection =[]
arrayCollection = Array.from(sliderCollection) // HTML COLLECTION turned into an array
console.log("initial number of divs:",arrayCollection.length) // initial number of divs
arrayCollection.forEach( (el)=>{
el.addEventListener('click',()=>{
sliderCollection[0].remove() // remove always the first element
arrayCollection = Array.from(sliderCollection) // refill the array with the updated number of divs
console.log(arrayCollection.length) // keep track of the current number of divs
})
})
.container {
display: flex;
flex-direction: row;
}
.slider {
height: 100px;
width: 100px;
background-color: red;
border: solid 2px black;
font-size: 3em;
}
<div class="container">
<div class="slider">0</div>
<div class="slider">1</div>
<div class="slider">2</div>
<div class="slider">3</div>
<div class="slider">4</div>
<div class="slider">5</div>
</div>

How to create many divs without actually creating them individually

I'm working on a project with html, css and js and I want to create many divs (let's say, 200) so, how can i create them without actually writing code for 200 different divs... I know I can do it with js but could I do it only with html and css..?
As I already said in my comment, you can't do it without JavaScript.
A JavaScript solution would be the following:
const container = document.getElementById("container");
for (let i = 0; i < 200; i++) {
const item = document.createElement("div");
item.classList.add("item", "item-" + i);
container.appendChild(item);
}
#container {
border: 1px solid red;
padding: .5rem;
}
.item {
border: 1px solid black;
height: 3rem;
margin-bottom: .3rem;
}
<div id="container">
</div>
One way with js:
you can create one HTML Span Element and with js you change the content of it. I bet with that knowledge you find something
how it's going? Html or css is not a programming language. So you can't do some kind of automation or repeating code or something like that. The only way to do that sort of things is using Javascript. Here is how:
<div class="container">
<!-- let's say we trying to add divs here. -->
</div>
And here's script:
function addMultiple (where, what, count, callBack) {
for (let i = 0; i < count; i++) {
// creating elements with document.createElement() method
let item = document.createElement(what)
// and then adding them into "where" element.
where.appendChild(item)
/*
and finally we'll have a function called Callback.
This is just gonna take our item as an argument and
do some stuff on it according to what we want.
(I'll show a simple usage down below)
*/
callBack(item)
}
}
// let's say we want to add 200 divs into "container" element
// and then we want to add class name to those divs.
const container = document.querySelector('.container')
addMultiple(container, 'div', 200, item => {
// this code will be repeated for all divs.
item.classList.add('what_ever_you_want')
})

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

How to remove one css class from div container with multiple elements and classes?

I want to get all elements which belong to two specific different classes and remove and add these classes seperately from these elements. I tried:
.concealed {
display: none;
}
.slide {
background: #333;
color: #fff;
}
// <div class="slide concealed">myText</div><br>
<div class="slide"><div class="concealed">myText</div></div><br>
<div class="slide"><div class="concealed">myText_2</div></div><br>
<div class="slide"><div class="concealed">myText_3</div></div><br>
// var slides = document.getElementsByClassName('slide');
// var slides = document.querySelectorAll('.slide, .concealed');
var slides = document.getElementsByClassName('slide concealed');
slides[0].classList.remove("concealed");
As you can see I tried several ways to achieve this but I can only remove and add "concealed" when I do var slides = document.getElementsByClassName('concealed'); . When doing getElementsByClassName with multiple classnames it seems to miss out concealed and only get slide. E.g. doing slides[0].classList.remove("concealed"); after document.getElementsByClassName('slide concealed'); has no effect.
I am sure I am missing something, this can't that hard to implement. Any ideas? Thanks.
The issue is that getElementsByClassName is a live HTMLCollection. When an element no longer matches (because you've removed the class), it's removed from the collection:
const list = document.getElementsByClassName("example");
console.log(list.length); // 2
console.log(list[0].innerHTML); // "A"
list[0].classList.remove("example");
console.log(list.length); // 1
console.log(list[0].innerHTML); // "B"
<div class="example">A</div>
<div class="example">B</div>
This means that if you're doing a loop and operate on the first entry, then increment your index, you'll miss what used to be the second entry because it's the first entry now.
A couple of ways to fix that:
Loop backward, or
Convert the HTMLCollection into an array before starting, or
Use querySelectorAll to get a snapshot NodeList instead, so that the lists contents don't change while you're updating.
Here's an example removing the classes red and bold from a series of elements using querySelectorAll so the NodeList is static:
setTimeout(() => {
const list = document.querySelectorAll(".red, .bold");
for (let n = 0; n < list.length; ++n) {
list[n].classList.remove("red");
list[n].classList.remove("bold");
}
}, 800);
.red {
color: red;
}
.bold {
font-weight: bold;
}
<div class="red">red 1</div>
<div class="bold">bold 1</div>
<div class="red bold">red 2 and bold 2</div>
<div class="bold">bold 3</div>
<div class="bold">bold 4</div>
See also my answer here: NodeList is now officially iterable, meaning you should be able to use a for-of loop on it. That answer shows how to polyfill that on slightly-older environments that haven't implemented it yet. It also shows how to add iterability to HTMLCollection (but note that HTMLCollection is not specified to be iterable).

Jquery associate two arrays

I would realy appreciate any help with this. Is there any way to simplify this code? I would like to pair elements of both arrays with the same index to perform specific functions.
var insects = ['#redhairy', '#bollworm', '#stalk', '#stem', '#chuija', '#diamond', '#pyrilla'];
var plants = ['.groundnut', '.cotton', '.maize', '.rice', '.wheat', '.mustard', '.sugarcane'];
$(insects[0]).hover(function(){
$(plants[0]).toggleClass('active');
});
$(insects[1]).hover(function(){
$(plants[1]).toggleClass('active');
});
$(insects[2]).hover(function(){
$(plants[2]).toggleClass('active');
});
$(insects[3]).hover(function(){
$(plants[3]).toggleClass('active');
});
$(insects[4]).hover(function(){
$(plants[4]).toggleClass('active');
});
$(insects[5]).hover(function(){
$(plants[5]).toggleClass('active');
});
$(insects[6]).hover(function(){
$(plants[6]).toggleClass('active');
});
If you have access to the HTML you could add a data attribute to the elements which receive the hover event which is the selector for the element that is to have the class toggled. This way you can group them with a common class and only use one selector, like this:
$('.item').hover(function() {
$($(this).data('target')).toggleClass('active');
});
.active {
color: #C00;
font-weight: bold;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="redhairy" class="item" data-target=".groundnut">Red hariy</div>
<div id="bollworm" class="item" data-target=".cotton">Bollworm</div>
<div class="groundnut">Groundnut</div>
<div class="cotton">Cotton</div>
Alternatively, if you can't amend the HTML you could loop through the array and put a event handler on each element individually, using the index of the loop to get each item from the array:
var insects = ['#redhairy', '#bollworm', '#stalk', '#stem', '#chuija', '#diamond', '#pyrilla'];
var plants = ['.groundnut', '.cotton', '.maize', '.rice', '.wheat', '.mustard', '.sugarcane'];
insects.forEach(function(insect, i) {
$(insect).hover(function() {
$(plants[i]).toggleClass('active');
});
});
.active {
color: #C00;
font-weight: bold;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="redhairy" class="item">Red hariy</div>
<div id="bollworm" class="item">Bollworm</div>
<div class="groundnut">Groundnut</div>
<div class="cotton">Cotton</div>
Note that the first method, which amends the HTML, is by far the better option here. Also note that forEach() relies on browser support of ES2015. If you need to support older browsers (such as IE) you'll need to use a polyFill, or a for() loop instead.
You could build a matrix (2D Array) with per line the insect and its plant. this build is easy with map().
var insects = ['#redhairy', '#bollworm', '#stalk', '#stem', '#chuija', '#diamond', '#pyrilla'];
var plants = ['.groundnut', '.cotton', '.maize', '.rice', '.wheat', '.mustard', '.sugarcane'];
var pairs = insects.map((x,i) => [x, plants[i]]);
console.log(pairs);

Categories

Resources