Why is 'undefined' appearing in my webpage when it is not in the HTML file? - javascript

When I load my webpage up, the word "undefined" appears in a <ul> that is then modified with javascript and .innerHTML after the page is loaded up.
This is how the HTML appears:
<div>
<ul>
"undefined
"
<li>...</li>
</ul>
</div>
If there are no <li>'s, then it just appears as so:
<div>
<ul>undefined</ul>
</div>
Here is the Javascript function that seems to be causing it. The data parameter is the data from the database.
const setupBookmarks = (data) => {
let favoritesHTML, bookmarksHTML = '';
const bookmarkData = [];
if (data && data.length) {
data.forEach(doc => {
const bookmarkId = Math.floor(Math.random() * 10000000);
const bookmark = doc.data();
if (bookmark.favorite) {
favoritesHTML += bookmarkHTML(bookmark, bookmarkId);
} else {
bookmarksHTML += bookmarkHTML(bookmark, bookmarkId);
}
bookmarkData.push({bookmark, bookmarkId});
})
}
favoriteList.innerHTML = favoritesHTML;
bookmarkList.innerHTML = bookmarksHTML;
return bookmarkData;
}
Here is shortened version of the bookmarkHTML function, which simply adds the HTML to the favoritesHTML and bookmarksHTML variables in the previous function:
const bookmarkHTML = (bookmark, bookmarkId) => {
let html = '';
const li = `
<li>
<div class="row card-margin">
<div class="col s12 m12 card-padding">
<div class="card-panel teal card-size">
<img src='${"https://www.google.com/s2/favicons?domain_url=" + bookmark.url}' alt='icon' class='img-size'>
<a class='white-text' href='${bookmark.url}' target='_blank'>${bookmark.website}</a>
<button class='btn-flat btn-small right cross' id='${bookmarkId}'><i class='material-icons'>clear</i></button>
<button class='btn-flat waves-light btn-small right listener' id='${bookmarkId}'><i class='material-icons left'>compare_arrows</i>switch</button>
</div>
</div>
</div>
</li>
`;
html += li;
return html;
}
What could be causing this 'undefined' to appear in the HTML of my website upon being loaded up?

Related

Dynamically generated cards with JS

I'm trying to make a some dynamically generated bootstrap cards with a button clearing the page and showing the info (which I have stored on a Firebase project). I managed to make the cards show properly but I'm blocked and can't find a way to make each button show a different information.
So basically I have this:
Cards
Content showing
It has to be something close to the eventListener or another way to write the code... I think the way I did it is why its showing all the information on the same card, but how can I write the code right? I'm trying for 2 days and can't work around it.
OBS: I know my code can be a little messy or could be best ways to do some stuff but I'm still learning.
import { getWikiTI } from './prevenirWiki.js'
const dataCard = document.getElementById('data');
async function loadData() {
let i = 0;
const data = await getWikiTI()
dataCard.innerHTML = data.map(d => `
<div class="card border-primary mb-3 fb-item" style="max-width: 20rem;">
<div class="card-header">${d.setor}</div>
<div class="card-body">
<h4 class="card-title">${d.descricao}</h4>
<button type="button" class="btn btn-lg btn-primary fb-item" id="carregar-card">Carregar</button>
</div>
</div>
`,
console.log(data),
console.log(i)
).join('')
const btnCarregar = document.getElementById('carregar-card')
btnCarregar.addEventListener('click', () => {
dataCard.innerHTML = data.map(function (d) {
return `${d.conteudo}`;
})
document.body.appendChild(dataCard)
})
}
document.onload = loadData();
EDIT: Alright ! I managed to get it working... not the best code in the world but it's something :) If anyone has any opinions or ideias on how to improve it, im all ears.
import { getWikiTI } from './prevenirWiki.js'
const dataCard = document.getElementById('data');
async function loadData() {
let i = 0;
const data = await getWikiTI()
dataCard.innerHTML = data.map(d => `
<div class="card border-primary mb-3 fb-item" style="max-width: 20rem;">
<div class="card-header">${d.setor}</div>
<div class="card-body">
<h4 class="card-title">${d.descricao}</h4>
<button type="button" class="btn btn-lg btn-primary fb-item" id="carregar-card${i++}">Carregar</button>
<p hidden class="card-text">${d.conteudo}</p>
</div>
</div>
`,
).join('')
for (let i = 0; i <= data.length; i++) {
let btnCarregar = document.getElementById(`carregar-card${i}`)
btnCarregar.addEventListener('click', () => {
dataCard.innerHTML = data[i].conteudo;
document.body.appendChild(dataCard)
})
}
}
document.onload = loadData();

