Im fetching a group of documents in a forEach from Firestore, I know im fetching this all as I can see documents and is fields in the console.
But when I add the id to the field fetched it only shows one document. (it should be 5 tiles and images but I can only see one)
From reading I know I only need to some how duplicate the HTML code based on the documents fetch in the collection but am struggling to do so.
Javascript code
const i = query(collection(db, "teams"));
const unsubscribe = onSnapshot(i, (querySnapshot) => {
querySnapshot.forEach((doc) => {
const docData = doc.data();
document.getElementById("ageGroup").innerText = docData.ageGroup,
document.getElementById("teamImage").src = docData.teamImage,
console.log("Current data: ", docData);
});
});
HTML Code
<section class="teams">
<article>
<h1 class="team-names" id="ageGroup"></h1>
<div class="team-line"></div>
<img class="team-image" id="teamImage">
</article>
</section>
Unless you know how many items will be in the result in advance, dynamically create new elements inside the loop, and then insert the data from the database into the HTML.
Don't use IDs - those should only be used for absolutely unique elements, not for repeated elements.
For an <article> for each item, where the parent of all articles is the .teams, do:
const teams = document.querySelector('.teams');
querySnapshot.forEach((doc) => {
const docData = doc.data();
const article = teams.appendChild(document.createElement('article'));
article.innerHTML = `
<h1 class="team-names"></h1>
<div class="team-line"></div>
<img class="team-image">
`;
article.children[0].textContent = docData.ageGroup;
article.children[2].src = docData.teamImage;
});
Related
I have this piece of code that reads data from an excel sheet, turns them into objects and then display their details in a neat product card
let allHoodies = [
['Hoodie', 'Purple', 'Cotton', '$39.99', 'image/items/hoodies/hoodie(1).jpg'],
['Hoodie', 'Blue', 'Cotton', '$39.99', 'image/items/hoodies/hoodie(2).jpg'],
['Hoodie', 'Green', 'Cotton', '$39.99', 'image/items/hoodies/hoodie(3).jpg']
]
allHoodies.forEach((element, index) => {
let obj = {}
obj.id = index
obj.type = element[0]
obj.color = element[1]
obj.material = element[2]
obj.price = element[3]
obj.imagesrc = element[4]
allHoodies[index] = obj
})
//Evaluating each hoodie and displaying its information in HTML
allHoodies.forEach(function(hoodie) {
let card = `
<div class="card">
<img class="product-image" src="${hoodie.imagesrc}">
<h1 class="product-type">${hoodie.type}</h1>
<p>Color: ${hoodie.color}</p>
<p>${hoodie.material} Read more </p>
<p class="price">${hoodie.price}</p>
<p><button>Buy</button></p>
</div>
`;
// Add the card to the page
document.getElementById('product-container').innerHTML += card;
});
What I'm trying to do is, upon clicking "Buy", it adds multiple items to the local storage although I'm struggling to do it and add multiple ones, it keeps on adding only 1 of them and overwriting the previous one (I'm assuming due to the fact that they have the same key)
Here's what I've tried (which works, but its not my goal):
function addToCart(id){
let hoodie = hoodies[id];
localStorage.setItem('item', JSON.stringify(hoodie));
}
and then I simply add the addToCart() function to the button, would someone guide me and help me figure out how I could actually add multiple ones to the local storage and not just keep overwriting?
Expected result:
Runnable JSFiddle snippet
You can use localStorage#getItem to get the current list, and JSON#parse to convert it to an array of objects. Then, use Array#push to add the current item, and finally, use localStorage#set and JSON#stringify to save the updated list:
function addToCart(id) {
try {
const hoodie = allHoodies[id];
if(hoodie) {
const items = JSON.parse(localStorage.getItem('items') || "[]");
items.push(hoodie);
localStorage.setItem('items', JSON.stringify(items));
}
} catch(e) {
console.log('error adding item');
}
}
Function to show the saved list:
function displayProductsinCart() {
const products = JSON.parse(localStorage.getItem("item") || "[]");
document.getElementById("item-container").innerHTML = products.reduce((cards, product) =>
cards + `<div class="card">
<img class="item-image" src="${product.image}">
<h1 class="product-type">${product.type}</h1>
<p>Color: ${product.color}</p>
<p>${product.description}</p>
<p class="price">${product.price} </p>
<p><button>Buy</button></p>
</div>
`, '');
}
I am a beginner in JavaScript and I can't figure out the following problem: I am trying to create a simple JavaScript Movie List. I have 10 lists on the Movie List. I tried to show all of the lists with for loop, but it doesn't work.
Here's the code:
function renderModal() {
for (let i = 0; i < listMovies.length; i++) {
let movieData = listMovies[i];
document.getElementById("poster").src = movieData.img;
document.getElementById("title").innerHTML = movieData.name;
document.getElementById("genre").innerHTML = movieData.genre;
document.getElementById("rating-num").innerHTML = "Rating: "+ movieData.rating + "/10";
document.getElementById("movie-desc").innerHTML = movieData.desc;
document.getElementById("imdb-page").href = movieData.link;
return movieData;
}
}
What do I have to do?
Help me to fix it!.
You can use template tag for list and render it into target element.I am showing an example.
Movie list
<div id="movieList"></div>
template for list
<template id="movieListTemplate">
<div class="movie">
<img src="" class="poster" alt="">
<div class="title"></div>
<div class="genre"></div>
<div class="rating-num"></div>
<div class="movie-desc"></div>
<div class="imdb-page"></div>
</div>
</template>
Javascript code:
if (listMovies.length > 0) {
const movileListTemplate = document.getElementById('movieListTemplate')
const movieRenederElement = document.getElementById('movieList')
for(const movie of listMovies) {
const movieEl = document.importNode(movileListTemplate.content, true)
movieEl.querySelector('.poster').src = movie.img
movieEl.querySelector('.title').textContent = movie.name
//use all queryselector like above
}
}
Your return movieData; will stop the loop dead. Not that running it more than once will change anything since you change the same elements over and over. IDs must be unique.
Here is a useful way to render an array
document.getElementById("container").innerHTML = listMovies.map(movieData => `<img src="${movieData.img}" />
<h3>${movieData.name}</h3>
<p>${movieData.genre}</p>
<p>Rating: ${movieData.rating}/10</p>
<p>${movieData.desc}
IMDB
</p>`).join("<hr/>");
With return movieData, the for loop will ends in advance.You should put it outside the for loop.
Why when you are searching for something else is deleting the previous contents ?For example first you search for egg and show the contents but then when you search for beef the program deletes the egg and shows only beef.Code :
const searchBtn = document.getElementById('search-btn');
const mealList = document.getElementById('meal');
const mealDetailsContent = document.querySelector('.meal-details-content');
const recipeCloseBtn = document.getElementById('recipe-close-btn');
// event listeners
searchBtn.addEventListener('click', getMealList);
mealList.addEventListener('click', getMealRecipe);
recipeCloseBtn.addEventListener('click', () => {
mealDetailsContent.parentElement.classList.remove('showRecipe');
});
// get meal list that matches with the ingredients
function getMealList(){
let searchInputTxt = document.getElementById('search-input').value.trim();
fetch(`https://www.themealdb.com/api/json/v1/1/filter.php?i=${searchInputTxt}`)
.then(response => response.json())
.then(data => {
let html = "";
if(data.meals){
data.meals.forEach(meal => {
html += `
<div class = "meal-item" data-id = "${meal.idMeal}">
<div class = "meal-img">
<img src = "${meal.strMealThumb}" alt = "food">
</div>
<div class = "meal-name">
<h3>${meal.strMeal}</h3>
Get Recipe
</div>
</div>
`;
});
mealList.classList.remove('notFound');
} else{
html = "Sorry, we didn't find any meal!";
mealList.classList.add('notFound');
}
mealList.innerHTML = html;
});
}
It's because you are replacing the contents in the mealList element every time.
A simple workaround would be to retrieve the the innerHTML values before you update it.
Something like
let html = mealList.innerHTML;
rather than starting off empty every time you call the function should do the trick.
Issue:
I want to return the button Element within my document that matches the specified selector, in this case ".comment-body__interaction--delete" but keep getting a return of null every time I console.log the variable that contains the return element.
Background Info
The HTML element I'm Trying to target has been inserted into the document via innerHTML.
All my scripts are at the bottom of the index.html page
I'm using querySelector at the bottom of the js document.
I know my class name is correct because I can style it via CSS.
my code
// LOCATION VARIABLES ***
const conversation = document.querySelector('.conversation-container-posted');
const form = document.querySelector('form');
console.log(form);
// Array THAT HOLDS ALL MY COMMENT OBJECTS
let objectsArray;
// VARIABLE THAT HOLDS MY HTML TEMPLATE
const template = (singleCommentObj) => {
return `
<article class="comment-container">
<figure class="comment-container__picture">
<img class="comment-container__picture-img" src="${singleCommentObj.image}" alt="profile picture" />
</figure>
<div class="comment-body">
<h3 class="comment-body__name">${singleCommentObj.name}</h3>
<div class="comment-body__date">${singleCommentObj.date}</div>
<article class="comment-body__comment"><p>${singleCommentObj.comment}</p></article>
<div class="comment-body__interaction">
<div class="comment-body__interaction--likes">Likes</div>
<button id="${singleCommentObj.id}" class="comment-body__interaction--delete">Delete</button>
</div>
</div>
</article>
<hr class="comment-container__divider"/>
`;
};
const displayComment = (object) => {
let staticComments = object
.sort((a, b) => b.timestamp - a.timestamp)
.map((values) => {
values.image = 'https://loremflickr.com/48/48';
values.date = moment.unix(values.timestamp / 1000).fromNow();
return template(values);
})
.join('');
conversation.innerHTML = staticComments;
};
// Gets AN ARRAY OF OBJECTS FROM THE api AND ASSIGNS IT TO objectsArray
// CALLS displayComment WITH objectsArray AS A PARAMETER TO INSERT ITS CONTENT INTO THE DOM
axios
.get('https://project-1-api.herokuapp.com/comments?api_key=7d8d085e-486e-42dc-b836-58009cbfa68f')
.then((response) => {
objectsArray = response.data;
displayComment(objectsArray);
})
.catch((error) => {
console.log(error);
});
form.addEventListener('submit', (e) => {
e.preventDefault();
let fluidObject = new FormData(e.target);
fluidObject = Object.fromEntries(fluidObject);
axios
.post('https://project-1-api.herokuapp.com/comments?api_key=7d8d085e-486e-42dc-b836-58009cbfa68f&content-type=application/json', {
name: fluidObject.name,
comment: fluidObject.comment,
})
.then((response) => {
objectsArray.push(response.data);
displayComment(objectsArray);
})
.catch((error) => {
console.log(error);
});
});
// DELETE
const a = document.querySelector('.comment-body__interaction--delete');
console.log(a);
This console.log(a) returns NULL
The code that creates the said element, displayComment is in an asynchronous actions callback.
You have to wait for the action to complete before you try to access the element.
In other words const a = document.querySelector('.comment-body__interaction--delete'); executes before your request was successful and the elements were created.
I have coded a ajax based "JS TABS" containing .JSON file like 10 months ago, now wanted to reuse it, and can't find out why it's not working. I haven't touched it since and don't know where is the bug.
When i click the button to render products nothing prints out - except console telling me: items is undefined = so i moved it inside function changeCategoryItems(categoryId) { } well no errors but nothing renders...can someone help me ?
Here is a codepen reference of what i mean: https://codepen.io/Contemplator191/pen/WNwgypY
And this is JSON : https://api.jsonbin.io/b/5f634e0c302a837e95680846
If codepen is not suitable/allowed here is whole JS for that
let items = [];
const buttons = document.querySelectorAll('button');
const wrapper = document.querySelector('section.products');
buttons.forEach(function (button) {
button.addEventListener('click',event => {
changeCategoryItems(event.target.dataset.category);
});
});
function changeCategoryItems(categoryId) {
let items = [];
const buttons = document.querySelectorAll('button');
const wrapper = document.querySelector('section.products');
const viewItems = (categoryId == 0 ) ? items : items.filter(item => item.category == categoryId);
wrapper.innerHTML = "";
viewItems.forEach(item => {
const div = document.createElement('div');
div.setAttribute("class", "product");
div.innerHTML = createItem(item);
wrapper.appendChild(div);
});
};
function createItem(item) {
return `
<div class="product__img">
<img src="${item.img}" class="">
</div>
<div class="product__name _tc">
<h4 class="">${item.heading}</h4>
</div>
<div class="text-desc product__desc">
<p class="">${item.description}</p>
</div>
<div class="product__bottom-content">
<span class="product__info">${item.info}</span>
${item.btn}
</div>
`
}
fetch('https://api.jsonbin.io/b/5f634e0c302a837e95680846')
.then(function (res) { return res.json() })
.then(function (data) {
items = data.items;
changeCategoryItems(1);
});`
In your fetch you're trying to assign data.items to the items variable but the api doesn't return data with an items node so items is undefined. It's possible the api changed their return format since the last time you used it which would explain why it worked previously.
this seems to fix it
.then(function (data) {
items = data;
changeCategoryItems(1);
});
Your issue is in this line:
items = data.items;
Now, the returned value is an array, hence you can use it as it is.
The updated codepen