Getting 'For Loop' to work the same as 'forEach' - javascript

This one uses For Loop
After you click on another button they don’t change back to the play button, and instead they stay on pause. The audio pauses without an issue, it’s just the buttons that don’t change back for some reason.
https://jsfiddle.net/pezuLqvo/85/
function hideAllButtons(button) {
const buttons = button.querySelectorAll(".play, .pause, .speaker");
for (let i = 0; i < buttons.length; i += 1) {
hide(buttons[i]);
}
}
function pauseAllButtons(buttons) {
for (let i = 0; i < buttons.length; i += 1) {
if (isPlaying(buttons[i])) {
showPlayButton(buttons[i]);
}
}
}
function showPauseButton(button) {
const pause = getPause(button);
pauseAllButtons(button);
hideAllButtons(button);
show(pause);
button.classList.add("active");
}
Looking at how this one was set up, are you able to determine what I would change in the above code to fix that issue?
This one uses forEach
https://jsfiddle.net/pezuLqvo/84/
function hideAllButtons(button) {
button.querySelectorAll(".play, .pause, .speaker").forEach(hide);
}
function pauseAllButtons() {
const buttons = document.querySelectorAll(".playButton");
buttons.forEach(function hidePause(button) {
if (isPlaying(button)) {
showPlayButton(button);
}
});
}
function showPauseButton(button) {
const pause = getPause(button);
pauseAllButtons();
hideAllButtons(button);
show(pause);
button.classList.add("active");
}

This was the answer:
https://jsfiddle.net/pezuLqvo/93/
function pauseAllButtons() {
const buttons = document.querySelectorAll(".playButton");
for (let i = 0; i < buttons.length; i += 1) {
if (isPlaying(buttons[i])) {
showPlayButton(buttons[i]);
}
}
}

Related

removeEventListener only removing single instance

