I have an array with the button objects.
When it is clicked, it gets the "button-active" tag and
when it is clicked again, it removes the "button-active" class
I want to removeEventListener when flag is true
let flag = false;
const buttonActive = () => {
arr.forEach(e => {
e.addEventListener("click", function eventListener(event){
event.preventDefault()
if(checkClass(e, "button-active")) removeClass(e, "button-active")
else addClass(e, "button-active")
})
})
}
button.addEventListener("click", (event) => {
event.preventDefault()
let input = document.createElement('div')
input.className = "info"
input.innerHTML += `...(some html with buttons that have weekday class)...`
info.appendChild(input)
flag = true;
arr = document.querySelectorAll('.weekday')
buttonActive()
})
I thought of a way of putting the eventListener function outside the buttonActive function, but the eventListener function uses the variable e.
How should I solve this problem?
simple~
let flag = false;
let cbs = [];
const buttonActive = (arr, active) => {
if (active) {
cbs = arr.map(e => {
const cb = (event) => {
event.preventDefault()
if(checkClass(e, "button-active")) removeClass(e, "button-active")
else addClass(e, "button-active")
}
e.addEventListener("click", cb)
return cb;
});
} else {
for (int i = 0; i < arr.length; ++i) {
arr[i].removeEventListener('click', cbs[i]);
}
}
}
// I guess this is the trigger button
button.addEventListener("click", (event) => {
event.preventDefault()
let input = document.createElement('div')
input.className = "info"
input.innerHTML += `...(some html with buttons that have weekday class)...`
info.appendChild(input)
flag = !!flag;
arr = document.querySelectorAll('.weekday')
buttonActive(arr, flag)
})
Related
I'm having an issue with localStorage. Whenever the X (e.target.tagName === 'I') icon is clicked to remove one single item from localStorage, all of localStorage is cleared. What's going on? Any suggestions will be appreciated.
Javascript File
const todoInput = document.querySelector('#todo-item')
const todoList = document.querySelector('.todo-list')
const form = document.querySelector('form')
const todos = []
const savedTodos = JSON.parse(localStorage.getItem('todos')) || []
for (let i = 0; i < savedTodos.length; i++) {
let newTodo = document.createElement('LI')
newTodo.innerText = savedTodos[i]
newTodo.classList.add('todo-item')
const deleteBtn = document.createElement('I')
deleteBtn.classList.add('fa-sharp', 'fa-solid', 'fa-square-xmark')
newTodo.append(deleteBtn)
todoList.appendChild(newTodo)
}
form.addEventListener('submit', function (e) {
// e.preventDefault()
const newTodo = document.createElement('li')
newTodo.innerText = todoInput.value
newTodo.classList.add('todo-item')
const deleteBtn = document.createElement('i')
deleteBtn.classList.add('fa-sharp', 'fa-solid', 'fa-square-xmark')
newTodo.append(deleteBtn)
todoList.appendChild(newTodo)
todoInput.value = ''
todos.push(newTodo.innerText)
console.log(newTodo.innerText)
localStorage.setItem('todos', JSON.stringify(todos))
})
function removeFromStorage(itemToRemove) {
for (let i = 0; i < todos.length; i++) {
if (todos[i] === itemToRemove) {
todos.splice(i, 1)
}
}
localStorage.setItem('todos', JSON.stringify(todos))
}
todoList.addEventListener('click', function (e) {
// When this line of code is run all local storage is deleted
if (e.target.tagName === 'I') {
e.target.parentElement.remove()
removeFromStorage(e.target.parentElement.innerText)
} else if (e.target.tagName === 'LI') {
e.target.classList.toggle('todo-complete')
}
console.log(e.target.innerText)
})
I have a dynamic dropdown list that items inside can be clicked(will do something)
When focus, it will show some suggestion items
When blur, it will remove the items inside the list node
The problem is when the blur/focusout event trigger, my element removed, and the click event do not trigger.
Here is the minimal reproduce in codepen: https://codepen.io/XiaoChu/pen/NWYJdvj?editors=1111
HTML:
<div class="search-wrapper">
<input id="keyword-search" type="text" placeholder="Type some keywords...">
<ul id="result-list"></ul>
</div>
JS:
const inputDom = document.getElementById('keyword-search');
const listDom = document.getElementById('result-list');
inputDom.addEventListener('focus', () => {
for(let i = 0; i < 5; i++) {
const li = document.createElement('li');
li.innerText = `item-${i + 1}`;
li.classList.add('item');
li.addEventListener('click', (e) => {
alert(e.currentTarget.innerText);
});
listDom.appendChild(li);
}
});
inputDom.addEventListener('blur', () => {
listDom.innerHTML = '';
});
I have tried setTimeout to wait some milliseconds, and it works. But I want to know is this solution a good way?
You can use a flag, mouseover and mouseleave like this
const inputDom = document.getElementById('keyword-search');
const listDom = document.getElementById('result-list');
let isInList;
listDom.addEventListener('mouseover', () => {
isInList = true;
console.log(isInList);
})
listDom.addEventListener('mouseleave', () => {
isInList = false;
console.log(isInList)
})
inputDom.addEventListener('focus', () => {
for(let i = 0; i < 5; i++) {
const li = document.createElement('li');
li.innerText = `item-${i + 1}`;
li.classList.add('item');
li.addEventListener('click', (e) => {
alert(e.currentTarget.innerText);
});
listDom.appendChild(li);
}
});
inputDom.addEventListener('blur', () => {
if(!isInList){
listDom.innerHTML = '';
}
});
I have two buttons (each of them has a value which matches the id of a dish in the food menu) with two EventListeners. One button for adding something to a shopping cart and one button to remove something from the shopping cart. My problem is, that i cant figure out how to pass the Eventlistener to the correct class function. This is my code so far:
class Cart {
constructor() {
this.inhalt = [];
}
add(item) {
this.inhalt.push(item);
console.log(this.inhalt)
}
remove(item) {
for (let i = 0; i < this.inhalt.length; i++) {
if (this.inhalt[i].id === item.id) {
this.inhalt.splice(i, 1);
console.log(this.inhalt)
}
}
}
sum() {
let s = null;
this.inhalt.price.forEach(element => {
s += element
});
console.log(s)
}
}
const myCart = new Cart();
function getItem(type) {
let item = null;
for (let i=0; i<speisekarte.length; i++) {
if (speisekarte[i].id === this.value) {
item = speisekarte[i];
break;
}
}
if (type == "plus") {myCart.add(item)}
else if (type == "minus") {myCart.remove(item)};
}
let plus = document.querySelectorAll(".kaufen");
plus.forEach(el =>{
let type = "plus"; el.addEventListener("click", getItem(type));
});
let minus = document.querySelectorAll(".zurück");
minus.forEach(el =>{
let type = "minus"; el.addEventListener("click", getItem(type));
});
You shouldn't be calling the functions when registering the event listeners.
Instead of:
let plus = document.querySelectorAll(".kaufen");
plus.forEach(el =>{
let type = "plus"; el.addEventListener("click", getItem(type));
});
let minus = document.querySelectorAll(".zurück");
minus.forEach(el =>{
let type = "minus"; el.addEventListener("click", getItem(type));
});
Do this:
let plus = document.querySelectorAll(".kaufen");
plus.forEach(el =>{
el.addEventListener("click", () => getItem("plus"));
});
let minus = document.querySelectorAll(".zurück");
minus.forEach(el =>{
el.addEventListener("click", () => getItem("minus"));
});
First of all I want to know if I am right about cause of the problem.
const updateScore = (isCorrect) => {
// Update Game Variables
if (isCorrect === true) {
counter++;
score += 100;
}
};
// Reset Styling
const resetLoadedQuestionStyling = (isCorrect) => {
questionScreen.style.display = 'none';
answerArr.forEach(answer => {
answer.classList.remove('correct');
answer.classList.remove('wrong');
answer.classList.remove('disable');
});
updateScore(isCorrect);
};
const styleAnswer = (div, isCorrect) => {
if (isCorrect === true) {
div.classList.add('correct');
} else {
div.classList.add('wrong');
for (let i = 0; i < answerArr.length; i++) {
if (i === currentQuestion.correct) {
answerArr[i].classList.add('correct');
}
}
}
// Prevent Second Check
answerArr.forEach(answer => {
answer.classList.add('disable');
});
// Reset Styling
setTimeout(() => {
resetLoadedQuestionStyling(isCorrect);
}, 3000);
};
const checkAnswer = (div, index) => {
const userChoice = index;
// Default Answer State
let isCorrect = false;
if (userChoice === currentQuestion.correct) {
isCorrect = true;
}
styleAnswer(div, isCorrect);
};
answerArr.forEach((div, index) => {
div.addEventListener('click', () => {
checkAnswer(div, index);
});
});
My counter updates 1,time, that 2 times... and I think the cause of this issue is that my EventListener is in a forEach loop, is that right?
How to prevent it?
Thanks!
EDIT: Addded more of the code in order to get my idea better.
EDIT: answerArr is array of 4 divs in my HTML
There may be a setTimeout-related issue. Every time an answer is clicked, the counter is set to be incremented after 3 seconds.
Here's the sequence when an answer is clicked:
'click'
checkAnswer ->
styleAnswer ->
setTimeout =>
resetLoadedQuestionStyling ->
updateScore ->
counter++
Below is the code with all of the unrelated lines removed. It does increment the counter after every click, but only after 3 seconds.
const answerArr = [...document.querySelectorAll('button')];
let counter = 0;
const span = document.getElementById('counter');
const updateScore = (isCorrect) => {
if (isCorrect === true) {
counter++
}
span.innerText = counter;
}
const resetLoadedQuestionStyling = (isCorrect) => {
updateScore(isCorrect)
}
const styleAnswer = (div, isCorrect) => {
// Reset Styling after 3 seconds
setTimeout(() => {
resetLoadedQuestionStyling(isCorrect);
}, 3000);
}
const checkAnswer = (div, index) => {
styleAnswer(div, true);
}
answerArr.forEach((div, index) => {
div.addEventListener('click', () => {
checkAnswer(div, index);
});
});
<button>Answer 1</button><br>
<button>Answer 2</button><br>
<button>Answer 3</button><br>
<button>Answer 4</button><br>
<p>Counter: <span id="counter"></span></p>
So I'm a newb at web dev, and trying to get my head around JavaScript by writing a.. yeah, you guessed it, a to do list.
I've been trying to set the items to the local storage, and then retrieve it, it sorta works, however when the list items are retrieved, the buttons do not seem to function, and I can't for the life of me figure out why... Any thoughts?
Here's the code:
document.addEventListener('DOMContentLoaded', () => {
const submitButton = document.querySelector('.submit');
submitButton.type = 'submit';
const inputField = document.querySelector('.createItem');
const toDoUl = document.querySelector('.toDoUl');
const completedUl = document.querySelector('.completedUl');
const form = document.querySelector('#header');
const tdContainer = document.getElementById('tdContainer');
const toDoItems = document.getElementById('toDoItems');
(function loadStorage() {
if (localStorage.getItem('todo')) {
tdContainer.innerHTML = localStorage.getItem('todo');
}
})();
function noChildren() {
if (toDoUl.hasChildNodes()) {
tdContainer.classList.remove('tdContainer');
} else {
tdContainer.className = 'tdContainer';
}
}
function createLi() {
const wrapper = document.getElementById('wrapper');
const doneButton = document.createElement('input');
const checkedLabel = document.createElement('label');
doneButton.type = 'checkbox';
checkedLabel.className = 'done';
checkedLabel.appendChild(doneButton);
const listItem = document.createElement('li');
const p = document.createElement('p');
const editButton = document.createElement('button');
const removeButton = document.createElement('button');
toDoUl.appendChild(listItem);
p.textContent = inputField.value;
inputField.value = '';
editButton.className = 'edit';
removeButton.className = 'remove';
listItem.appendChild(checkedLabel);
listItem.appendChild(p);
listItem.appendChild(editButton);
listItem.appendChild(removeButton);
doneButton.style.display = 'none';
editButton.addEventListener('click', () => {
listItem.contentEditable = 'true';
});
listItem.addEventListener('blur', () => {
listItem.contentEditable = 'false';
});
removeButton.addEventListener('click', e => {
const ul = e.target.parentNode.parentNode;
/*const li = e.target.parentNode.parentNode;*/
ul.removeChild(e.target.parentNode);
noChildren();
});
doneButton.addEventListener('click', e => {
if (e.target.parentNode.parentNode.parentNode.className === 'toDoUl') {
completedUl.appendChild(e.target.parentNode.parentNode);
e.target.parentNode.parentNode.className = 'removeTransition';
noChildren();
localStorage.setItem('todo', tdContainer.innerHTML);
} else {
toDoUl.appendChild(e.target.parentNode.parentNode);
e.target.parentNode.parentNode.className = 'addTransition';
noChildren();
}
});
}
form.addEventListener('submit', e => {
e.preventDefault();
noChildren();
createLi();
localStorage.setItem('todo', tdContainer.innerHTML);
});
});
You can see the working version here: http://kozyrev.site/todo/
i'm glad you're writing arrow functions and cool stuff :D
but it seems that you are setting event listeners within createLi function, that is dispatched on form's submit event.
But, when you loads localStorage, is setting HTML content like this:
tdContainer.innerHTML = localStorage.getItem('todo');
event listener is not attached to them, because all of these elements that you created from localStorage, is not create by createLi function :(
but you might write something like this:
// loads from localStorage
(function loadStorage() {
if (localStorage.getItem('todo')) {
tdContainer.innerHTML = localStorage.getItem('todo');
}
})();
// set listeners below
var liSelector = '.toDoUl > li'
var liElements = document.querySelectorAll(liSelector)
Array.prototype.forEach.call(liElements, (liElement) => {
var editButton = liElement.querySelector('.edit')
console.log(editButton)
// you can set listeners here
editButton.addEventListener('click', (e) => {
e.preventDefault()
console.log('yey, event has dispatched, do your magic :)')
})
})
UPDATE: example using named function to reuse them:
function createLi() {
....
const listItem = document.createElement('li');
....
editButton.addEventListener('click', () => {
listItem.contentEditable = 'true';
});
could be written like this
// this function at top of script
const setEditable = (listItem) => {
listItem.contentEditable = 'true';
}
// you can use setEditable within createLi
function createLi() {
....
const listItem = document.createElement('li');
....
editButton.addEventListener('click', () => {
setEditable(listItem)
});
also, after HTML was written from localStorage like this
// loads from localStorage
(function loadStorage() {
if (localStorage.getItem('todo')) {
tdContainer.innerHTML = localStorage.getItem('todo');
}
})();
// set listeners below
var liSelector = '.toDoUl > li'
var liElements = document.querySelectorAll(liSelector)
Array.prototype.forEach.call(liElements, (listItem) => {
var editButton = listItem.querySelector('.edit')
// you can set listeners here
editButton.addEventListener('click', (e) => {
setEditable(listItem)
})
})
I didn't tested, but i hope it works, and it's shows you that named function could be reused for setting listeners :)