I need to create a card element for each row of the database. The problem in my function is that print only the first element. What i forgot? (the query works)
aggiornaEventi();
function aggiornaEventi() {
fetch("../user_area/query/select_allevents.php").then(onResponse).then(onJson);
}
function onResponse(response) {
return response.json();
}
function onJson(json) {
console.log(json);
if (json.length !== 0) {
const eventi = document.getElementById("eventi"); //inserire l'id
for (const evento of json) {
const card = document.getElementById("eventi").innerHTML = `
<div class="card" style="width: 18rem;">
<div class="card-body">
<h5 class="card-title">${evento.titolo}</h5>
<p class="card-text">${evento.descrizione} | ${evento.data}</p>
Vai
</div>
</div>
`;;
eventi.appendChild(card);
}
}
}
It looks like you are not creating a new element for each card, instead reusing the same one over an over.
Try:
// ...
const card = Document.createElement();
card.innerHTML = `
<div class="card" style="width: 18rem;">
<div class="card-body">
<h5 class="card-title">${evento.titolo}</h5>
<p class="card-text">${evento.descrizione} | ${evento.data}</p>
Vai
</div>
</div>
`;
eventi.appendChild(card);
// ...
You override the innerHTML of the card on 'evento' element. You should either use
const card = document.getElementById("eventi").innerHTML += `...`
or create a new element on every run with Document.createElement();
I fixed with this, thanks to all
function onJson(json) {
console.log(json);
if (json.length !== 0) {
const eventi = document.getElementById("eventi"); //inserire l'id
for (const evento of json) {
const card = document.createElement("eventi");
card.innerHTML = `
<div class="card" style="width: 18rem;">
<div class="card-body">
<h5 class="card-title">${evento.titolo}</h5>
<p class="card-text">${evento.descrizione} | ${evento.data}</p>
Vai
</div>
</div>
`;
eventi.appendChild(card);
}
}
}
Related
I created two createCardNote function
First one pull data information from the state.countries
Second is from state.borders
in the createCountryCardNode, console.log(cardDiv) has value thus can be seen in the console
However, createBorderCardNode, console.log(borderDiv) has no value at all
Both codes are the same why has different outcomes?
function createCountryCardNode(country) {
const cardDiv = document.createElement('div');
cardDiv.className = 'country';
cardDiv.innerHTML = `
<img class="country__img" src="${country.flag}" />
<div class="country__data">
<h3 class="country__name">${country.name}</h3>
<h4 class="country__region">${country.region}</h4>
<p class="country__row"><span>👫</span>${country.population}</p>
<p class="country__row"><span>🗣️</span>${country.languages[0].name}</p>
<p class="country__row"><span>💰</span>${country.currencies[0].name}</p>
</div>`;
console.log(cardDiv);
return cardDiv;
}
function createBorderCardNode(border) {
const borderDiv = document.createElement('div');
borderDiv.className = 'country';
// console.log(border.flags.svg);
// console.log(border.name.common);
// console.log(border.region);
// console.log(border.population);
// console.log(border.languages[0].name);
borderDiv.innerHTML = `
<img class="country__img" src="${border.flags.svg}" />
<div class="country__data">
<h3 class="country__name">${border.name.common}</h3>
<h4 class="country__region">${border.region}</h4>
<p class="country__row"><span>👫</span>${border.population}</p>
<p class="country__row"><span>🗣️</span>${border.languages[0].name}</p>
</div>
`;
console.log(borderDiv);
return borderDiv;
}
I have made a TODO app and added a counter to keep a count of the items in the list. If the counter hits zero, I've set it to re-show a message 'You currently have no tasks. Use the input field above to start adding.'
if(count === 0){
noTasksText.classList.remove('d-none');
}
In the console I print out the div and it doesn't have d-none in the class list any more which is what I want, however, in the actual DOM it does.
Here is a full example - https://codepen.io/tomdurkin/pen/LYdpXKJ?editors=1111
I really can't seem to work this out. I can't seem to interact with that div when the counter becomes zero, however I can get console logs etc to show when expected.
Any help would be appreciated!
const mainInput = document.querySelector('#main-input');
const todoContainer = document.querySelector('#todo-container');
const errorText = document.querySelector('#js-error');
const noTasksText = document.querySelector('.js-no-tasks')
let tasks = [];
let count = 0;
// focus input on load
window.onload = () => {
mainInput.focus();
const storedTasks = JSON.parse(localStorage.getItem('tasks'));
if (storedTasks != null && storedTasks.length > 0) {
// set count to number of pre-existing items
count = storedTasks.length
// hide the 'no tasks' text
noTasksText.classList.add('d-none');
// overwrite tasks array with stored tasks
tasks = storedTasks;
tasks.forEach(task => {
// Build the markup
const markup = `
<div class="js-single-task single-task border-bottom pt-2 pb-2">
<div class="row">
<div class="col d-flex align-items-center js-single-task-name">
<h5 class="mb-0" data-title="${task}">${task}</h5>
</div>
<div class="col d-flex justify-content-end">
<button class="js-remove-task d-block btn btn-danger">Remove Item</button>
</div>
</div>
</div>`;
// Append it to the container
todoContainer.innerHTML += markup;
});
} else {
if (noTasksText.classList.contains('d-none')) {
noTasksText.classList.remove('d-none');
}
}
};
// event listener for 'enter on input'
mainInput.addEventListener("keydown", e => {
// if error is showing, hide it!
if (!errorText.classList.contains('d-none')) {
errorText.classList.add('d-none');
}
if (e.key === "Enter") {
// Get the value of the input
let inputValue = mainInput.value;
if (inputValue) {
// Build the markup
const markup = `
<div class="js-single-task border-bottom pt-2 pb-2">
<div class="row">
<div class="col d-flex align-items-center js-single-task-name">
<h5 class="mb-0" data-title="${inputValue}">${inputValue}</h5>
</div>
<div class="col d-flex justify-content-end">
<button class="js-remove-task d-block btn btn-danger">Remove Item</button>
</div>
</div>
</div>`;
// hide 'no tasks' text
noTasksText.classList.add('d-none');
// Append it to the container
todoContainer.innerHTML += markup;
// Push value to 'tasks' array
tasks.push(inputValue);
// Put in localStorage
textTasks = JSON.stringify(tasks);
localStorage.setItem("tasks", textTasks);
// Reset the value of the input field
mainInput.value = '';
// add 1 to the count
count++
} else {
// Some very basic validation
errorText.classList.remove('d-none');
}
}
});
// remove task
todoContainer.addEventListener('click', (e) => {
// Find the button in the row that needs removing (bubbling)
const buttonIsDelete = e.target.classList.contains('js-remove-task');
if (buttonIsDelete) {
// Remove the HTML from the screen
e.target.closest('.js-single-task').remove();
// Grab the name of the single task
let taskName = e.target.closest('.js-single-task').querySelector('.js-single-task-name h5').getAttribute('data-title');
// filter out the selected word
tasks = tasks.filter(item => item != taskName);
textTasks = JSON.stringify(tasks);
localStorage.setItem("tasks", textTasks);
// update counter
count--
// check if counter is zero and re-show 'no tasks' text if true
if (count === 0) {
noTasksText.classList.remove('d-none');
console.log(noTasksText);
}
}
});
body {
background: #e1e1e1;
}
<div class="container">
<div class="row d-flex justify-content-center mt-5">
<div class="col-10 col-lg-6">
<div class="card p-3">
<h2>To dos</h2>
<p>
Use this app to keep a list of things you need to do
</p>
<input class="form-control" id="main-input" type="text" placeholder="Type your todo and hit enter..." class="w-100" />
<small id="js-error" class="text-danger d-none">
Please type a value and press enter
</small>
<hr />
<h4 class="mb-5">Your 'To dos'</h4>
<div id="todo-container">
<!-- todos append in here -->
<div class="js-no-tasks">
<small class="d-block w-100 text-center mb-3">
<i>
You currently have no tasks. Use the input field above to start adding
</i>
</small>
</div>
</div>
</div>
<!-- /card -->
</div>
</div>
</div>
Upon setting innerHTML by using += innerHTML the node noTasksText is lost, because browser processes the whole new set innerHTML and creates new objects. You can either retrieve noTasksText again after that, or append nodes using todoContainer.appendChild. I forked your pen and solved it with the latter solution.
https://codepen.io/aghosey/pen/wvmGwWd
You can do the following, it will work (here innerHTML is changing the DOM, so I added an extra function to recalculate elements after DOM is changed due to innerHTML):
var mainInput = document.querySelector("#main-input");
var todoContainer = document.querySelector("#todo-container");
var errorText = document.querySelector("#js-error");
var noTasksText = document.querySelector(".js-no-tasks");
let tasks = [];
let count = 0;
function getAllElements() {
mainInput = document.querySelector("#main-input");
todoContainer = document.querySelector("#todo-container");
errorText = document.querySelector("#js-error");
noTasksText = document.querySelector(".js-no-tasks");
}
// focus input on load
window.onload = () => {
mainInput.focus();
var storedTasks = JSON.parse(localStorage.getItem("tasks"));
if (storedTasks != null && storedTasks.length > 0) {
// set count to number of pre-existing items
count = storedTasks.length;
// hide the 'no tasks' text
noTasksText.classList.add("d-none");
// overwrite tasks array with stored tasks
tasks = storedTasks;
tasks.forEach((task) => {
// Build the markup
const markup = `
<div class="js-single-task single-task border-bottom pt-2 pb-2">
<div class="row">
<div class="col d-flex align-items-center js-single-task-name">
<h5 class="mb-0" data-title="${task}">${task}</h5>
</div>
<div class="col d-flex justify-content-end">
<button class="js-remove-task d-block btn btn-danger">Remove Item</button>
</div>
</div>
</div>`;
// Append it to the container
todoContainer.innerHTML += markup;
getAllElements();
});
} else {
if (noTasksText.classList.contains("d-none")) {
noTasksText.classList.remove("d-none");
}
}
};
// event listener for 'enter on input'
mainInput.addEventListener("keydown", (e) => {
// if error is showing, hide it!
if (!errorText.classList.contains("d-none")) {
errorText.classList.add("d-none");
}
if (e.key === "Enter") {
// Get the value of the input
let inputValue = mainInput.value;
if (inputValue) {
// Build the markup
const markup = `
<div class="js-single-task border-bottom pt-2 pb-2">
<div class="row">
<div class="col d-flex align-items-center js-single-task-name">
<h5 class="mb-0" data-title="${inputValue}">${inputValue}</h5>
</div>
<div class="col d-flex justify-content-end">
<button class="js-remove-task d-block btn btn-danger">Remove Item</button>
</div>
</div>
</div>`;
// hide 'no tasks' text
noTasksText.classList.add("d-none");
// Append it to the container
todoContainer.innerHTML += markup;
getAllElements();
// Push value to 'tasks' array
tasks.push(inputValue);
// Put in localStorage
textTasks = JSON.stringify(tasks);
localStorage.setItem("tasks", textTasks);
// Reset the value of the input field
mainInput.value = "";
// add 1 to the count
count++;
} else {
// Some very basic validation
errorText.classList.remove("d-none");
}
}
});
// remove task
todoContainer.addEventListener("click", (e) => {
// Find the button in the row that needs removing (bubbling)
const buttonIsDelete = e.target.classList.contains("js-remove-task");
if (buttonIsDelete) {
// Remove the HTML from the screen
e.target.closest(".js-single-task").remove();
// Grab the name of the single task
let taskName = e.target
.closest(".js-single-task")
.querySelector(".js-single-task-name h5")
.getAttribute("data-title");
// filter out the selected word
tasks = tasks.filter((item) => item != taskName);
textTasks = JSON.stringify(tasks);
localStorage.setItem("tasks", textTasks);
// update counter
count--;
// check if counter is zero and re-show 'no tasks' text if true
if (count === 0) {
noTasksText.classList.remove("d-none");
console.log(noTasksText);
}
}
});
body {
background: #e1e1e1;
}
<div class="container">
<div class="row d-flex justify-content-center mt-5">
<div class="col-10 col-lg-6">
<div class="card p-3">
<h2>To dos</h2>
<p>
Use this app to keep a list of things you need to do
</p>
<input class="form-control" id="main-input" type="text" placeholder="Type your todo and hit enter..." class="w-100" />
<small id="js-error" class="text-danger d-none">
Please type a value and press enter
</small>
<hr />
<h4 class="mb-5">Your 'To dos'</h4>
<div id="todo-container">
<!-- todos append in here -->
<div class="js-no-tasks">
<small class="d-block w-100 text-center mb-3">
<i>
You currently have no tasks. Use the input field above to start adding
</i>
</small>
</div>
</div>
</div>
<!-- /card -->
</div>
</div>
</div>
I am looping through an array of objects (rendering data coming from an API) via Vanilla JavaScript (DOM). And I want to pass more than one value (Product ID, Product Name .. etc) to a function triggered in the event of a click button. saveProduct(${proInfo}) as it shown below (proInfo is an object).
The problem is I have been able to pass ONLY ONE value to this function. I tried to pass the variable as an object but it didn't work and got the error (index):1 Uncaught SyntaxError: Unexpected end of input
var shopNcounter = 0;
const baseURL = "***";
const idsArray = [];
const divRow = document.querySelector(".row");
const buttonContainer = document.querySelector("#button-container")
//create column div
function createDiv() {
const div = document.createElement("div");
div.classList.add("col-xl-3");
div.classList.add("col-md-6");
div.classList.add("col-sm-12");
return div;
}
async function getData(id) {
shopNcounter ++;
console.log(shopNcounter);
if(shopNcounter > 5) {
getProInfo(id);
return;
} else if (shopNcounter > 4) {
getProducts(id);
return;
}
idsArray.push(id);
console.log(id);
console.log(idsArray);
if(idsArray.length > 10) {
idsArray.splice(0,6);
}
const api = `**shop=${shopNcounter}&id=${id}`;
try {
const response = await fetch(api);
const data = await response.json();
divRow.innerHTML = "";
if (shopNcounter > 1) {
buttonContainer.innerHTML = `
<button class="btn btn-lg btn-warning px-5" onclick="moveBack(${idsArray[idsArray.length-2]})">رجوع</button>
`
} else {
buttonContainer.innerHTML = "";
}
//mapping through data
data.map(item => {
const div = createDiv();
divRow.appendChild(div);
div.innerHTML+=`
<div class="card text-center h-100 mx-auto border-white shadow" style="width: 16rem;">
<img src="${baseURL + item.image}" class="card-img-top mx-auto" alt="...">
<div class="card-body">
<a onclick=handleClick(${item.id}) class="btn btn-outline-dark px-5 mt-4" id="goToItemButton">${item.name}</a>
</div>
</div>
`;
});
} catch(error) {console.log(error);}
}
getData(0);
async function getProducts(id) {
//shop > 4 = 5
console.log("Get Product is being executing");
const apiProducts = `**product?id=${id}`;
try {
const response = await fetch(apiProducts);
const data = await response.json();
console.log(data);
divRow.innerHTML = "";
//mapping through data
data.map((item, index) => {
var proInfo = {
pid: item.PID,
pname: item.PName
}
const div = createDiv();
divRow.appendChild(div);
div.innerHTML+=`
<div class="card text-center h-100 mx-auto border-white shadow" style="width: 16rem;">
<img src="${baseURL + item.image}" class="card-img-top mx-auto" alt="...">
<div class="card-body">
<h5>${item.PName}</h5>
<p class="product-price">Price: ${item.PSelPrice}</p>
<a onclick=saveProduct(${item.PID}) class="btn btn-danger px-5 mt-4"> Add to Card </a>
</div>
</div>
`;
});
} catch(error) {
console.log(error);
}
}
function moveBack(id) {
shopNcounter = shopNcounter -2;
getData(id);
}
function handleClick(id) {
let clickedButton = document.querySelector("#goToItemButton");
clickedButton.onclick = "";
getData(id);
}
function saveProduct(g) {
console.log(g);
}
your code should not result in the error you see
However you can make your life easier using delegation
do this
document.getElementById("container").innerHTML = data.map((item,i) => `<div class="card text-center h-100 mx-auto border-white shadow" style="width: 16rem;">
<img src="${baseURL + item.image}" class="card-img-top mx-auto" alt="...">
<div class="card-body">
<h5>${item.PName}</h5>
<p class="product-price">Price: ${item.PSelPrice}</p>
<a data-id="${i}" class="add btn btn-danger px-5 mt-4"> Add to Card </a>
</div>
</div>`).join("");
Have this
document.getElementById("container").addEventListener("click",function(e) {
const tgt = e.target.closest("a");
if (tgt && tgt.className.contains("add")) {
const item = data[tgt.dataset.id]
// here you can add the item
saveProduct({pid: item.PID,pname: item.PName })
}
})
I have one button that shows all my products in the webpage, each product is loaded from an array and inserted with innerHTML, each product is inserted with one button, i tried to assing one function for those buttons, but nothing happens:
Show.addEventListener('click', () => {
flag = flag + 1
if (flag == 2) {
document.getElementById('ItemsID').disabled = true
}
console.log(ItemsID.options[ItemsID.selectedIndex].value)
if (ItemsID.options[ItemsID.selectedIndex].value == "Vuelos") {
document.getElementById('ShowItems').disabled = true
VUELOSPROMOCIONES.forEach((VUELOSPROMOCIONES, indice) => {
div_Vuelos.innerHTML += `
<div class="card" id="persona${indice + 1}" style="width: 18rem;">
<div class="card-body">
<h5 class="card-title">Vuelo Promocional ${indice + 1}</h5>
<p>Origen: ${VUELOSPROMOCIONES.origen}</p>
<p>Destino: ${VUELOSPROMOCIONES.destino}</p>
<p>Fecha Ida: ${VUELOSPROMOCIONES.FechaIda}</p>
<p>Fecha Vuelta: ${VUELOSPROMOCIONES.FechaVuelta}</p>
<p>Precio: ${VUELOSPROMOCIONES.precio}</p>
</div>
</div>
**<button id="Enviar">Click me</button>**
`
})
} else if (ItemsID.options[ItemsID.selectedIndex].value == "Autos") {
PROMOCIONESAUTOS.forEach((PROMOCIONESAUTOS, indice) => {
document.getElementById('ShowItems').disabled = true
div_Autos.innerHTML += `
<div class="card" id="Autos Disponibles ${indice + 1}" style="width: 18rem;">
<div class="card-title">Persona ${indice + 1}</h5>
<p>Modelo: ${PROMOCIONESAUTOS.modelo}</p>
<p>Marca: ${PROMOCIONESAUTOS.marca}</p>
<p>Color: ${PROMOCIONESAUTOS.color}</p>
</div>
</div>
<button id="Enviar">Click me</button>
`
})
}
})
if i insert the same button in the html, it works with the same id
this if my first question here, so sorry for any mistake or if the question is too silly.
*I have a Class with a method that displays a country-card on screen. I need to add an onclick function to save the name of the country so I can access to it from another page. i don't know if there is a way to make it work.
Any ideas?
class UI {
constructor() {
this.cardsContainer = document.querySelector("#cards-container");
}
showCountries(data) {
data.forEach(country => {
let div = `
<div class="card">
// this is the onclick function i need to access
<a onclick="countrySelected(${this.country.borders})">
<div class="card-image">
<img src=${country.flag} alt="">
</div>
<div class="card-info">
<h2 class="card-name"> ${country.name}</h2>
<p><strong>Population:</strong> ${country.population}</p>
<p><strong>Region:</strong> ${country.region}</p>
<p><strong>Capital:</strong> ${country.bogota}</p>
</div>
</a>
</div>
`;
this.cardsContainer.innerHTML += div;
})
}
//method
countrySelected(data) {
sessionStorage.setItem('country', data);
}
}
I assume that the country.name is unique.
class UI {
constructor() {
this.cardsContainer = document.querySelector("#cards-container");
}
showCountries(data) {
data.forEach(country => {
let div = `
<div class="card">
// this is the onclick function i need to access
<a id=${country.name}>
<div class="card-image">
<img src=${country.flag} alt="">
</div>
<div class="card-info">
<h2 class="card-name"> ${country.name}</h2>
<p><strong>Population:</strong> ${country.population}</p>
<p><strong>Region:</strong> ${country.region}</p>
<p><strong>Capital:</strong> ${country.bogota}</p>
</div>
</a>
</div>
`;
this.cardsContainer.innerHTML += div;
document.querySelector(`a[id="${country.name}"]`)
.addEventListener('click', () => countrySelected(country.borders));
})
}
//method
countrySelected(data) {
sessionStorage.setItem('country', data);
}
}
Also, you can refer to this post: Setting HTML Button`onclick` in template literal