I am not able to discover the problem that is causing the function to be executed more than once.
The code snippet below is when the page is opened or when there has been a change, whether new or changed data. This function takes the data from the bank and shows, this part I will not show because it is unnecessary. After showing it, it calls two functions and it is the first one that is giving me a problem, the update function.
function carteProduct_listProducts() {
let dataProductsDatas = document.getElementById("data_products_datas");
firebase.database().ref("Products").on("value", snapshot => {
dataProductsDatas.innerHTML = ""
snapshot.forEach(element => {
...
});
// Edit buttons event listener
// Update
modalEditProduct()
// Delete
realtimedb_delete("Products");
})
}
Then comes the update function, or as it is in the code, modalEditProduct.
I made a modal just for that, because I was having trouble using only one modal.
So, this function is called there in the other function. And I thought it was because of this that I was giving this problem, the function being executed twice, but no! I took the function out of there and tested it, but the problem keeps happening.
Then, when an edit button is clicked, the modal opens. So it performs some functions that are within that same function
// Update product
function modalEditProduct() {
let editBtns = document.querySelectorAll("#data_products_datas .editBtn");
let productTitleInput = document.querySelector("#modalEditProduct form input[name='productTitle']");
let productCategoryInput = document.querySelector("#modalEditProduct form select[name='productCategory']");
let productPriceInput = document.querySelector("#modalEditProduct form input[name='productPrice']");
let productIngredientsInput = document.querySelector("#modalEditProduct form input[name='productIngredients']");
let productDescriptionInput = document.querySelector("#modalEditProduct form input[name='productDescription']");
let editSubmitBtn = document.querySelector("#modalEditProduct form button");
console.log(editBtns)
editBtns.forEach(element => {
element.addEventListener("click", (e) => {
let key = e.target.id;
// Open modal
modal_openModal("modalEditProduct");
// Get the data
let data = getData(key);
// putting categories on select
cartePorudct_puttingCategoriesSelect(productCategoryInput);
// let children = productCategoryInput.children;
// console.log(children)
// for (let i = 0; i < children.length; i++) {
// const element = children[i];
// console.log(element)
// }
// Insert the data in the field/inputs
setData(data);
// Submit form
editSubmitBtn.addEventListener("click", (e) => {
e.preventDefault();
realtimedb_verification("modalEditProduct", edit_productData, key)
})
})
});
// Get the data
function getData(key) {
let data;
firebase.database().ref("Products/"+key).on("value", snapshot => {
data = {
title: snapshot.val().title,
category: snapshot.val().category,
price: snapshot.val().price,
ingredients: snapshot.val().ingredients,
description: snapshot.val().description,
}
});
return data;
}
// Insert the data in the field/inputs
function setData(data) {
productTitleInput.value = data.title;
// productCategoryInput.value = data.category,
productPriceInput.value = data.price;
productIngredientsInput.value = data.ingredients;
productDescriptionInput.value = data.description;
}
// Submit form
function submitForm(btn, key){
}
// Data
function edit_productData(key) {
let data = {
title: productTitleInput.value,
category: productCategoryInput.value,
price: productPriceInput.value,
ingredients: productIngredientsInput.value,
description: productDescriptionInput.value,
}
// Update
realtimedb_update("Products", key, data);
}
}
Anyway, the problem is. When I open a modal, make a change and save, everything is fine. If I open the modal of another data, the previous data will also be changed with the value of that data after I click the submit button. And if I open a third modal, everything happens again.
Every time you call modalEditProduct(), you add a new event listener to each of those editBtns.
That means that the first time you've called modalEditProduct() and click a button, it does its thing once. After a second call, the buttons now have two click event listeners, so clicking them will do a thing twice, etc.
Related
I have an async function that on page load it runs a function that gets a JSON file and stores it in songResults. it then gets a random object from the JSON and takes some parameters from it and uses them to set the sources for some elements in my HTML file. I am having an issue with the callback for the event listener, the parameter I am passing through guessingGame() is undefined inside the guessingGame() function and I'm not sure why. any help would be muchly appreciated.
JS code
//A async function to run when the page loads to get the first song and album cover to display.
const onPageLoad = async () => {
let songResults = await getSongData();
let randomSong = songResults[Math.floor(Math.random() * songResults.length)];
audioSound.src = randomSong.song_path;
audioSound.load();
albumCover.src = randomSong.photo_path;
//An event listener for the submit button to run the guessing game function.
submitButton.addEventListener("click", guessingGame(randomSong.song_path))
};
//async function for when the button is clicked to check the answer in the input box to the json data.
const guessingGame = async (songPath) => {
//get the value of the input box
let input = document.getElementById("guessInputBox").value;
//check if the value of the input box matches the song path in the json data
if (input) {
if (input === songPath) {
alert('correct')
score++;
alert("that took " + score + " attempts");
score = 0;
changeSong();
} else {
alert('incorrect')
alert(songPath);
score++;
};
};
};
What a json file response looks like
{
"song_name": "Listen to Us",
"release_date": "1/05/2012",
"album": "Home Brew",
"photo_path": "/pictures/home-brew.jpg",
"song_path": "/songs/homebrewski-listenToUs.mp3"
}
so the new code updates it kind of it works the first time you guess but the second time you guess it says your incorrect then correct and keeps looping heaps really weird and lags out the browser.
const onPageLoad = async () => {
let songResults = await getSongData();
let randomSong = songResults[Math.floor(Math.random() * songResults.length)];
audioSound.src = randomSong.song_path;
audioSound.load();
albumCover.src = randomSong.photo_path;
//An event listener for the submit button to run the guessing game function.
submitButton.addEventListener("click", () => {
guessingGame(randomSong.song_name);
});
};
//async function for when the button is clicked to check the answer in the input box to the json data.
const guessingGame = async (songPath) => {
//get the value of the input box
let input = document.getElementById("guessInputBox").value;
//check if the value of the input box matches the song path in the json data
if (input) {
if (input.toLowerCase() === songPath.toLowerCase()) {
alert('correct')
score++;
alert("that took " + score + " attempts");
score = 0;
changeSong();
} else {
alert('incorrect')
alert(songPath);
score++;
};
};
};
//need to change this to an async function once figured out how to store data.
const changeSong = async () => {
let songResults = await getSongData();
let randomSong = songResults[Math.floor(Math.random() * songResults.length)];
audioSound.src = randomSong.song_path;
audioSound.load();
albumCover.src = randomSong.photo_path;
submitButton.addEventListener("click", () => {
guessingGame(randomSong.song_name);
});
};
Updated Answer
Thank you for posting more code. It looks like you may have mistakenly passed along the song_name instead of the song_path:
// From the onPageLoad and changeSong functions
guessingGame(randomSong.song_name);
// From the guessingGame function
input.toLowerCase() === songPath.toLowerCase()
Another thing to consider is your click-handler; you're adding one to the submitButton every time a song is loaded. As such, the first time you click the button, a single event handler is called. The second time you click, two are called. The third time, three, etc.
Your submit button behavior really only needs to be set once, and forgotten. It's job is to do one thing: see if the user's selection matches the currently-playing song. And, if the user got the correct answer, load another song:
// Minimal game state
let score = 0;
let songs = [];
let songIndex = -1;
// References to game elements
const submit = document.querySelector("#submit")
const selection = document.querySelector("options");
// Begin game functionality
async function onPageLoad () {
// Load meta data for all songs into `songs`
songs = await getSongData();
// TODO: Populate #options based on `songs` data
setRandomSong();
}
function setRandomSong () {
songIndex = Math.floor( Math.random() * songs.length );
audioSound.src = songs[ songIndex ].path;
albumCover.src = songs[ songIndex ].photo;
}
// This will be called whenever a song is changed
audioSound.addEventListener( "load", function audioLoaded () {
console.log( "Audio has loaded, game can be played." );
});
// We need only a single handler for our guess-button
submit.addEventListener( "click", function guess () {
// Get our values to compare
const guess = selection.value;
const answer = songs[ songIndex ].name;
// Increment the number of attempts
score += 1;
// Check lowercase values for equality
// If the answer is false, share number of attempts
if ( guess.toLowerCase() !== answer.toLowerCase() ) {
alert( `You have guessed ${ score } times.` );
return;
}
// If the answer was correct, reset game state
alert( `Correct! And after ${ score } guesses.` );
score = 0;
setRandomSong();
});
Note, this code is untested, but should clearly define a suitable approach to your problem. Please feel free to engage further within the comments as needed.
Original Answer
The addEventListener method expects a function as the second argument. Look closely at your code:
submitButton.addEventListener("click", guessingGame(randomSong.song_path))
Note that you're executing guessingGame, rather than referencing it. Instead, you'd want to provide another function:
submitButton.addEventListener("click", function () {
guessingGame( randomSong.song_path );
});
Now, when the submitButton is clicked, our anonymous function will be called, which in turn will pass randomSong.song_path to guessingGame.
I have a problem with the getItem of my localStorage in my React Form. I put a onChange attribute:
<div className = 'InputForm' onChange={save_data}>
I found the setItem function to store the data in. Here is the function:
function save_data(){
let textarea = document.querySelectorAll("textarea")
let input = document.querySelectorAll("input[type='text']")
let saved_fields = []
textarea.forEach(x => {
saved_fields.push({
key: x.className,
value: x.value
})
})
input.forEach(x => {
saved_fields.push({
key: x.className,
value: x.value
})
})
localStorage.setItem("saved_data", JSON.stringify(saved_fields))
}
My main problem is that I don't find a way to put the data back to the page after the page reload. I just found out how to persist all my inputs in the console:
window.onload = dataLoad();
function dataLoad () {
let show_saved_data = localStorage.getItem("saved_data");
console.log('show_saved_data:',JSON.parse(show_saved_data));
}
Can you guys help me find the retrieve/persist data function?
Edit : Here is the html of the form, i use props from another component. I don't know if this can change the function i need to use.
<InputFields
stateKey = 'contactInfo'
key = {props.contactInfo.id}
completedFields = {props.contactInfo}
templateFields = {props.templates.contactInfo}
onDataEntry = {props.onDataEntry}
newField = {props.newField}
/>
Can we have your HTML form to help you? You should not identify your inputs / textareas by their className.
After that, by using ID as identifiers for your input / textarea, you just have to do it in reverse:
Get your input/textarea list
forEach items, set the value based on the ID
function dataLoad () {
var show_saved_data = localStorage.getItem("saved_data");
var inputList = JSON.parse(show_saved_data);
inputList.forEach(x => {
document.getElementById(x.key).setAttribute('value', x.value);
})
}
Giving us your complete HTML/JS will be easier to give you a complete solution.
I'm building a ToDo app. The feature I'm having issue with is when I go to "Edit" a task and upon clicking "Enter", it adds a New Task, rather than updating the existing task.
Simultaneously, I want to update the Date of the task in "Edit". I have an EventListener tied to 'Set Date' button, inside of my datepicker. Each time the 'Set Date' is clicked, it also adds a New Task, rather than updating the date.
// edit task function
function editTask(taskId, taskName) {
editId = taskId;
isEditedTask = true;
taskInput.value = taskName;
element.classList.add("show__date")
}
// enter feature
taskInput.addEventListener("keyup", e => {
let userTask = taskInput.value.trim();
if(e.key == "Enter" && userTask ) {
// alert(When Is This Task Due? 😁")
element.classList.add("show__date");
} document.addEventListener("click", () => {
// removing show class from the task menu on the document click
if(e.target !== insertDate) {
element.classList.remove("show__date");
}
});
})
// set date feature
insertDate.addEventListener("click", () => {
if (dateSelect.value.length !== "") { // checking to see if .date__wrapper input
field is empty
element.classList.remove("show__date"); // removing datepicker, if field is
not empty after clicking button
let userTask = taskInput.value.trim();
let dueDate = dateSelect.value.trim();
let taskInfo = {name: userTask, status: "pending", date: dueDate};
todos.push(taskInfo); // adding new task to todos
}
// else if (!isEditedTask) {
// if(!todos) { //if todos doesn't exist, pass an empty array to todos
// todos = [];
// }
// }
localStorage.setItem("todo__list", JSON.stringify(todos));
showTodo(); //displays new task inside the task menu once enter is keyed
showTodo("all")
})
So I click "Edit" on "Task 2" and it fills the input field and I change the new task name to Task 12 and press "Enter". After clicking "Enter", the datepicker shows and I choose a date. After clicking "Set Date", instead of updating the existing task, it adds a new task entirely. So Task 1, Task 2, Task 3, and Task 12. Instead of Task 1, Task 12, Task 3.
In the insertDate.addEventListener("click"...
You always take the inputs and then add a new todo. You should first check if you are currently editing and if you are, take the todo you are editing and change it instead of pushing a new one in the list.
it would look something like this:
// set date feature
insertDate.addEventListener("click", () => {
if (dateSelect.value.length !== "") { // checking to see if .date__wrapper input
field is empty
element.classList.remove("show__date"); // removing datepicker, if field is not empty after clicking button
let userTask = taskInput.value.trim();
let dueDate = dateSelect.value.trim();
if (editId) {
const todo = todos.find(todo => todo.id === editId);
todo.name = userTask;
todo.date = dueDate
} else {
let taskInfo = {name: userTask, status: "pending", date: dueDate};
todos.push(taskInfo); // adding new task to todos
}
}
// else if (!isEditedTask) {
// if(!todos) { //if todos doesn't exist, pass an empty array to todos
// todos = [];
// }
// }
localStorage.setItem("todo__list", JSON.stringify(todos));
showTodo(); //displays new task inside the task menu once enter is keyed
showTodo("all")
})
everyone, I have some problem with fetching data and displaying message on initial loading as well as when I change some of the input filed value. The idea here is to display specific message in two cases after doing some calculation.
const potrosnja = document.getElementById('potrosnja');
const nagib = document.getElementById('nagib');
const input = document.querySelectorAll('input[type="number"]');
const submitBtn = document.getElementById('submitBtn');
const poruka = document.getElementById('poruka');
let str = document.querySelector('input[name="strane-sveta"]:checked').value;
let godisnjaPotrosnja = parseInt(potrosnja.value);
let nagibKrovaInput = nagib.value;
//On button submit it fetches data and calculate the value needed for yearly consumption of energy
//fetching data
async function dataFetch(){
let response = await fetch('./csvjson.json')
let data = await response.json();
data.map(strana => {
strana.strana.map((item, i) => {
try {
if(item == str && nagibKrovaInput == strana.nagib) {
let result = Math.ceil(godisnjaPotrosnja / strana.vrednost[i]);
console.log("try works")
poruka.innerHTML = `You need <span class="kw">${result}</span>`
}
}
catch(err) {
poruka.innerHTML = `Please fill required fields.`
console.log(err)
}
})
})
}
//event listeners
submitBtn.addEventListener('click', () => {
dataFetch()
console.log('clicked')
input.forEach(input => {
if(input.value == ''){
input.classList.add("active");
}
})
})
I can see that the problem is inside try function, it like condition isn't treated on initial load, and I have to reload page so it would work. Can someone help me understanding what is the problem?
Ok, I found solution... First thing I have to do Is to check if nagibKrovaInput == strana.nagib, after I get true, I compared does the indexOf item is equal as str and after I get true, it will display something. I also changed on click on the button to send values to data function as an arguments and It solved the problem. Tnx for help.
my goal is to render some user data from API https://reqres.in/api/users?page=( this can be 1,2 or more if pages are available) and output it to a html table using JS / Promises. So initially I have managed to get the first page's data to the table and now I need it to be modified when I click "Load More" button it should delete the current data on the table and shows the page 2's data. This is my code so far
let userDataTable = document.getElementById("userData");
var tot_pages;
let pagesCount = 1;
console.log(tot_pages);
let getUSerInfo = function () {
fetch(`https://reqres.in/api/users?page=${pagesCount}`)
.then((response) => response.json())
.then((people) => {
let users = people.data;
tot_pages = people.total_pages;
console.log(users);
console.log(tot_pages);
for (let i = 0; i < users.length; i++) {
let htmlContent = `<tr><td>${users[i].id}</td><td><img src="${users[i].avatar}"/></td><td>${users[i].first_name}</td><td>${users[i].last_name}</td><td>${users[i].email}</td></tr>`;
userDataTable.insertAdjacentHTML("beforeend", htmlContent);
}
})
.catch(function (error) {
console.log(error);
});
};
getUSerInfo();
console.log(tot_pages);
document
.getElementById("load_btn")
.addEventListener("click", () => getUSerInfo());
also when there are no pages left to load (ex: when the last page's data is showing in the table then the "Load More" button should not be visible)
I'll explain what my idea was achieving this task : I was trying to create a global variable(tot_pages) initializing it to 1. Then inside the promise I was trying to assign the total pages from the object which I render via reqres.in and return it to the button as a counter or something. So as an ex : When I click the button the counter will increase(in this case the tot_pages variable will increase). Anyway after hours of trying on my own could not get this done yet. I do really appreciate if someone can help me out. Thank you for you time and consideration!
I think your code will work fine with you updating pagesCount variable on each successful API fetch, check this updated code. I've changed some variable names
let totalPages,
currentPage = 0,
loadMoreBtn = document.getElementById("load_btn");
// bind load more button
loadMoreBtn.addEventListener("click",getUSerInfo);
// fetch people
function getUSerInfo() {
// ignore if all data has been loaded
if(currentPage >= totalPages) return
const nextPage = currentPage + 1;
fetch(`https://reqres.in/api/users?page=${nextPage}`)
.then((response) => response.json())
.then((people) => {
const users = people.data,
userDataTable = document.getElementById("userData");
totalPages = people.total_pages;
// hide load more button
if(totalPages == nextPage) loadMoreBtn.style.visibility = 'hidden';
// udate table data
for (let i = 0; i < users.length; i++) {
let htmlContent = `<tr><td>${users[i].id}</td><td><img src="${users[i].avatar}"/></td><td>${users[i].first_name}</td><td>${users[i].last_name}< /td><td>${users[i].email}</td></tr>`;
userDataTable.insertAdjacentHTML("beforeend", htmlContent);
}
currentPage = nextPage;
})
.catch(function (error) {
console.log(error);
});
};
// fetch initial page
getUSerInfo();