HTML not responding with JavaScript filter

This is an update to a previous question I had. I am making a simple page that filters the first 9 Pokemon by their type after clicking a button. I will include the code below. I ran the code through a validator and there were no issues. Also, I don't see any errors in the console. I did console the type and the array every time a button is clicked so it looks like the filter is working. But, nothing is happening on the page.
Here's a link to the page: https://sobreratas.github.io/pokemonfilter/
const pokemon=[{name:"Bulbasaur",image:"images/bulbasaur.png",number:1,type:"Grass"},{name:"Ivysaur",image:"images/ivysaur.png",number:2,type:"Grass"},{name:"Venusaur",image:"images/venusaur.png",number:3,type:"Grass"},{name:"Charmander",image:"images/charmander.png",number:4,type:"Fire"},{name:"Charmeleon",image:"images/charmeleon.png",number:5,type:"Fire"},{name:"Charizard",image:"images/charizard.png",number:6,type:"Fire"},{name:"Squirtle",image:"images/squirtle.png",number:7,type:"Water"},{name:"Wartortle",image:"images/wartortle.png",number:8,type:"Water"},{name:"Blastoise",image:"images/blastoise.png",number:9,type:"Water"}];
const sectionCenter = document.querySelector('.section-center');
const btnContainer = document.querySelector('.btn_container');
window.addEventListener('DOMContentLoaded', function() {
displayPokemonMenu();
displayMenuButtons();
})
function displayPokemonMenu(array) {
let pokemonMap = pokemon.map(function(item) {
return `<div class="pokemon-item">
<img class="photo" src="${item.image}" alt="${item.name}">
<h2>${item.name}</h2>
<h3># <span id="number">${item.number}</span></h3>
<h4>Type: <span id="type">${item.type}</span></h4>
</div>`
})
pokemonMap = pokemonMap.join('');
sectionCenter.innerHTML = pokemonMap;
}
function displayMenuButtons() {
let types = pokemon.reduce(function(values, item) {
if (!values.includes(item.type)) {
values.push(item.type);
}
return values
}, ['All']);
const typesBtns = types.map(function(type) {
return `<button class="filter-btn" data-id="${type}">${type}</button>`
}).join('');
btnContainer.innerHTML = typesBtns;
const filterBtns = btnContainer.querySelectorAll('.filter-btn');
filterBtns.forEach(function(btn) {
btn.addEventListener('click', function(e) {
let type = e.currentTarget.dataset.id;
console.log(type)
const pokemonFilter = pokemon.filter(function(item) {
if (type === item.type) {
return item;
}
})
if (type === 'All') {
displayPokemonMenu(pokemon);
} else {
displayPokemonMenu(pokemonFilter);
console.log(pokemonFilter);
}
})
})
}
<img class="logo" src="images/logo.png" alt="logo">
<br>
<br>
<div class="btn_container">
<button class="filter-btn" data-id="All">All</button>
</div>
<div class="section-center">
<div class="pokemon-item">
<img class="photo" src="images/bulbasaur.png" alt="Bulbasaur">
<h2>Bulbasaur</h2>
<h3># <span id="number">1</span></h3>
<h4>Type: <span id="type">Grass</span></h4>
</div>
</div>

Format date and text data from API

