Apendchild only apending once in ForEach method - javascript

function addOrgsToCard(Data) {
const orgs = document.getElementById("orgs");
const org = document.createElement("div");
org.classList.add("org");
Data.forEach((organisation)=>{
org.innerHTML = `
<div class="org">
<img src="${organisation.avatar_url}" alt="" />
<span>${organisation.login}</span>
</div>
`;
console.log(organisation.login);
orgs.appendChild(org);
});
}
This is the function to add organization details in a div of a profile card but the problem is apendchild method is appending only one organisation detail (the last one from the API call) ,Can anyone help me solve this issue?

org.innerHTML = overwrites the previous value.
So every time you loop over the data the previous elements get removed.
You should move the creation of your org element inside the forEach loop:
function addOrgsToCard(Data) {
const orgs = document.getElementById("orgs");
Data.forEach((organisation)=>{
const org = document.createElement("div");
org.classList.add("org");
org.innerHTML = `
<div class="org">
<img src="${organisation.avatar_url}" alt="" />
<span>${organisation.login}</span>
</div>
`;
console.log(organisation.login);
orgs.appendChild(org);
});
}

Try using only "append"(I am not sure if this works).

Related

Firestore forEach returns one item

I'm creating a project / to-do appliction with firestore.
I want to return all the current projects where the active user has been assigned to.
In a console.log(doc.id, doc.data()), I get the two projects where he has been signed up for.
But when I want to show them both on the home screen, I only see one project.
I don't know what I'm doing wrong
Anyone that can help me?
My function:
const returnProjects = async () => {
const list = document.querySelector<HTMLDivElement>('#projectList');
const projects = query(collectionGroup(db, 'projects'));
const querySnapshot = await getDocs(projects);
querySnapshot.forEach((doc) => {
const deadline = doc.data().Deadline;
const fireBaseTime = new Date(
deadline.seconds * 1000 + deadline.nanoseconds / 1000000,
);
const formatOptions = {
format: 'dd MMM yy',
};
console.log(doc.id, '>', doc.data());
const newElemement = document.createElement('div');
if (list) list.appendChild(newElemement).setAttribute('class', 'projectCard');
const card = document.querySelector<HTMLDivElement>('.projectCard');
if (card) { card.innerHTML = `
<h4>${doc.id, doc.data().Name}</h4>
<p>${fireBaseTime.toLocaleDateString('eng-BE', formatOptions)}</p>
<span>3</span>
`; }
});
};
my Html:
<main>
<div id="dashboard" class="dashboard">
<div class='dashboardUtils'>
<h3 id="dashboardName"></h3>
<span id="currentDate"></span>
</div>
<button id="editDashboard" class="secondary-button"></button>
</div>
<div id='dashboardEdits-form' class="editOpen">
<form id='dashboardEdits' class='edit-form'>
<div id='practicalDisplayname'>
<label for='displayname' class='form-label'>Username</label>
<input type='text' class='form-input' id="displaynameInput" name='displayname'></input>
</div>
</form>
<button id="confirmEdits" class="secondary-button">Save edits</button>
</div>
<div id='amountMessage'>
<h1 id='amountProjects'></h1>
</div>
<div id='projectList'></div>
</main>
A screenshot:
The problem is that for each document in the results you do:
const card = document.querySelector<HTMLDivElement>('.projectCard');
if (card) { card.innerHTML = `
<h4>${doc.id, doc.data().Name}</h4>
<p>${fireBaseTime.toLocaleDateString('eng-BE', formatOptions)}</p>
<span>3</span>
`; }
While you're creating a new project card for each result, the querySelector always returns the first card for the HTML. From the MDN documentation on querySelector:
An Element object representing the first element in the document that matches
So for the second document, you're replacing the innerHTML that you set for the first document.
To solve the problem, since you already have a reference to the element you just generated, add the innerHTML to that instead of looking it up with a query selector:
querySnapshot.forEach((doc) => {
...
const newElement = document.createElement('div');
if (list) list.appendChild(newElemement).setAttribute('class', 'projectCard');
newElement.innerHTML = `
<h4>${doc.id, doc.data().Name}</h4>
<p>${fireBaseTime.toLocaleDateString('eng-BE', formatOptions)}</p>
<span>3</span>
`;
})
The problem you are facing here is caused by the following behavior:
https://developer.mozilla.org/en-US/docs/Web/API/Node/appendChild#:~:text=The%20appendChild()%20method%20of%20the%20Node%20interface%20adds%20a%20node%20to%20the%20end%20of%20the%20list%20of%20children%20of%20a%20specified%20parent%20node.%20If%20the%20given%20child%20is%20a%20reference%20to%20an%20existing%20node%20in%20the%20document%2C%20appendChild()%20moves%20it%20from%20its%20current%20position%20to%20the%20new%20position.
What this means is that appendChild will remove the node if already present in the DOM, which is what we are seeing here. This can be easily solved by creating a deep clone of the node first using cloneNode, before appending it to the target div.

Firebase Paginate