I've been having issues where when I try to remove an event from the buttons it seems to only be removing the event for the one-button even though I have looped through the buttons and removed the event.
thank you.
function ChangeQuestions() {
let currentQuestion = getQuestion(); //another function to get the question from an array - returns an object with questions, answers and correctAnswer
const correctAnswer = currentQuestion.correct;
console.log(currentQuestion);
if (questionsArray.length === 0) {
//If the array is empty push the questions again
questionsArray.push(firstQuestion, secondQuestion, thirdQuestion);
}
document.querySelector('.question-header').textContent = currentQuestion.questions;
for (let i = 1; i < 4; i++) {
document.querySelector('.btn-Ans-' + i).textContent = currentQuestion.answers[i - 1];
document.querySelector('.btn-Ans-' + i).addEventListener('click', function checkAns(e) {
if (e.target.innerHTML === correctAnswer) {
score++;
console.log(score);
removeEvent('click', checkAns);
ChangeQuestions();
} else {
console.log(score);
removeEvent('click', checkAns);
ChangeQuestions();
}
});
}
}
function removeEvent(event, func) {
for (let i = 1; i < 4; i++) {
document.querySelector('.btn-Ans-' + i).removeEventListener(event, func);
}
}
With
for (let i = 1; i < 4; i++) {
document.querySelector('.btn-Ans-' + i).addEventListener('click', function checkAns(e) {
A new checkAns function is created inside every iteration of the loop, and removeEventListener must be passed the exact same function that addEventListener was called with. Since the different loop iterations have different functions passed into their respective addEventListener calls, the removeEvent function appears to only affect the element that was clicked, and none of the rest.
Here's a more minimal example:
const fns = [];
for (let i = 0; i < 2; i++) {
const foo = () => console.log('foo');
fns.push(foo);
window.addEventListener('click', foo);
}
// Not the same function:
console.log(fns[0] === fns[1]);
I'd add just a single listener to the container instead, and use event delegation to check which element was clicked on:
btnContainer.addEventListener('click', function handler(e) {
if (!e.target.matches('[class^="btn-Ans"]')) {
return;
}
btnContainer.removeEventListener('click', handler);
if (e.target.innerHTML === correctAnswer) {
score++;
}
console.log(score);
ChangeQuestions();
});
where btnContainer is a container for your btn-Ans-s.

Dot pagination with pure JavaScript

i m very much new to programming with JavaScript and would love to gain more experience, my problem isn't actually a problem, it more like of optimizing a code, I've been working on making what so called a "pagination" a dot navigation. you can find my code example in this code pen https://codepen.io/Tarek-Chentouf/pen/ajqXpW . My code goes as follow:
"use strict";
var buttons = document.querySelectorAll('.button__outline');
function reset() {
for (let i = 0; i < buttons.length; i++) {
buttons[i].classList.remove('active');
}
}
function addActive() {
for (let i = 0; i < buttons.length; i++) {
buttons[i].addEventListener('click', function() {
if (i == 0) {
reset();
buttons[0].classList.add('active');
}
if (i == 1) {
reset();
buttons[1].classList.add('active');
}
if (i == 2) {
reset();
buttons[2].classList.add('active');
}
if (i == 3) {
reset();
buttons[3].classList.add('active');
}
});
}
}
addActive();
my Question goes as follow is there a better way to achieve the same result without having to repeat the if statement?.
Thank you all in advance.
For the general case, you could simply access buttons[i] instead of if (i == 0) ... buttons[0] ... if (i == 1) ... buttons[1] ...:
function addActive() {
for (let i = 0; i < buttons.length; i++) {
buttons[i].addEventListener('click', function() {
reset();
buttons[i].classList.add('active');
});
}
}
But you could make the code cleaner and DRY-er with a forEach - instead of accessing an index of the buttons collection, abstract the button being iterated over into a variable of its own:
function addActive() {
buttons.forEach(button => {
button.addEventListener('click', () => {
reset();
button.classList.add('active');
});
});
}
Or, as Patrick Roberts suggested, you might move all the classList changes into the reset function and use event delegation on the container (that way you only need one listener, rather than many):
document.querySelector('.container').addEventListener('click', ({ target }) => {
if (!target.matches('.button__outline')) return;
reset(target);
});
const buttons = document.querySelectorAll('.button__outline');
function reset(showButton) {
buttons.forEach(button => {
button.classList.remove('active');
})
showButton.classList.add('active');
}
reset(buttons[0]);

Is only able to use the last element of the antZipCodes array when using the pestDisplay function

I wrote some JS that involves targeting the DOM, specifically a text input form. It only changes the display for the last element of the array when the user types in 95827. If the user types in 95828 or 95604, the display isn't filtered properly.
Here's a link to the full code.
I was told it may have to do with the removeDisplay function and how it's iterating through display (divs), but still can't manage to fix it.
Still new to DOM Manipulation.
var display = document.querySelectorAll(".display");
var zipCodeSearch = document.querySelector("#site-search");
var ants = document.querySelector("#ants");
const antZipCodes = [95828, 95604, 95827];
zipCodeSearch.addEventListener('change', function(e) {
// for(var i = 0; i < antZipCode.length; i++) {
// if(antZipCode[i] === Number(e.target.value)) {
// removeDisplay(displayNone);
// addDisplay(ants);
// }
// }
pestDisplay(antZipCodes, ants, display, e);
});
function removeDisplay(items) {
for(var i = 0; i < items.length; i++) {
items[i].style.display = "none";
}
}
function addDisplay(item) {
item.style.display = "block";
}
function displayAll(items) {
for(var i = 0; i < items.length; i++) {
items[i].style.display = "block";
}
}
function pestDisplay(arr, id, display, e) {
for(var i = 0; i < arr.length; i++) {
if(arr[i] === Number(e.target.value)) {
removeDisplay(display);
addDisplay(id);
} else {
displayAll(display);
}
}
}

todolist double click to add class?

I am making a todo list... When the task is finished i need to be able to click it and then add a class to that item... It works but I have to double click.. Any suggestions?
list.onclick = function() {
var list = document.getElementsByTagName('li');
for (var i = 0; i < list.length; i++) {
list[i].onclick = function() {
if (!this.classList.contains("checked") || this.classList.contains("checked")) {
this.classList.add("checked");
} else {
this.classList.remove("checked");
}
}
}
}
As I understand purpose of this function is to check or uncheck list element each time user clicks on it. For this purpose, first of all we need to identify if 'class' exists or not and remove it. In other cases just add that 'class' to classList attribute.
list.onclick = function()
{
var list = document.getElementsByTagName('li');
for (var i = 0; i < list.length; i++)
{
list[i].onclick = function()
{
if (this.classList.contains("checked")
{
this.classList.remove("checked");
}
else
{
this.classList.add("checked");
}
}
}
}

matchMedia / Javascript Media Query Issue

I created this function to handle the toggle for my mobile nav.
const mobileNav = document.getElementById('mobile-nav');
let tabs = document.getElementsByClassName('nav_tabs');
//nav toggle control
mobileNav.onclick = (event) => {
event.preventDefault();
for(let i = 0; i < tabs.length; i++) {
if(tabs[i].style.display === "block"){
tabs[i].style.display = "none";
} else {
tabs[i].style.display = "block";
}
}
};
It's working on great on mobile. The problem is when I resize, the toggle is still set to display none and the toggled menu options are not visible. I have tried using this JS Media Query to reset the display block based on a min-width of 786px but it is not reseting the menu.
// media query event handler
if (matchMedia) {
const dsktp = window.matchMedia("(min-width: 768px)");
dsktp.addListener(WidthChange);
WidthChange(dsktp);
}
function WidthChange(elem) {
for(let i = 0; i < elem.length; i++) {
tabs[i].style.display = "block";
}
}
Here's a codepen of the problem.
Your code does not work because of this code (pay attention to the comments):
if (matchMedia) {
const dsktp = window.matchMedia("(min-width: 768px)");
dsktp.addListener(WidthChange); // <-- add function as handler
WidthChange(dsktp);
}
function WidthChange(elem) { // elem argument it is not the dom elements here
for(let i = 0; i < elem.length; i++) {
tabs[i].style.display = "block";
}
}
So you should rewrite your code this way:
if (matchMedia) {
const dsktp = window.matchMedia("(min-width: 768px)");
dsktp.addListener(WidthChange);
WidthChange(dsktp);
}
function WidthChange(mediaQueryEvent) {
for(let i = 0; i < tabs.length; i++) {
tabs[i].style.display = mediaQueryEvent.matches ? "block" : "none";
}
}
Check my fork of your pen.

Categories