I am working with Github API and I am displaying the data from users. When the date is displayed I want it to only have the date user.created_at with DD/MM/YY and not the whole hour. Also when the user has no Biography user.bio the data appears as null, and I want it to display the text 'The user has no bio'. I have not figured out the way to do both things so if you could help me I would very much appreciate it
Here below the code:
const APIURL = 'https://api.github.com/users/'
const main = document.getElementById('main')
const form = document.getElementById('form')
const search = document.getElementById('search')
async function getUser(username){
try{
const { data } = await axios(APIURL + username)
createUserCard(data)
getRepos(username)
}catch (err){
if(err.response.status == 404){
createErrorCard('No profile with this Username')
}
}
}
async function getRepos(username){
try{
const { data } = await axios(APIURL + username + '/repos?sort=created')
addReposToCard(data)
}catch (err){
createErrorCard('Problem Fetching Repos')
}
}
function createUserCard(user){
const cardHTML = `
<div class="card">
<div>
<img src="${user.avatar_url}" alt="${user.name}" class="avatar">
</div>
<div class="user-info">
<div class="header">
<h2>${user.name}</h2>
<p class="date">Joined ${user.created_at}</p>
</div>
<p>#${user.login}</p>
<p>${user.bio}</p>
<ul>
<div class="list">
<li>${user.followers} </li>
<li>${user.following} </li>
<li>${user.public_repos} </li>
</div>
<div class="list-names">
<strong>Followers</strong>
<strong>Following</strong>
<strong>Repos</strong>
</div>
</ul>
<div class="additional-data">
<p class="location"><img src="./img/location.svg" alt="Location" class="img" /> ${user.location} </p>
<img src="./img/link.svg" alt="Link" class="img" />${user.html_url}
</div>
<div id="repos"></div>
</div>
</div>`
main.innerHTML = cardHTML
}
function createErrorCard(msg){
const cardHTML = `
<div class="card">
<h1>${msg}</h1>
</div>
`
main.innerHTML = cardHTML
}
function addReposToCard(repos){
const reposEl = document.getElementById('repos')
repos
.slice(0, 5)
.forEach(repo => {
const repoEl = document.createElement('a')
repoEl.classList.add('repo')
repoEl.href = repo.html_url
repoEl.target = '_black'
repoEl.innerText = repo.name
reposEl.appendChild(repoEl)
})
}
form.addEventListener('submit', (e) => {
e.preventDefault()
const user = search.value
if(user){
getUser(user)
search.value = ''
}
})
in the case of user.bio you can use the ternary operator:
(conditional)?value when conditional true: value when conditional false
for example:
${(user.bio!="null")?user.bio:"The user has no bio"}
or
${(user.bio!=null)?user.bio:"The user has no bio"}
in the case of date github helps us giving us a formated string that we can cast to a date using new Date() and format it using Date.prototype.toLocaleString()
${(new Date(user.created_at)).toLocaleDateString()}
in this case is not needed to pass parameters to toLocaleDateString() but I encourage you to read about this function here

Navbar TypeError: Cannot read property 'classList' of null

I am trying to implement a navbar to html which has the effect dynamically switch pages instead of changing links via href. The trick I'm using to accomplish this is by adding a classList of is-active to the div elements in the section tag.
Here is an example code of the generated HTML :
navBar.js
const navbarItem = [
{
navitem : "About",
link: 1
},
{
navitem : "Legal",
link: 2
},
{
navitem : "Contact",
link: 3
}
];
window.onload = function navLoad() {
const navbar = document.getElementById('navbar');
navbar.innerHTML =
`
<div class="toggle">
<i class="fa fa-bars menu" aria-hidden="true"></i>
</div>
<ul class="nav-list">
<li class="tab is-active">
<a onclick="renderNav()">Home</a>
</li>
${navbarItem.map(loadNavitems).join(" ")}
</ul>
`
}
function loadNavitems(navItems) {
return `
<li class="tab">
<a data-switcher data-id="${navItems.link}" onclick="renderNav(); navSwap();">${navItems.navitem}</a>
</li>
`
}
function renderNav(){
const pages = document.getElementById('main');
document.getElementById('alphabetButtons').style.display = "block";
pages.innerHTML =
`
<section class="pages">
${navbarItem.map(item => `
<div class="page" data-page="${item.link}">
<h1>${item.navitem}</h1>
</div>
`).join('')}
</section>
`
};
And here is the code which takes care of the page switching:
navSwitcher.js
function navSwap() {
const tab_switchers = document.querySelectorAll('[data-switcher]');
for (let input of tab_switchers) {
const page_id = input.dataset.switcher;
console.log(page_id);
input.addEventListener('click', function () {
if(document.querySelector('.nav-list .tab.is-active')){
document.querySelector('.nav-list .tab.is-active').classList.remove('is-active');
console.log('removed');
}
if(input.parentNode.classList.contains('tab')){
input.parentNode.classList.add('is-active');
}
//SwitchNav(page_id);
});
}
}
function SwitchNav(page_id) {
const currentPage = document.querySelector('.pages .page');
const next_page = document.querySelector(`.pages .page[data-page="${page_id}"]`);
console.log(next_page);
if(document.querySelector('.pages .page.is-active')){
document.querySelector('.pages .page.is-active').classList.remove('is-active');
}
next_page.classList.add('is-active');
}
Update : The issue is that causes the error comes when attempting to place [data-page="${page_id}"] after .page inside the querySelector. When console logging, [data-page="${page_id}"] will return null. This is weird because data-page is named correctly in the renderNav function which holds the div of class page.
Hence my hypothesis now is that the issue comes from [data-page="${page_id}"] in the SwitchNav(page_id) function. My question is why is this occuring if everything is named correctly?
Fixes tried:
Attempted to change the for/of loop to a regular for loop inside the navSwap function.
Inside the navSwap function, const page_id = input.dataset.tab; was changed to const page_id = input.dataset.switcher; which now returns 3 items.
So after some digging around, in the end it was actually const page_id = tab_switcher.dataset.tab; which was returning undefined, hence causing the error to happen. In reality it was returning id so I changed it to tab_switcher.dataset.id;.
Here is the final attempt at the code (i placed everything in one single file):
navBar.js:
const navbarItem = [
{
navitem : "About",
link: 1
},
{
navitem : "Legal",
link: 2
},
{
navitem : "Contact",
link: 3
}
];
window.onload = function navLoad() {
const navbar = document.getElementById('navbar');
navbar.innerHTML =
`
<div class="toggle">
<i class="fa fa-bars menu" aria-hidden="true"></i>
</div>
<ul class="nav-list">
<li class="tab is-active">
<a onclick="renderNav()">Home</a>
</li>
${navbarItem.map(loadNavitems).join(" ")}
</ul>
`
}
function loadNavitems(navItems) {
return `
<li class="tab">
<a data-switcher data-id="${navItems.link}" onclick="renderNav(); navSwap();">${navItems.navitem}</a>
</li>
`
}
function renderNav(){
const pages = document.getElementById('main');
document.getElementById('alphabetButtons').style.display = "block";
pages.innerHTML =
`
<section class="pages">
${navbarItem.map(item => `
<div class="page" data-page="${item.link}">
<h1>${item.navitem}</h1>
</div>
`).join('')}
</section>
`
};
function navSwap() {
const tab_switchers = document.querySelectorAll('[data-switcher]');
for (let i = 0; i < tab_switchers.length; i++) {
const tab_switcher = tab_switchers[i];
const page_id = tab_switcher.dataset.id;
tab_switcher.addEventListener('click', function(){
document.querySelector('.nav-list .tab.is-active').classList.remove('is-active');
tab_switcher.parentNode.classList.add('is-active');
SwitchNav(page_id);
});
}
}
function SwitchNav(page_id) {
const currentPage = document.querySelector('.pages .page');
const next_page = document.querySelector(`.pages .page[data-page="${page_id}"]`);
console.log(next_page);
if(document.querySelector('.pages .page.is-active')){
currentPage.classList.remove('is-active');
}
if(document.querySelector('.pages .page')){
next_page.classList.add('is-active');
}
}
Although the code generates the intended results, I have to click twice on the tag to fire it's onclick event. If anybody knows why this occurs it would be of much help to know in the comments.
Also let me know if this is a viable fix. Thanks again :)

