I have a chat program that adds an element to the dom when messaged. This element adds properly, but when I go to query it from the dom it shows as null. I do a fetch to the server just before, but it has an await to slow it down. Any ideas?
Here is my javascript code
socket.on('receive_message', async function(data) {
let messaged_user_li = document.getElementById('messaged_user_li'+data['from_user']);
console.log('messaged_user_li: '+messaged_user_li)
if (messaged_user_li == null) {
console.log('if fired')
await fetch('/get_messaged_user/'+data['from_user']).then((response) => {
response.json().then((data2) => {
loadMessagedUser(data2[0].id, data2[0].avatar, data2[0].username, data2[0].last_seen);
});
});
console.log(data['from_user'])
messaged_user_li = document.getElementById('messaged_user_li'+data['from_user']);
//despite this element being added this log shows nulls which causes issues later on in the code.
console.log('messaged_user_li: '+messaged_user_li)
}
let message_user = document.getElementById('message_user'+data['from_user']);
let message_target = document.getElementById("message_target"+data['from_user']);
if (messaged_user_li.classList.contains('active') == false) {
messaged_user_li.classList.add('flash-message');
}
if (message_user != null) {
data = `
<li class="clearfix">
<div class="message-data text-right">
<span class="message-data-time">just now</span>
</div>
<div class="message other-message float-right">`+data['message']+`</div>
</li>`;
message_target.innerHTML += data;
//Move scroller to bottom when message received
myDiv = message_user.querySelector(".chat-history");
myDiv.scrollTop = myDiv.scrollHeight;
}
});
You are only waiting for this promise to complete:
await fetch('/get_messaged_user/'+data['from_user']).then((response) => {
// ...
});
Not this one inside the callback:
response.json().then((data2) => {
loadMessagedUser(data2[0].id, data2[0].avatar, data2[0].username, data2[0].last_seen);
});
Which is why your code continues executing and trying to get the added element (which hasn't been added yet).
To solve this, you could chain the then calls:
await fetch('/get_messaged_user/'+data['from_user'])
.then((response) => response.json())
.then((data2) => loadMessagedUser(data2[0].id, data2[0].avatar, data2[0].username, data2[0].last_seen));
Or use await again:
const response = await fetch('/get_messaged_user/'+data['from_user']);
const data2 = await response.json();
loadMessagedUser(data2[0].id, data2[0].avatar, data2[0].username, data2[0].last_seen);
Related
I need help by editing my code on how i can be able to display the data i have fetched from an external api and that from a local json file so that my card can show the user's avatar,name,occcupation,total number of impressions and total number of conversions on the same card.
Here is my code:
<script>
fetch("https://api.airtable.com/v0/appBTaX8XIvvr6zEC/tblYPd5g5k5IKIc98?api_key=key4v56MUqVr9sNJv")
.then((data)=>{
// console.log(data)
return data.json();
})
.then ((completedata)=>{
//console.log(completedata.records[0])
//an empty variable to catch the incoming data
let data1="";
// Then i added a html template to the data1 variable
completedata.records.forEach((values)=>{
data1+=`
<div class="card">
<span class="avatarLetter">${values.fields.Name.substr(0,1)}</span>
<p class="name">${values.fields.Name}</p>
<p class="occupation">${values.fields.occupation}</p>
</div> `;
});
document.querySelector("#card").innerHTML=data1;
})
.catch((error)=>{
console.log(error);
})
//pulling data from local json file
fetch('./logs.json')
.then((response) => {
return response.json();
})
.then((data) => {
// console.log(data);
let data1 = "";
let totalImpressions = 0;
let totalConversions = 0;
data.forEach((values) => {
totalImpressions += values.impression;
totalConversions += values.conversion;
data1 += `
<div class="card">
<p class="impression">${values.impression}</p>
<p class="conversion">${values.conversion}</p>
</div> `;
});
data1 += `<div>Total impressions = ${totalImpressions} Total conversions = ${totalConversions}</div>`;
document.querySelector("#card").innerHTML = data1;
})
.catch((error) => {
console.log(error);
})
</script>
When you write second time
document.querySelector("#card").innerHTML = data1;
you overwrite #card content.
The right way:
document.querySelector("#card").innerHTML += data1;
I am creating a project that when I click a certain category card I get the id of that category and redirect to movies screen.
I am aware that the row.eventlistener() in index.js it will be executed before the elements are rendered and that is why it does not pick the id. How should I add the event listener to each newly rendered item before adding it to the container so that I can get the id for each category card.
index.js
async function getCategories() {
let url = 'http://localhost:8080/movieCategories';
try {
let res = await fetch(url);
return await res.json();
} catch (error) {
console.log(error);
}
}
async function renderCategories() {
let categories = await getCategories();
let html = '';
categories.forEach(category => {
let htmlSegment = `
<div class="category-card" id=${category.id}>
<img src="./assets/images/sci-fi.jpg" alt="" class="card-img">
<div class="name">${category.name}</div>
</div>
`;
html += htmlSegment;
});
let container = document.querySelector('.category-grid');
container.innerHTML = html;
}
renderCategories();
document.querySelectorAll('div.category-card').forEach(row=>{
row.addEventListener('click',event=>{
console.log('Category clicked', event.currentTarget.id)
window.location= 'movies.html?categoryId=' +event.currentTarget.id;
});
});
index.html
<section class="category" >
<h2 class="section-heading">Category</h2>
<div class="category-grid">
</div>
</section>
You could also just add the event on the div itself directly when you create it and pass the id or whatever you want.
<div class="category-card" id=${category.id} onclick="onCatClick(${category.id})">
After this, you can move your logic inside a top level onCatClick function.
function onCatClick(id) {
console.log('Category clicked', id)
window.location= 'movies.html?categoryId=' + id;
}
This is if your styling/layout doesn't allow you to use an anchor element. Otherwise, you could simply replace the div with an anchor element:
You are not awaiting the call to renderCategories, move your event listener logic inside the renderCategories method OR You could use an immediately invoked async function expression or IIAFE for short, more about this here immediately-invoked-async-function-expression:
(async () => {
await renderCategories();
document.querySelectorAll('div.category-card').forEach(row => {
row.addEventListener('click', event => {
console.log('Category clicked', event.currentTarget.id)
window.location= 'movies.html?categoryId=' +event.currentTarget.id;
});
});
})();
try:
async function getCategories() {
let url = 'http://localhost:8080/movieCategories';
try {
let res = await fetch(url);
return await res.json();
} catch (error) {
console.log(error);
}
}
async function renderCategories() {
let categories = await getCategories();
let html = '';
categories.forEach(category => {
let htmlSegment = `
<div class="category-card" id=${category.id}>
<img src="./assets/images/sci-fi.jpg" alt="" class="card-img">
<div class="name">${category.name}</div>
</div>
`;
html += htmlSegment;
});
let container = document.querySelector('.category-grid');
container.innerHTML = html;
}
//es8
await renderCategories();
// fix timeout async render element
setTimeout(() => {
document.querySelectorAll('div.category-card').forEach(row=>{
row.addEventListener('click',event=>{
console.log('Category clicked', event.currentTarget.id)
window.location= 'movies.html?categoryId=' +event.currentTarget.id;
});
})
});
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.Thank you for your time 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;
});
}
Beacuse you are using innerHTML , if you want to save the previous contents you should use append or innerHTML + = .
Because everytime you make a search, the html var is populated with new data.
if you move the 'html' variable to the root scope, this should get you there:
// get meal list that matches with the ingredients
let html = ""; // <-- place it outside the function body
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 = ""; // <-- remove it from here
if(data.meals){
data.meals.forEach(meal => {
I've tried to retrieve a saved picture URL from Firebase Firestore, but I'm keep getting this error:
Uncaught (in promise) TypeError: Cannot read property 'picURL' of undefined.
I've tried a code, which you can see below. It's from "imgRef" and down.
HTML:
<div class="container">
<ul class="posts">
</ul>
</div>
JavaScript:
var postDocRef = db.collection('posts').doc(uid).collection('userPosts')
postDocRef.get().then(snapshot => {
setupPosts(snapshot.docs)
})
const posts = document.querySelector('.posts');
const setupPosts = (data) => {
let html = '';
data.forEach(doc => {
var docRefIDpost = docRef.id
const post = doc.data();
const li = `
<li>
<div class="title">${post.title}</div>
<div class="content">${post.content}</div>
<img class="img">
</li>
`;
var imgRef = db.collection('posts').doc(uid).collection('userPosts').doc(docRefIDpost);
imgRef.get().then(function(snapshot) {
const picURL = snapshot.data().picURL
if (picURL.exists) {
console.log(snapshot.data)
console.log(picURL)
var imgpost = document.querySelector(".img");
imgpost.src = picURL
}
})
html += li
})
posts.innerHTML = html;
}
});
The error is telling you that snapshot.data() returned undefined. As you can see from the API documentation, data() will return undefined when the requested document was not found. It's not clear here why, but your code should check for that first before accessing properties.
imgRef.get().then(function(snapshot) {
const data = snapshot.data()
if (data) {
const picURL = data.picURL
}
else {
// decide what you want to do if the document doesn't exist
}
})
I'm pretty new when it comes to working with Api's. I'm trying to query the URL string and return some Gifs.
const gifForm = document.querySelector("#gif-form");
gifForm.addEventListener("submit", fetchGiphs);
function fetchGiphs(e) {
e.preventDefault();
const searchTerm = document.querySelector("#search").value;
fetch(`https://api.giphy.com/v1/gifs/search?&q=${searchTerm}&limit=100&api_key=3mIxmBZUIIPyb8R69gtxaW8Hsh74dFKV`)
.then((response) => {return response.json(); })
.then(data => showGiphs(data.images.fixed_width.url))
.then(err => console.log(err));
}
function showGiphs(fixed_width) {
const results = document.querySelector("#results");
let output = '<div class="container">';
fixed_width.forEach((url) => {
console.log(url);
output += `
<img src="${data.images.original.fixed_width_url}"/>
`;
});
document.getElementById('results').innerHTML = output;
}
<form id="gif-form">
<input type="text" id="search">
<input type="submit" value="find">
</form>
<div id="results"></div>
If I remove the .then(data => showGiphs(data.images.fixed_width.url)) from the fetch and just console.log the data it's returning the search results that I want. However, when I try to map to the gif"data.images.fixed_width.url, I'm getting a console error of "Uncaught (in promise) TypeError: Cannot read property 'fixed_width' of undefined"
at fetch.then.then.data
Any help or push in the right direction would be awesome! Thank you! Also, if you'd like the view the demo you can view it here: https://codepen.io/Brushel/pen/jKgEXO?editors=1010
There is couple issues with your code.
The response from the API is an object, in this object there is data array, and each element in this array is information about each gif.
Try:
const gifForm = document.querySelector("#gif-form");
gifForm.addEventListener("submit", fetchGiphs);
function fetchGiphs(e) {
e.preventDefault();
const searchTerm = document.querySelector(".search").value;
fetch(`https://api.giphy.com/v1/gifs/search?&q=${searchTerm}&limit=100&api_key=3mIxmBZUIIPyb8R69gtxaW8Hsh74dFKV`)
.then((response) => {return response.json(); })
.then((resp => {
// Here we get the data array from the response object
let dataArray = resp.data
// We pass the array to showGiphs function
showGiphs(dataArray);
}))
.catch(err => console.log(err)); // We use catch method for Error handling
}
function showGiphs(dataArray) {
const results = document.querySelector(".results");
let output = '<div class="container">';
dataArray.forEach((imgData) => {
output += `
<img src="${imgData.images.fixed_width.url}"/>
`;
});
document.querySelector('.results').innerHTML = output;
}
<form id="gif-form">
<input type="text" class="search">
<input type="submit" value="find">
</form>
<div class="results"></div>
I hope this will help.
The response has a data property which is an array. If you want the first GIF in that array, that would look like this: data.data[0].images.fixed_width.url. So the full line would be .then(data => showGiphs(data.data[0].images.fixed_width.url)).