I'm having a for loop to get a certain task done.Now in that for loop,in the last iteration, I need to add the green-color class to all the elements which has the class checkMarks.
This is my code, currently it adds the green-color class only to the first element. Is there a way to do this without having to use another for loop inside the current for loop?
const studentLength = 24;
for(let i=0; i<studentLength; i++){
//something happens here
if(i===studentLength ){ //if last iteration
document.querySelectorAll(".checkMarks").classList.add("green-color");
}
}
You need to iterate the result of querySelectorAll and apply the class to each element:
document.querySelectorAll(".checkMarks").forEach(e => e.classList.add("green-color"));
.green-color {
color: green;
}
<div class="checkMarks">TEST</div>
<div class="checkMarks">TEST2</div>
<div class="checkMarks">TEST3</div>
<div class="checkMarks">TEST4</div>
<div class="checkMarks">TEST5</div>
<div class="checkMarks">TEST6</div>
<div class="checkMarks">TEST7</div>
You need to loop since the All in querySelectorAll returns a nodelist, but you should use forEach on the students too
I wrap in a spread [...] to handle early EDGE browsers
students.forEach(student => { /*something happens here */});
[...document.querySelectorAll(".checkMarks")].forEach(chk => chk.classList.add("green-color"));
If there is only ONE checkMark you can do document.querySelector(".checkMarks").classList.add("green-color") without the all, but vanilla JS does not support adding to a list in one go like the equivalent jQuery $(".checkMarks").addClass("green-color") would
Personally, I would use a for...of loop (ES6):
const divs = document.querySelectorAll(".checkMarks");
for (const div of divs) {
div.classList.add("green-color");
}
.green-color {
color: green;
}
<div class="checkMarks">DIV1</div>
<div class="checkMarks">DIV2</div>
<div class="checkMarks">DIV3</div>
<div class="checkMarks">DIV4</div>
<div class="checkMarks">DIV5</div>
<div class="checkMarks">DIV6</div>
<div class="checkMarks">DIV7</div>
Related
I'm very new to JS, and really don't understand a lot of it. Trying to learn as I go.
I'm trying to add some new divs to buttons to style them to look like the rest of the buttons on my site as I cant edit the plugins HTML. I've managed to successfully do this for one button. But it won't work for the other buttons. I've tried to read into it and it looks like because I am using getElementsByClassName its only selecting the first button and not the others.
So I dont know if this is right or not and correct me if it ain't. but I think I need to set up a Node loop? so that getElementsByClassName doesn't just select the first node on the page. However I got no Idea how to set up a node loop and reading about it is just confusing me more.
Can someone help and possibly explain this to me so I can make sense of it for future reference.
Thanks
This is the code I currently have, I just don't know how to make it target all elements with that class rather than just the first element with that class.
var btnSwirls = document.createElement('div');
btnSwirls.id = 'dbtb-button-swirl-wrap';
btnSwirls.className = 'dbtb-button-swirl-wrap';
document.getElementsByClassName("dbtb-add-btn-assets")[0].appendChild(btnSwirls);
const btnSwirls = document.createElement('div');
btnSwirls.id = 'dbtb-button-swirl-wrap';
btnSwirls.className = 'dbtb-button-swirl-wrap';
document.getElementsByClassName("dbtb-add-btn-assets").forEach(element => {
element.appendChild(btnSwirls);
})
learn more about forEach(): https://www.youtube.com/watch?v=SXb5LN_opbA
learn more about arrow functions: https://www.youtube.com/watch?v=h33Srr5J9nY
learn more about var, let, and const: https://www.youtube.com/watch?v=9WIJQDvt4Us
First of all, you are handling an "array" of elements. For that, you'd need a loop to iterate over the array.
you shouldn't be using this
document.getElementsByClassName("dbtb-add-btn-assets")[0] <-- because this part [0] denotes that you are targeting the first element in the array, hence 0, since all arrays start with the index 0; i.e. [0, 1, 2, 3, ...] indices
so for iterating over an array you can either use a (for loop) or a (for of) loop
for loop:
let dbtb_add_btn_assets = document.getElementsByClassName("dbtb-add-btn-assets"); //you are assigning a variable to the array;
for(let i = 0; i < dbtb_add_btn_assets.length; i++) {
var btnSwirls = document.createElement('div');
btnSwirls.id = 'dbtb-button-swirl-wrap';
btnSwirls.className = 'dbtb-button-swirl-wrap'; //create btnswirls per iteration of the loop
dbtb_add_btn_assets[i].appendChild(btnSwirls);
}
the i is the current index of the loop, the i++ part of the for loop
will automatically add 1 to itself upon executing the statement inside
the for loop and ends when i is not less than the dbtb_add_btn_assets
length. Length meaning the number of elements inside the array.
for of:
let dbtb_add_btn_assets = document.querySelectorAll('.dbtb-add-btn-assets'); //personally I'd use querySelectorAll instead of getElementsByCLassName just add . for classes # for ids
for(let dbtb of dbtb_add_btn_assets) { //name whatever variable you want to use
var btnSwirls = document.createElement('div');
btnSwirls.id = 'dbtb-button-swirl-wrap';
btnSwirls.className = 'dbtb-button-swirl-wrap'; //create btnswirls per dbtb
dbtb.appendChild(btnSwirls);
}
the for of loop takes the contents from a specified array and put them into a temporary variable, successfully giving access to the individual content/object, and then automatically iterates over each one as you manipulate it however you like inside the loop.
You need to loop over all the elements. You only access the first of many elements using [0].
There are multiple ways to do this.
Here are two ways to do it using a sample application which just toggles a class (adds/ removes a class) every two seconds.
#1 - Use querySelectorAll() and forEach()
You can use querySelectorAll() to get a NodeList of HTML elements which match the given CSS selector .someClass.
Notice that the CSS selector requires a . before a class name.
window.addEventListener("DOMContentLoaded", e => {
const allElements = document.querySelectorAll(".someClass");
// add/ remove class every 2 seconds
setInterval(() => {
// loop over all elements and add/ remove a class
allElements.forEach(element => {
element.classList.toggle("anotherClass");
});
}, 2000)
})
.someClass {
padding: 20;
background-color: black;
color: white;
margin: 5px;
}
.anotherClass {
border: 2px solid red;
}
<div class="someClass">div 1</div>
<div class="someClass">div 2</div>
<div class="someClass">div 3</div>
<div class="someClass">div 4</div>
#2 - Use getElementsByClassName() and for .. of ... loop
Alternatively you can use getElementsByClassName() which returns a HTMLCollection. You can then use a for ... of ... loop to iterate over all the elements in the collection.
Notice that here for the getElementsByClassName() call we MUST NOT use a . before the class name.
window.addEventListener("DOMContentLoaded", e => {
const allElements = document.getElementsByClassName("someClass");
// loop
setInterval(() => {
// loop over all elements and add/ remove a class
for (const element of allElements) {
element.classList.toggle("anotherClass");
}
}, 2000)
})
.someClass {
padding: 20;
background-color: black;
color: white;
margin: 5px;
}
.anotherClass {
border: 2px solid red;
}
<div class="someClass">div 1</div>
<div class="someClass">div 2</div>
<div class="someClass">div 3</div>
<div class="someClass">div 4</div>
Please note: You should use DOMContentLoaded event so you wait till the HTML document is ready before you try to access the DOM.
I have a JS for loop that iterates over all elements with a specific class, and then removes the class. However, whilst the loop works for the first element found, it then stops. I cannot see any errors, I've tried it inside a try/catch, and can't see anything else that might be causing the problem. Does anyone have any suggestions? Thanks :)
let visibleTags = document.getElementsByClassName('show');
console.log(visibleTags.length) // length is 2
for (let index = 0; index < visibleTags.length; index++) {
console.log(index); // 0
visibleTags[index].classList.remove('show'); // removes 'show' from element 0
}
// element 1 still has the 'show' class and was not touched by the loop... ?
visibleTags is a "live" DOM query - the elements within it will change as the DOM changes.
Therefore, when you remove the show class from an element, it simultaneously disappears from visibleTags, since your query was for elements with the show class. Thus, as soon as you remove the class, visibleTags.length drops to 1, and your loop will exit because the loop counter is already at 1.
There's a number of ways to work with this:
One solution to this is to run the loop backwards, so that it starts at visibleTags.length and counts back to zero. This way, you can remove the elements and the length will drop, but you'll then move onto the previous one and the loop carries on.
Another option is to run the loop as a while loop and just keep removing the first item: ie:
while (visibleTags.length) {
visibleTags[0].classList.remove('show');
}
This would be my preferred solution.
Finally, you may opt to create a non-live array of the elements that you can loop through. You probably don't need to do this, but it may be a useful option if you need to loop through the same list of elements again later on (eg maybe to restore the show class).
You shouldn't use indexes, visibleTag is a live collection and you're modifying part of the selection criteria (the show class) so the collection itself will change. Since you want to remove show from everything that has the show class, using a while loop like this is better:
let shown = document.getElementsByClassName('show');
while(shown.length > 0) {
shown[0].classList.remove('show');
}
<div>
<div class="show">1</div>
<div class="show">2</div>
<div class="show">3</div>
<div class="show">4</div>
</div>
This is because document.getElementsByClassName() is referencing the actual array of elements matching your class.
So when iterating and changing its class, the element itself does not belongs anymore to the array, thus the index becomes index-1.
A workaround, if you haven't another path to reach the object, is to rely on another class/selector to retrieve the list of elements:
let visibleTags = document.getElementsByClassName('test');
console.log(visibleTags.length) // length is 2
for (let index = 0; index < visibleTags.length; index++) {
console.log(index); // 0
visibleTags[index].classList.remove('show'); // removes 'show' from element 0
}
.test {
width: 300px;
height: 300px;
border: 1px solid #ccc;
}
.show {
background-color: red;
}
<div>
<div class="show test">1</div>
<div class="show test">2</div>
</div>
Try to use this function:
function removeClassFromElements(className) {
document
.querySelectorAll(`.${className}`)
.forEach(el => el.classList.remove(className));
}
For your case:
removeClassFromElements('show');
You could use querySelectorAll to select all the element with class show.
The Document method querySelectorAll() returns a static (not live) NodeList representing a list of the document's elements that match the specified group of selectors. Read more about this selector here
function removeClass() {
let visibleTags = document.querySelectorAll(".show");
console.log("Number of selected Elements: ", visibleTags.length); // length is 2
for (let index = 0; index < visibleTags.length; index++) {
console.log("Index: ", index); // 0
visibleTags[index].classList.remove("show"); // removes 'show' from element 0
}
}
.show {
background-color: red;
}
<button onclick="removeClass()">Remove Class</button>
<br/>
<br/>
<div>
<div class="show test">1</div>
<div class="show test">2</div>
</div>
I want to loop through a nested HTML DOM node, as shown below:
<div id="main">
<div class="nested-div-one">
<div class="nested-div-two">
<div class="nested-div-three">
</div>
</div>
</div>
<div class="nested-div-one">
<div class="nested-div-two">
<div class="nested-div-three">
</div>
</div>
</div>
</div>
How would I do this using Javascript to loop through every single one of the dividers?
I am guessing OP was not specific for DIV elements, here's a more dynamic approach:
So first you wanna get the first container, in your case it's:
var mainEl = document.getElementById('main');
Once you have that, each DOM element has a .children property with all child nodes. Since DOM is a tree object, you can also add a flag to achieve recursive behavior.
function visitChildren(el, visitor, recursive) {
for(var i = 0; i < el.children.length; i++) {
visitor(children[i]);
if(recursive)
visitChildren(children[i], visitor, recursive);
}
}
And now, let's say you want to change all div backgrounds to red:
visitChildren(mainEl, function(el) { el.style.background = 'red' });
You can use vanilla javascript for this
document.querySelectorAll('div').forEach(el => {
// el = div element
console.log(el);
});
I'm trying to only show certain divs. The way I have decided to do this is to first hide all elements that start with "page" and then only show the correct divs. Here's my (simplified) code:
<form>
<input type="text" onfocus="showfields(1);">
<input type="text" onfocus="showfields(2);">
</form>
<div class="page1 row">Some content</div>
<div class="page1 row">Some content</div>
<div class="page2 row">Some content</div>
<div class="page2 row">Some content</div>
<script>
function showfields(page){
//hide all items that have a class starting with page*
var patt1 = /^page/;
var items = document.getElementsByClassName(patt1);
console.log(items);
for(var i = 0; i < items.length; i++){
items[i].style.display = "none";
}
//now show all items that have class 'page'+page
var item = document.getElementsByClassName('page' + page);
item.style.display = '';
}
</script>
When I console.log(items); I get a blank array. I'm pretty sure the regexp is right (get all items starting with 'page').
The code I'm using is old school JS, but I'm not adverse to using jQuery. Also if there is a solution that doesn't use regexp, that's fine too as I'm new to using regexp's.
getElementsByClassName only matches on classes, not bits of classes. You can't pass a regular expression to it (well, you can, but it will be type converted to a string, which is unhelpful).
The best approach is to use multiple classes…
<div class="page page1">
i.e. This div is a page, it is also a page1.
Then you can simply document.getElementsByClassName('page').
Failing that, you can look to querySelector and a substring matching attribute selector:
document.querySelectorAll("[class^=page]")
… but that will only work if pageSomething is the first listed class name in the class attribute.
document.querySelectorAll("[class*=page]")
… but that will match class attributes which mention "page" and not just those with classes which start with "page" (i.e. it will match class="not-page".
That said, you could use the last approach and then loop over .classList to confirm if the element should match.
var potentials = document.querySelectorAll("[class*=page]");
console.log(potentials.length);
elementLoop:
for (var i = 0; i < potentials.length; i++) {
var potential = potentials[i];
console.log(potential);
classLoop:
for (var j = 0; j < potential.classList.length; j++) {
if (potential.classList[j].match(/^page/)) {
console.log("yes");
potential.style.background = "green";
continue elementLoop;
}
}
console.log("no");
potential.style.background = "red";
}
<div class="page">Yes</div>
<div class="notpage">No</div>
<div class="some page">Yes</div>
<div class="pageXXX">Yes</div>
<div class="page1">Yes</div>
<div class="some">Unmatched entirely</div>
Previous answers contain parts of the correct one, but none really gives it.
To do this, you need to combine two selectors in a single query, using the comma , separator.
The first part would be [class^="page"], which will find all the elements whose class attribute begins with page, this selector is thus not viable for elements with multiple classes, but this can be fixed by [class*=" page"] which will find all the elements whose class attribute have somewhere the string " page" (note the space at the beginning).
By combining both selectors, we have our classStartsWith selector:
document.querySelectorAll('[class^="page"],[class*=" page"]')
.forEach(el => el.style.backgroundColor = "green");
<div class="page">Yes</div>
<div class="notpage">No</div>
<div class="some page">Yes</div>
<div class="pageXXX">Yes</div>
<div class="page1">Yes</div>
<div class="some">Unmatched entirely</div>
You can use jQuery solution..
var $divs = $('div[class^="page"]');
This will get all the divs which start with classname page
$(document).ready(function () {
$("[class^=page]").show();
$("[class^=page]").hide();
});
Use this to show hide div's with specific css class it will show/hide all div's with css class mention.
Is there a way to select elements appearing after a given element using CSS selectors.
Suppose I have a DOM like this one
<body>
<span class="next">A next span</span>
<div>
<span class="next target">the target next span</span>
</div>
<div>
<span class="next">this is the one I want to select</span>
</div>
</body>
I want to select the spans of class next but only the ones that appear after span.next.target. What make this tricky is that they need not to be siblings or under the same parent node but can appear anywhere in the DOM.
Is this even possible, or am I doomed to stick with a for loop?
You can use plain javascript for this:
'use strict';
console.clear();
const spanNextNodes = document.querySelectorAll('div span.next');
let find = function*(nodes) {
let isNextTargetFound = false,
arrayOfNodes = Array.prototype.slice.call(nodes); // untill spread operator is supported [...nodes], we have to use slice method
for (let n of arrayOfNodes) {
if (isNextTargetFound) {
yield n;
} else if (n.classList.contains('target')) {
isNextTargetFound = true;
}
}
}
for (let item of find(spanNextNodes)) {
console.log(item);
}
Here is an example
It's based on ES2015 standards using function generators (yield), but if you would like to use ES5 you can add items to array and return array instead.