Json file struggling with the length

So, i got everything almost working as i want it, just a mistake that im struggling. Everytime i search for an item, when the result for that item shows the length is repeated.
When i search for ox there are 2 results and that is correct, but the length (2) shows in both of them, i only display one
[Code]
const resultHtml = (itemsMatch) => {
if (itemsMatch.length > 0) {
const html = itemsMatch
.map(
(item) => `
<span>${itemsMatch.length}</span>
<div class="card">
<div class="items-img">
</div>
<div class="items-info">
<h4>${item.title}</h4>
<small>${item.path}</small>
</div>
</div>
`
)
.join('');
//console.log(html);
itemList.innerHTML = html;
}
};
////
Question 2
I got one more question, i was trying to get the image from the Json and what i got was the path haha
why the apth and not the img
const resultHtml = (itemsMatch) => {
if (itemsMatch.length > 0) {
const html =
`<span class="items-results">${itemsMatch.length} Resultados</span>` +
itemsMatch
.map(
(item) => `
<div class="card">
<div class="items-img">
${item.image}
</div>
<div class="items-info">
<h4>${item.title}</h4>
<small>${item.path}</small>
</div>
</div>
`
)
.join('');
console.log(html);
itemList.innerHTML = html;
}
};
If you move <span>${itemsMatch.length}</span> out of your map callback, it will not repeat for each item. Read more about map() here.
Replace:
const html = itemsMatch
.map(
(item) => `
<span>${itemsMatch.length}</span>
... more HTML here
`
)
.join('');
With this:
const html = `<span>${itemsMatch.length}</span>` + (
itemsMatch
.map(
(item) => `
<div class="card">
<div class="items-img">
</div>
<div class="items-info">
<h4>${item.title}</h4>
<small>${item.path}</small>
</div>
</div>
`
)
.join('')
);
Regarding your image issue:
You are just outputting the path and that's why it's printing out just the path. If you are trying to display an image then put the path as source of <img> tag.
So, instead of just:
${item.image}
Use:
<img src="${item.image}">

Categories