I made the code below referring to the pagination document of FIREBASE.
( https://firebase.google.com/docs/firestore/query-data/query-cursors#web-v8_3 )
I know that 'limit(3)' prints 3 documents, but I don't know how to use the 'next' and 'last' variables.
What I want to implement is to show three of my posts per page and move to the next page when the button is pressed.
Since I just started web development, everything is difficult. please help me
var first = db.collection('product').orderBy('date').limit(3);
var paginate = first.get().then((snapshot)=>{
snapshot.forEach((doc) => {
console.log(doc.id);
var summary = doc.data().content.slice(0,50);
var template = `<div class="product">
<div class="thumbnail" style="background-image: url('${doc.data().image}')"></div>
<div class="flex-grow-1 p-4">
<h5 class="title">${doc.data().title}</h5>
<p class="date">${doc.data().date.toDate()}</p>
<p class = "summary">${summary}</p>
</div>
</div>
<hr>`;
$('.container').append(template);
})
You can try this function:
let lastDocSnap = null
async function getNextPage(lastDoc) {
let ref = db.collection('product').orderBy('date')
// Check if there is previos snapshot available
if (lastDoc) ref = ref.startAfter(lastDoc)
const snapshot = await ref.limit(3).get()
// Set new last snapshot
lastDocSnapshot = snapshot.docs[snapshot.size - 1]
// return data
return snapshot.docs.map(d => d.data())
}
While calling this function, pass the lastDocSnap as a param:
getNextPage().then((docs) => {
docs.forEach((doc) => {
// doc itself is the data of document here
// Hence .data() is removed from original code
console.log(doc.id);
var summary = doc.content.slice(0,50);
var template = `<div class="product">
<div class="thumbnail" style="background-image: url('${doc.image}')"></div>
<div class="flex-grow-1 p-4">
<h5 class="title">${doc.title}</h5>
<p class="date">${doc.date.toDate()}</p>
<p class = "summary">${summary}</p>
</div>
</div>
<hr>`;
$('.container').append(template);
})
})
Call this function at page load (as lastDocSnap will be null, it'll fetch first 3 docs). Call the same function when user clicks 'next' but this time as we have lastDocSnap, the startAfter method will be added to the query. This essentially means the query will first order the document by date and then fetch 3 documents after the document you pass in startAfter

Working with html template tag and can't seem to display whole array of information

This is my first time working with the html template tag. I don't know if I am doing it wrong but the output of the template is only showing the last item in the array. I've tried to loop through it and the foreach method but still only the last item shows. I know I am probably overriding the items as they are stacked on top of each other but I can't seem to figure it out Can someone point me in the right direction?
<div class="products-container">
<template>
<div class="product">
<img class="img">
<h2 class="item-title"></h2>
<h3 class="price"></h3>
</div>
</template>
</div>
const template = document.querySelector('template').content
const copyTemplate = document.importNode(template, true)
let shoppingList = [];
async function getData(data) {
const products = data.items.map(product => {
copyTemplate.querySelector('.item-title').textContent = product.title;
copyTemplate.querySelector('.price').textContent = product.price;
copyTemplate.querySelector('.img').src = product.image;
});
document.querySelector('.products-container').appendChild(copyTemplate);
shoppingList.push(products);
return products
}
I figured it out. I had to put the template and the copytemplate within the map function for it to work.

How can I add the same XML tags multiple times, with different content?

I have some problems with my code. I want to create an XML Document with JQuery / JavaScript. I am now at the point, where I want to create a few Tags and populate them each with the same tags but different content inside the tags.
Here is the code for better understand
function setItems(xmlDoc, channelTag){
const itemList = [];
const itemTitle = xmlDoc.createElement("title");
const itemLink = xmlDoc.createElement("link");
const itemGuid = xmlDoc.createElement("guid");
const itemMediaContent = xmlDoc.createElement("media:content");
const itemMediaDescription = xmlDoc.createElement("media:description");
itemList.push(itemTitle, itemLink, itemGuid, itemMediaContent, itemMediaDescription);
for (var i = 0; i < jsonObj.length; i++){
var item = xmlDoc.createElement("item");
channelTag.appendChild(item);
//Populate the <item> with the tags from "itemList" and content from "jsonObj"
$.each(itemList, function(index) {
$(channelTag).children('item')[i].appendChild(itemList[index]).textContent = jsonObj[0].title;
})
}
}
The Output of the code looks like this:
<item></item>
<item></item>
<item>
<title>Something</title>
<guid>Something</guid>
<link>Something</link>
<media:content>Something</media:description>
<media:description>Something</media:description>
</item>
It always populates the last item-Tag but not the ones above. What I want is that every item-Tag has the same child-Tags (e.g. title, link, guid and so on). Is there something i am missing some unique tags or something like that?
Edited:
Here is some minimal HTML and XML. The values for the function "xmlDoc" and "channelTag" just contains some Document Elements, where my items should be appended, like so:
<rss>
<channel>
<title>SomeTitle</title>
<atom:link href="Link" rel="self" type="application/rss+xml"/>
<link>SomeLink</link>
<description>SomeDesc</description>
<item></item>
<item></item>
<item></item>
</channel>
</rss>
<div class="col-5 col-sm-5 col-lg-3 order-2 count">
<a class="guid1"><img class="card-img image1"></a>
</div>
<div class="col-7 col-sm-7 col-lg-5 order-2">
<div class="card-body">
<a class="guid1">
<h5 class="card-title title1 overflow-title"></h5>
</a>
<p class="card-text body1 text-body overflow-body"></p>
<div class="card-body subtitle">
</div>
</div>
</div>
There are several issues with your code but the area we mostly want to focus on is this:
for (var i = 0; i < jsonObj.length; i++){
var item = xmlDoc.createElement("item");
channelTag.appendChild(item); // you're adding a node here
$.each(itemList, function(index) {
$(channelTag).children('item')[i].appendChild(... // and here
})
}
Instead of appending nodes multiple times per iteration, you should create and populate your node before add it it to channelTag.
Here's a way your could do it:
// use a "$" sign as a variable name prefix, so you know it's a Document Element and not a regular javascript variable
var $item = xmlDoc.createElement("item");
// you don't need jQuery for this iteration
itemList.forEach(function (item, index) {
$item.appendChild(itemList[index]).textContent = jsonObj[0].title;
});
// if "channelTag" is a Document Element, rename it "$channelTag"
$channelTag.appendChild(item);
Couple things about the code above:
you don't need jQuery, use forEach instead
there is no way telling what type is channelTag. If it is a selector (of type string), use $(selector), but you are using the appendChild() method before, suggesting it's actually a Document Element. In that case you don't need to wrap it with $()
I don't have the context needed to test this code, so no guarantee it'll work out of the box. But try and re-read your code and go through it top-to-bottom. For each variable, describe its type and value. I found that to be helpful when I'm lost in code.

Get values from elements inside DIV

I need to get values inside div on clicking a button, wich locates inside this div. Here is the html structure:
<div class="products__item">
<div class="products__content">
<a class="products__title" href="#">Altec Lansing Octiv Duo M202 акустическая система акустическая система</a>
<div class="products__priceholder">
<p class="products__price"><strong>86 590</strong> руб.</p>
<small class="products__id">ID. 10906</small>
</div>
<p class="exist">В наличии</p>
<div class="products__buttonholder">
Купить
</div>
</div>
</div>
When I click on .button-buy-modal, I need to get values from .products__title .products__price and .products__id, but the problem is that we have a lot same div's (product cards), and a lot of buttons inside them. I think that I should use something like $(this), but actually I don't know how.
I'm trying to test something like this, but it doesn't work:
$("a.button-buy-modal").click(function () {
$(this).find().closest('.products__priceholder').addClass('test1');
})
Here is a solution:
$("a.button-buy-modal").click(function () {
var prTitle = $(this).parent().parent().children('.products__title').html();
var prPrice = $(this).parent().parent().children('.products__priceholder').children('.products__price').children('strong').html();
var prId = $(this).parent().parent().children('.products__priceholder').children('.products__id').html();
var prImage = $(this).parent().parent().parent().children('.products__imageholder').children('.products__thumbnail').attr('src');
console.log(prTitle);
console.log(prPrice);
console.log(prId);
console.log(prImage);
})
$(this).parent().parent().find('.products__price').text()
will give you this - "86 590 руб."
$(this).parent().parent().find('.products__price').html()
will give you this -- "<strong>86 590</strong> руб."
To learn more about it as in how you can select any particular DOM element and read it or manipulate it etc, read here - http://api.jquery.com/category/selectors/
You can add an id to your parent div or use eq(x) to select the right group.
<div id="mydiv1" class="products__item">
<div class="products__content">
<a class="products__title" href="#">Altec Lansing Octiv Duo M202 акустическая система акустическая система</a>
<div class="products__priceholder">
<p class="products__price"><strong>86 590</strong> руб.</p>
<small class="products__id">ID. 10906</small>
</div>
<p class="exist">В наличии</p>
<div class="products__buttonholder">
Купить
</div>
</div>
</div>
Then with jQuery:
var title = $('#div1 .products__title').html();
/// OR
var title = $('.products__item:eq(0) .products__title').html();
var price = $('#div1 .products__price').html();
/// OR
var price = $('.products__item:eq(0) .products__price').html();
price = price.replace(/<[^>]+>/g, ""); // regex to remove tags
EDIT
If you want to use .closest(), you don't need .find()
$("a.button-buy-modal").click(function () {
$(this).closest('.products__priceholder').addClass('test1');
});
Thank's all for advices, I found a solution:
$("a.button-buy-modal").click(function () {
var prTitle = $(this).parent().parent().children('.products__title').html();
var prPrice = $(this).parent().parent().children('.products__priceholder').children('.products__price').children('strong').html();
var prId = $(this).parent().parent().children('.products__priceholder').children('.products__id').html();
var prImage = $(this).parent().parent().parent().children('.products__imageholder').children('.products__thumbnail').attr('src');
console.log(prTitle);
console.log(prPrice);
console.log(prId);
console.log(prImage);
})

Categories

Resources