Raw javascript: add class to parentNode and remove from siblings - javascript

I have a click event that adds an active class to the link's parentNode. On click the active class should be removed from the siblings of the parentNode. This is the code so far:
categories = document.querySelectorAll('.categories');
for(var i = 0; i < categories.length; i++) {
categories[i].onclick = function(e) {
// ('.categoriesparent').classList.remove('active');
this.parentNode.classList.add('active');
}
I tried the line that is commented out, and that breaks the adding of the class on click. Is there an equivalent to jQuery's siblings().removeClass('active')? Thanks for any help.
Here is the DOM for this section:
<div id="nav">
<span class="categoriesparent active">
<a class="categories">Link</a>
</span>
<span class="categoriesparent">
<a class="categories">Link2</a>
</span>
</div>
UPDATE - this is the original full code snippet:
<html>
<head></head>
<body>
<div id="nav">
<span class="categoriesparent">
Link
</span>
<br><br>
<span class="categoriesparent">
Link2
</span>
</div>
<script>
categories = document.querySelectorAll('.categories');
for(var i = 0; i < categories.length; i++) {
categories[i].onclick = function(e) {
// ('.categoriesparent').classList.remove('active');
this.parentNode.classList.add('active');
}
}
</script>
</body>
</html>
UPDATE after incorporating BRK's code snippet:
<html>
<head></head>
<body>
<div id="nav">
<span class="categoriesparent active">
Link
</span>
<br><br>
<span class="categoriesparent">
Link2
</span>
</div>
<script>
categories = document.querySelectorAll('.categories');
categories.forEach(function(cat, index) {
cat.onclick = function(e) {
document.querySelector('.categoriesparent.active').classList.remove('active');
this.parentNode.classList.add('active');
}
});
</script>
</body>
</html>

Can you try doing like this? It Might help!
categories = document.querySelectorAll('.categories');
for(let i = 0; i < categories.length; i++) {
categories[i].onclick = () => {
// ('.categoriesparent').classList.remove('active');
this.parentNode.classList.add('active');
}

You can first remove the active class from all the siblings of parent (and the parent itself) and then add to the parent
for (var i = 0; i < categories.length; i++) {
categories[i].onclick = function(e) {
[ ...getSiblingsAndMe(this.parentNode) ].forEach( el => el.classList.remove( 'active' ) );
this.parentNode.classList.add('active');
}
}
function getSiblingsAndMe( el )
{
return el.parentNode.children;
}
Demo
categories = document.querySelectorAll('.categories');
for (var i = 0; i < categories.length; i++) {
categories[i].onclick = function(e) {
[ ...getSiblingsAndMe(this.parentNode) ].forEach( el => el.classList.remove( 'active' ) );
this.parentNode.classList.add('active');
}
}
function getSiblingsAndMe( el )
{
return el.parentNode.children;
}
.categoriesparent
{
display:block;
}
.active
{
background-color:pink;
}
<div id="nav">
<span class="categoriesparent active">
<a class="categories">Link</a>
</span>
<span class="categoriesparent">
<a class="categories">Link2</a>
</span>
</div>

Inside the onclick handler you can again use document.querySelector('.categoriesparent.active') . This will return the first matched element and then use classList.remove to remove the active class from it.
this.parentNode refers to the element in context
var categories = document.querySelectorAll('.categories');
categories.forEach(function(cat, index) {
cat.onclick = function(e) {
document.querySelector('.categoriesparent.active').classList.remove('active')
this.parentNode.classList.add('active');
}
})
.active {
color: red;
}
<div id="nav">
<span class="categoriesparent active">
<a class="categories">Link</a>
</span>
<span class="categoriesparent">
<a class="categories">Link2</a>
</span>
</div>
Update from Jamie:
I used your suggestion like so:
<html>
<head></head>
<body>
<div id="nav">
<span class="categoriesparent active">
Link
</span>
<br><br>
<span class="categoriesparent">
Link2
</span>
</div>
<script>
categories = document.querySelectorAll('.categories');
categories.forEach(function(cat, index) {
cat.onclick = function(e) {
document.querySelector('.categoriesparent.active').classList.remove('active');
this.parentNode.classList.add('active');
}
});
</script>
</body>
</html>
I get an "Uncaught TypeError: undefined is not a function" error. Thanks for your input!

Related

Why does the addEventListener property does not work on a dynamic HTMLcollection object?

I am working on a chrome extension and trying to add an event listener to a getElementsByClassName variable, in which elements are added dynamically using template strings.
I have tried a lot of changes in the code, but the code doesn't work.
The code is supposed to delete the targeted element from the array "recipeList", storing the array in localStorage and then render the updated array "recipeList" using template string to the HTML code again.
DELETE BUTTON Function
let delBtn = document.getElementsByClassName("del-btn");
for(let i = 0; i < delBtn.length; i++) {
delBtn[i].addEventListener("click", function() {
recipeList.splice(i, 1);
localStorage.setItem("recipeList", JSON.stringify(recipeList));
recipeList = JSON.parse(localStorage.getItem("recipeList"));
render(recipeList);
if(!recipeList.length) {
tabBtn.style.width = "100%";
delAllBtn.style.display = "none";
}
});
}
RENDER CODE
function render(list) {
let recipeURL = "";
for(let i = 0; i < list.length; i++) {
recipeURL += `
<div class="mb-2 row">
<div class="col-1 num-btn">
${i+1}
</div>
<div class="col-10">
<div class="recipe-url">
<a target="_blank" href="${list[i]}">
${list[i]}
</a>
</div>
</div>
<div class="col-1 del-btn">
<a href="#">
<i class="bi bi-trash-fill"></i>
</a>
</div>
</div>
`
}
urlList.innerHTML = recipeURL;
console.log(delBtn);
}
When you render, you create new .del-btn which are not included in your first let delBtn = document.getElementsByClassName("del-btn");.
Each time you create new .del-btn, you should also add a new listener.
function render(list) {
let recipeURL = "";
for(let i = 0; i < list.length; i++) {
recipeURL += `
<div class="mb-2 row">
<div class="col-1 num-btn">
${i+1}
</div>
<div class="col-10">
<div class="recipe-url">
<a target="_blank" href="${list[i]}">
${list[i]}
</a>
</div>
</div>
<div class="col-1 del-btn">
<a href="#">
<i class="bi bi-trash-fill"></i>
</a>
</div>
</div>
`
}
urlList.innerHTML = recipeURL;
console.log(delBtn);
Array.from(urlList.querySelectorAll('.del-btn')).forEach((btn, i) => {
btn.addEventListener('click', () => console.log('.del-btn index: ' + i))
})
}
}

Remove class if id's the correct ID

Looking to remove a class if a certain button is clicked.
<div class="slide-container">
<section class="about" id="slide-0">
<div class="menu-total">
<nav class="nav">
<button class="nav_link home" onclick="slideTo('slide-2')">HOME</button>
<button class="nav_link about" onclick="slideTo('slide-0')">ABOUT</button>
<button class="nav_link fun-stuff" onclick="slideTo('slide-1')">FUN STUFF</button>
<button class="nav_link professional" onclick="slideTo('slide-3')">PROFESSIONAL</button>
<button class="nav_link contact" onclick="slideTo('slide-4')">CONTACT</button>
</nav>
<div class="hamburger">
<span class="hamburger__patty"></span>
<span class="hamburger__patty"></span>
<span class="hamburger__patty"></span>
</div>
</div>
The one I want to remove the class on is the HOME button. So "slideTo('slide-2)". If it's clicked on the others then the class is kept. I believe someone is either wrong with my loop or not getting the ID correctly of the items/
function slideTo(slideId) {
const slide = document.getElementById(slideId);
slide.scrollIntoView({
behavior: 'smooth'
})
// above this line works fine
let nonHome = document.querySelectorAll('.slide-container section');
let nonHomeID = document.getElementById('slide-2');
var i;
setTimeout(function(){
for (i=0; i < nonHome.length; i++ ){
// i believe it's somewhere here it is wrong
if (nonHome[i].id != nonHomeID){
nonHome[i].classList.add("nav-visibility");
} else{
nonHomeID.classList.remove("nav-visibility");
}
}
}, 1000)
}
If you can use jquery library, you can write in the HTML:
<button class="nav_link" data-value="home">HOME</button>
...
and then in the JS code:
$(".nav_link").on("click", function() {
var valueClicked = $(this).data("value"); // Get the data-value clicked
$(".nav_link").each(function() { // Loop through all elements of the class 'nav-link'
var v = $(this).data("value");
if (v == valueClicked) {
$(this).removeClass("nav-visibility");
} else {
$(this).addClass("nav-visibility");
}
)
}
Not much simpler, but the HTML is cleaner.
Simpler version if it is not required to browse through all buttons at each button click:
$(".nav_link").on("click", function() {
var valueClicked = $(this).data("value"); // The value of the button clicked by the user
if (valueClicked == "home") {
$(this).removeClass("nav-visibility");
console.log('remove')
} else { $(this).addClass("nav-visibility");
console.log('add')
}
});

How to change style on other element using Javascript with nodelist

Need help with using js script.
<ul class="producers-links">
<li class="section_All active-producer">A-Z</li>
<li class="section_0-9">0-9</li>
<li class="section_A">A</li>
<li class="section_B">B</li>
<li class="section_C">C</li>
</ul>
And
<div class="producers-list">
<div class="producers-container" id="producers-0-9">
<div class="break-producers">0-9</div>
</div>
<div class="producers-container" id="producers-A">
<div class="break-producers">A</div>
Producer 1
</div>
<div class="producers-container" id="producers-B">
<div class="break-producers">B</div>
Producer 2
</div>
<div class="producers-container" id="producers-C">
<div class="break-producers">C</div>
Producer 3
</div>
</div>
How to make js script that will allow user click on list element then all divs from producers-list will get display:none without this one which was clicked at list.
var producersList = document.querySelectorAll('ul.producers-links>li');
var producersLists = document.querySelectorAll('div.producers-list>div.producers-container');
for (var i = 0; i < producersList.length; i++) {
producersList[i].addEventListener('click', function () {
document.querySelector('.active-producer').classList.remove('active-producer');
this.classList.add('active-producer');
var index = 0,
length = producersList.length;
for (; index < length; index++) {
producersLists[index].style.display = "none";
}
});
}
This allow me to hide all elements from producers-container but i don't know how to show only one element clicked before at list.
First of all you should use classes instead of id in the second list in order to have the ability to add more procedures in the future
try this:
<ul class="producers-links">
<li id="section_All" class="active-producer">A-Z</li>
<li id="section_0-9">0-9</li>
<li id="section_A">A</li>
<li id="section_B">B</li>
<li id="section_C">C</li>
</ul>
<div class="producers-list">
<div class="producers-container section_0-9 section_All">
<div class="break-producers">0-9</div>
</div>
<div class="producers-container section_A section_All">
<div class="break-producers">A</div>
Producer 1
</div>
<div class="producers-container section_B section_All">
<div class="break-producers">B</div>
Producer 2
</div>
<div class="producers-container section_C section_All">
<div class="break-producers">C</div>
Producer 3
</div>
</div>
var producersList = document.querySelectorAll('ul.producers-links > li');
var producersLists = document.querySelectorAll('.producers-container');
for (var i = 0; i < producersList.length; i++) {
producersList[i].addEventListener('click', function () {
document.querySelector('.active-producer').classList.remove('active-producer');
this.classList.add('active-producer');
for (var index = 0; index < producersLists.length ; index++) {
var currElement = producersLists[index];
var hide = !currElement.classList.contains(this.id);
producersLists[index].style.display = hide? "none" : "block";
}
});
}
On click, you can sequentially:
- hide all
- select the one having the same end of id than the textContent of the clicked item (or select all if text is A-Z)
var producersList = document.querySelectorAll('ul.producers-links>li');
var producersLists = document.querySelectorAll('div.producers-list>div.producers-container');
// add eventlistener...
producersList.forEach(x => {
x.addEventListener("click", x => {
hideAll();
document.querySelector('.active-producer').classList.remove('active-producer');
x.target.classList.add('active-producer');
const txt = x.target.textContent;
selectForText(txt);
});
});
// hide/show all...
function hideAll(bShow) {
const cl = bShow === true?"block":"none";
producersLists.forEach(x => x.style.display = cl);
}
// select for text...
function selectForText(txt) {
if(txt === "A-Z") {
// select all...
hideAll(true);
return;
}
// the [...nodelist] part allows to 'cast' to proper array, and to have access to find() function...
const found = [...producersLists].find(q => q.id.split("producers-")[1] === txt);
if(found) {
found.style.display = "block";
}
else {
// ???
}
}
.active-producer {
color: #19f;
}
<ul class="producers-links">
<li class="section_All active-producer">A-Z</li>
<li class="section_0-9">0-9</li>
<li class="section_A">A</li>
<li class="section_B">B</li>
<li class="section_C">C</li>
</ul>
And
<div class="producers-list">
<div class="producers-container" id="producers-0-9">
<div class="break-producers">0-9</div>
</div>
<div class="producers-container" id="producers-A">
<div class="break-producers">A</div>
Producer 1
</div>
<div class="producers-container" id="producers-B">
<div class="break-producers">B</div>
Producer 2
</div>
<div class="producers-container" id="producers-C">
<div class="break-producers">C</div>
Producer 3
</div>
</div>

JavaScript. Sorting ements by value of children

Need a bit of help with JS. I'm trying to figure out how to sort a pile of div's, using values of their children elements. I'v found a solution here on stack and tried to modify it a bit but with no luck so far.
Please, give me some advise. Thank you
The idea is to sort div.person according to their "age" element.
<!DOCTYPE html>
<html>
<head>
<title>Sort list items alphabetically with Javascript</title>
<script type="text/javascript">
function sortUnorderedList(div, sortDescending) {
if(typeof div == "number")
div = document.getElementById(div);
var lis = div.getElementsByClassName("person");
var vals = [];
for(var i = 0, l = lis.length; i < l; i++)
vals.push(lis[i].innerHTML);
vals.sort();
if(sortDescending)
vals.reverse();
for(var i = 0, l = lis.length; i < l; i++)
lis[i].innerHTML = vals[i];
}
window.onload = function() {
var desc = false;
document.getElementById("test").onclick = function() {
sortUnorderedList("list", desc);
desc = !desc;
return false;
}
}
</script>
</head>
<body>
<input type="button" id="test" value="Sort List"/>
<div id="list">
<div class="person">
<div>Jack</div>
<div>Plumber</div>
<div class="info">
<span class="age">24</span>
<span class="hair-color">Blonde</span>
</div>
</div>
<div class="person">
<div>Jill</div>
<div>Actress</div>
<div class="info">
<span class="age">18</span>
<span class="hair-color">Gray</span>
</div>
</div>
<div class="person">
<div>John</div>
<div>Driver</div>
<div class="info">
<span class="age">37</span>
<span class="hair-color">Brown</span>
</div>
</div>
</ul>
</body>
</html>
I changed the code a bit to make it more readable for myself.
First I get all the htmlNode by class name "person", though this will return a htmlCollection which is array-ish but not an array.
Therefore I convert it to an array on the next line so I can perform array-methods on it like 'sort'.
You can swap out the different compareFunctions I wrote for different kind of sorting.
After sorting I empty the existing content of the list-element, and fill it up again with for-loop.
function sortUnorderedList(list, sortDescending) {
var htmlCollection = list.getElementsByClassName("person"),
elements = [].slice.call(htmlCollection); //convert htmlCollection to array
//sort by ...
//elements.sort(compareNames);
//elements.sort(compareJobs);
elements.sort(compareAges);
if (sortDescending) elements.reverse();
list.innerHtml = ''; //remove current contents
for (var i = 0; i < elements.length; i++) {
list.appendChild(elements[i]); //add them again in different order
}
function compareNames(el1, el2) {
//innerText of the first child of each element is the name
if (el1.children[0].innerText < el2.children[0].innerText) return -1;
if (el1.children[0].innerText > el2.children[0].innerText) return 1;
return 0;
}
function compareJobs(el1, el2) {
//innerText of the second child of each element is the job
if (el1.children[1].innerText < el2.children[1].innerText) return -1;
if (el1.children[1].innerText > el2.children[1].innerText) return 1;
return 0;
}
function compareAges(el1, el2) {
var age1 = parseInt(el1.children[2].children[0].innerText),
age2 = parseInt(el2.children[2].children[0].innerText);
if(isNaN(age1))age1=-1;
if(isNaN(age2))age2=-1;
return age1 - age2;
}
}
window.onload = function() {
var desc = false;
document.getElementById("test").onclick = function() {
sortUnorderedList(document.getElementById('list'), desc);
desc = !desc;
return false;
};
};
<!DOCTYPE html>
<html>
<head>
<title>Sort list items alphabetically with Javascript</title>
</head>
<body>
<input type="button" id="test" value="Sort List" />
<div id="list">
<div class="person">
<div>Jack</div>
<div>Plumber</div>
<div class="info">
<span class="age">24</span>
<span class="hair-color">Blonde</span>
</div>
</div>
<div class="person">
<div>Jill</div>
<div>Actress</div>
<div class="info">
<span class="age">0</span>
<span class="hair-color">Gray</span>
</div>
</div>
<div class="person">
<div>John</div>
<div>Driver</div>
<div class="info">
<span class="age"></span>
<span class="hair-color">Brown</span>
</div>
</div>
</ul>
</body>
</html>

toggle div while hiding other divs

I have a side nav on the left hand side with different types of surgeries. They are all in an ul.
When you click on a surgery (ul li), a div will show up on the right hand side, displaying FAQ's for that specific surgery. Clicking on another link in the ul will hide the currently open div and display the one you've just clicked on.
I've managed to do this, but I would also like to toggle the visibility of the FAQ div when I click the same ul li link again.
<html>
<head>
<style>
#popup div { display:none; }
#popup div.show { display:block; }
ul#links li { cursor:pointer;}
</style>
</head>
<body>
<div class="sidenav">
<ul id="links">
<li id="armlift">Arm Lift</li>
<li id="liposuction">Liposuction</li>
<li id="tummytuck">Tummy Tuck</li>
<li id="postgastric">Post-Gastric Bypass Surgery</li>
</ul>
</div>
<div id="popup">
<div id="a_armlift">
<span class="faq_header">Arm Lift</span>
<p class="treatment_question">What is a an Arm Lift?</p>
<div class="treatment_answer">This surgery removes excess...</div>
<p class="treatment_question">What should I know?</p>
<div class="treatment_answer">An incision is made...</div>
</div>
<div id="a_liposuction">
<span class="faq_header">Liposuction Lift</span>
<p class="treatment_question">What is a Liposuction?</p>
<div class="treatment_answer">Liposuction is the removal...</div>
<p class="treatment_question">What should I know?</p>
<div class="treatment_answer">Ideal candidates for...</div>
</div>
<div id="a_tummytuck">
<span class="faq_header">Tummy Tuck</span>
<p class="treatment_question">What is a Tummy Tuck?</p>
<div class="treatment_answer">A tummy tuck tightens...</div>
<p class="treatment_question">What is a Mini Tummy Tuck?</p>
<div class="treatment_answer">A mini-tuck is a...</div>
</div>
<div id="a_postgastric">
<span class="faq_header">Post-Gastric Bypass Surgery</span>
<p class="treatment_question">What is a Post-Gastric Bypass Surgery?</p>
<div class="treatment_answer">Gastric bypass surgery removes...</div>
<p class="treatment_question">What should I know?</p>
<div class="treatment_answer">Each patient has...</div>
</div>
</div>
<script type="text/javascript">
var links_ul = document.getElementById('links');
for (var i = 0; i < links_ul.children.length; i++) {
links_ul.children[i].onclick = function(ev) {
s = document.getElementById('a_' + this.id);
popup = document.getElementsByClassName('show');
for (var j = 0; j < popup.length; j++) {
popup[j].className = '';
}
s.className = 'show';
};
}
</script>
</body>
</html>
I would recommend looking into doing this differently. There are many plugins available that do this sort of thing.
But, here's the answer to your question: http://jsfiddle.net/6Vku8/1/
for (var i = 0; i < links_ul.children.length; i++) {
links_ul.children[i].onclick = function(ev) {
s = document.getElementById('a_' + this.id);
popup = document.getElementsByClassName('show');
var shown = s.className == 'show';
for (var j = 0; j < popup.length; j++) {
popup[j].className = '';
}
s.className = shown ? '' : 'show';
};
}​
You need to find out whether or not the div is "shown" before hiding all of the divs.

Categories