What I am trying to accomplish is making the clicked tab active
I have seen a lot of jQuery examples, but I'm using JavaScript ES6
I know it's an easy task but my mind is so done right now that I can't think
here is the code: https://codepen.io/revatto/pen/wvMzOqo
here is my main.js:
const tabs = document.querySelectorAll('[data-tab-target]');
const tabsContents = document.querySelectorAll('[data-tab-content]');
tabs.forEach(tab => {
tab.addEventListener('click', () => {
const target = document.querySelector(tab.dataset.tabTarget);
tabsContents.forEach(tabContent => {
tabContent.classList.remove('active');
})
target.classList.add('active');
})
})
You were only adding and removing 'active' class from tab contents you need to do the same with 'li' i.e. list too.
const tabs = document.querySelectorAll('[data-tab-target]');
const tabsContents = document.querySelectorAll('[data-tab-content]');
tabs.forEach(tab => {
tab.addEventListener('click', (e) => {
const target = document.querySelector(tab.dataset.tabTarget);
tabsContents.forEach(tabContent => {
tabContent.classList.remove('active');
})
target.classList.add('active');
//loop through 'li' items and remove 'active' class
tabs.forEach(tab => {
tab.classList.remove('active');
})
//add 'active' class to clicked 'li' item
e.target.classList.add("active");
})
})
Related
I am having trouble with adding a click event to an element that only exists if another element on the page has been clicked. This is on the Amazon website just for context. I am trying to add an overlay at the bottom of the mobile screen which allows you to select the quantity you would want and add it to basket.
In order to do this I have created my own dropdown which allows you to select the quantity (selectNumber function) - what this does is replicate the click on the original dropdown and bring up the popover element which you click in order to select the quantity. I then targeted the element in the popover which represents the quantity you would like and added a click event on that too.
The issue I am having is that on the first time I click on my dropdown and change the quantity it replicates the click on the original dropdown but doesn't update the quantity. However, if I try it again after it works perfectly. I have a feeling that it is to do with the fact the popover does not exist until the first click of the dropdown, it then fails to fire the rest of my code. I am using a Promise function (waitforElem) in order to observe that the element now exists on the page. The second time - as the element now exists - it is able to fire the rest of my code, which allows you to update the quantity, just fine. I have tried to put all my code in the same .then function but in the below I have split it into two .then's. Neither seems to work. My code is below:
function waitForElm(selector) {
return new Promise((resolve) => {
if (document.querySelector(selector)) {
return resolve(document.querySelector(selector))
}
const observer = new MutationObserver(() => {
if (document.querySelector(selector)) {
resolve(document.querySelector(selector))
observer.disconnect()
}
})
observer.observe(document.body, {
childList: true,
subtree: true,
})
})
}
selectNumber().forEach((el) =>
el.addEventListener('input', () => {
const numberSelected = Number(el.options[el.selectedIndex].value)
console.log(numberSelected)
amazonDropDown().click()
const popoverDropdown = () =>
document.querySelectorAll(
'.a-popover.a-dropdown.a-dropdown-common.a-declarative .a-popover-wrapper .a-nostyle.a-list-link .a-dropdown-item'
)
waitForElm(
'.a-popover.a-dropdown.a-dropdown-common.a-declarative .a-popover-wrapper '
)
.then(() => {
console.log('Element is ready')
})
.then(() => {
document.querySelectorAll(
'.a-popover.a-dropdown.a-dropdown-common.a-declarative .a-popover-wrapper .a-nostyle.a-list-link .a-dropdown-item'
)
const popoverArr = () => Array.from(popoverDropdown())
const popoverEl = () =>
popoverArr().filter((el) => {
const numb = () => Number(el.firstElementChild.textContent)
return numb() === numberSelected
})
console.log(popoverEl())
for (let i = 0, len = popoverEl().length; i < len; i++) {
const firstChild = (): any => popoverEl()[i].firstElementChild
console.log(firstChild())
firstChild().click()
}
})
})
)
I really hope the above makes sense and that someone can help me on this as I have been banging my head on it for a whole day. Thank you in advance.
I have a script that changes the class of some elements from "hidden" to "show" in order to add a cool "visible on scroll" effect. The issue is, after the page loads and the page has been scrolled the animations happen again when scrolling back up making it look clunky, and to make it worse if an element borders the visible/non-visible section of the screen it has a spasm that loops the animation constantly.
This is the current script :
const observer = new IntersectionObserver((entries) => {
entries.forEach((entry) => {
console.log(entry);
if (entry.isIntersecting) {
entry.target.classList.add("show");
} else {
entry.target.classList.remove("show");
}
});
});
const hiddenElements = document.querySelectorAll(".hidden");
hiddenElements.forEach((el) => observer.observe(el));
How do I make this happen just once and get rid of the junky spasms? Thanks for your help!
unobserve your elements:
const observer = new IntersectionObserver((entries) => {
entries.forEach((entry) => {
console.log(entry);
if (entry.isIntersecting) {
entry.target.classList.add("show");
//this stops the observer on the intersecting element:
observer.unobserve(entry.target);
} else {
entry.target.classList.remove("show");
}
});
});
const hiddenElements = document.querySelectorAll(".hidden");
hiddenElements.forEach((el) => observer.observe(el));
-First Way-
Here I Can't Remove The Previous Elements and add the class for The new One
linksBtnArr.forEach((item, index) => {
item.addEventListener("click", () => {
arrowArr[index].classList.toggle("active");
if (arrowArr[index].classList.contains("active")) {
menuArr[index].style.display = "block";
} else {
menuArr[index].style.display = "none";
}
});
});
-Second Way-
Here I Can remove The previous Element But i Can't use Toggle to Remove The class Form The Same Element That i clicked again
linksBtnArr.forEach((link, i) => {
link.addEventListener("click", () => {
removeActiveClasses();
arrowArr[i].classList.add("active");
});
});
// Remove The previous Menu
function removeActiveClasses() {
arrowArr.forEach((arrow) => {
arrow.classList.remove("active");
});
}
this way
linksBtnArr.forEach( (lk,_,All_lk) => {
lk.onclick =_=> {
if (lk.classList.toggle('active'))
All_lk.forEach( lx=> lx.classList.toggle('active', lk===lx) )
} })
Look At The UL Links That what i'm trying to Do !
https://mohmostafa-web.github.io/blogr-landing-page-frontend-mentor
No matter what I try, the .onclick or addEventListener 'click' will not work on my dynamically created buttons and I can't figure out why. As I was looking for solutions, I came across Event Delegation and I looked through 3 different websites and looked at the examples. I was sure this was going to solve my problem and I tried to mimic the examples but still it isn't working. I posted a question on here earlier but it was immediately removed because apparently it was too similar to another question (that was 12 years old!) but when I looked at that question they were using jQuery. I'm still a beginner in JS so I would prefer to understand how to resolve this in plain JS and I'm hoping this won't be removed.
This is my code:
document.addEventListener('DOMContentLoaded', function() {
userData();
document.querySelector('.list-group').addEventListener('click', function(e) {
if(e.target && e.target.nodeName == "BUTTON"){
console.log(e.target.id);
}
});
})
function userData() {
fetch("https://jsonplaceholder.typicode.com/users")
.then(response => response.json())
.then(users => {
const h6 = document.createElement("h6");
h6.innerText = "List of Users";
const userList = document.createElement("div");
userList.className = "list-group";
users.forEach(function(user) {
const userButton = document.createElement("button");
userButton.className = "list-group-item list-group-item-action";
userButton.id = `${user.id}`;
userButton.innerHTML = `
<strong>${user.name}</strong><br>
${user.email}<br>
${user.address.city}<br>
`;
userList.appendChild(userButton);
});
const container = document.querySelector('#response');
container.appendChild(h6);
container.insertBefore(userList, h6.nextSibling);
});
}
function userSelect(user_id) {
fetch(`https://jsonplaceholder.typicode.com/users/${user_id}`)
.then(response => response.json())
.then(user => {
console.log(user);
});
}
What I have now is a list of users and ultimately I want to be able to click on a user and bring up the full details of that user. At first I was trying to use the onclick function to redirect to the userSelect function but when that failed I looked around and found Event Delegation and still no luck. I tried to move the document.querySelector('.list-group) section down at the end of the userData function and still no luck. When I click on a button nothing shows up in console, if I use the userSelect function directly in console a user object appears. I'm at a real loss on how to get this to work. Please help!
Since function userData is making asynchronous call, the issue seems to be that you are adding the click event handler before the element with class '.list-group' got created.
You should use something like this to add click handler
document.addEventListener('DOMContentLoaded', function () {
userData().then(response => {
document.querySelector('.list-group').addEventListener('click', function (e) {
if (e.target && e.target.nodeName == "BUTTON") {
console.log(e.target.id);
}
})
});
})
Try below snippet:
document.addEventListener('DOMContentLoaded', function() {
userData().then(response => {
document.querySelector('.list-group').addEventListener('click', function(e) {
if (e.target && e.target.nodeName == "BUTTON") {
console.log(e.target.id);
}
})
});
})
function userData() {
return fetch("https://jsonplaceholder.typicode.com/users")
.then(response => response.json())
.then(users => {
const h6 = document.createElement("h6");
h6.innerText = "List of Users";
const userList = document.createElement("div");
userList.className = "list-group";
users.forEach(function(user) {
const userButton = document.createElement("button");
userButton.className = "list-group-item list-group-item-action";
userButton.id = `${user.id}`;
userButton.innerHTML = `
<strong>${user.name}</strong><br>
${user.email}<br>
${user.address.city}<br>
`;
userList.appendChild(userButton);
});
const container = document.querySelector('#response');
container.appendChild(h6);
container.insertBefore(userList, h6.nextSibling);
});
}
<div id="response">
</div>
or you can move the addEventListener code to end of userData
I found this great accordion with a very compact code but what I miss here is a function to auto close previous section when opened another one. It's probably quite easy but I'm a complete JS noob, sadly.
Original code source.
const items = document.querySelectorAll(".accordion a");
function toggleAccordion(){
this.classList.toggle('active');
this.nextElementSibling.classList.toggle('active');
}
items.forEach(item => item.addEventListener('click', toggleAccordion));
What have You tried so far ?
If You didn't, try the following logic.
Before you give an element a activeclass - loop over the rest of the elements and remove it from all of them :)
const items = document.querySelectorAll(".accordion a");
const remove = () => {
items.forEach(el => {
el.classList.remove('active');
el.nextElementSibling.classList.remove('active');
})
}
function toggleAccordion(){
remove()
this.classList.toggle('active');
this.nextElementSibling.classList.toggle('active');
}
items.forEach(item => item.addEventListener('click', toggleAccordion));
you could store the active one...
const items = document.querySelectorAll(".accordion a");
let active = null;
function toggleAccordion(){
if(active){
active.classList.toggle('active');
}
this.classList.toggle('active');
this.nextElementSibling.classList.toggle('active');
active = this;
}
items.forEach(item => item.addEventListener('click', toggleAccordion));