Problem closing a card modal on JavaScript - javascript

I have a card that opens when the user select a specific project and click on it. Then the card opens showing all data. The problem that I'm facing is that when I'm closing the card (clicking on the span "x") the card is removed, but it's created again as soon as the other card has been removed. I know that I need to break the loop when the card is removed, but I'm not being able to fix this problem. What am I missing?
Code:
async function createCard(i) {
div.appendChild(exit);
div.appendChild(title);
div.appendChild(innerDiv);
innerDiv.appendChild(createGifWrapper(i));
innerDiv.appendChild(createDescriptionWrapper(i));
}
function handleCard() {
for (let i = 0; i < cards.length; i++) {
// cards is an Array of objects that contain all data from each project
cards[i].addEventListener('click', listerner = function(e) {
if(document.querySelector('.card')) {
if(document.getElementById(i)) {
console.log('has card and id')
}
} else {
if(this.id === `card-${i+1}`) {
title.textContent = titles[i].innerText;
this.appendChild(div);
div.setAttribute('id', i+1);
createCard(i);
// Exit = Span
exit.addEventListener('click', function() {
console.log(document.getElementById(i+1));
let card = document.getElementById(i+1);
return card.remove();
// Should break here
})
}
}
})
}
}

As nobody has answered me. I found a solution by myself and would like to share if anyone step up on this problem one day.
import { langData } from '../Languages/langData';
export async function portfolio() {
/**
* Data for each card project
*/
let cardData
let defaultText
switch(location.hash) {
case('#pt-br'):
cardData = langData['pt-br'].portfolio.cardData
defaultText = langData['pt-br'].portfolio.defaultTitles
console.log(defaultText)
break;
case('#en'):
cardData = langData['en'].portfolio.cardData
defaultText = langData['en'].portfolio.defaultTitles
break;
}
await console.log('teste')
/**
* Creating card
*/
const cards = document.querySelectorAll('.project-card')
// Exit button
const exit = document.createElement('span')
// Card Title
const titles = document.querySelectorAll('.project-title')
let title = document.createElement('h3')
title.setAttribute('class', 'card-title')
// GIFs
const gifWrapper = document.createElement('div')
const gifMobile = document.createElement('img')
const gifDesktop = document.createElement('img')
// Description
const descriptionTitle = document.createElement('h4')
const description = document.createElement('h5')
const toolsTitle = document.createElement('h4')
const tools = document.createElement('h5')
const descriptionWrapper = document.createElement('div')
// Inner Div - card-details
const innerDiv = document.createElement('div')
let divCard = document.createElement('div')
divCard.setAttribute('class', 'card')
async function destroyCard() {
await exit.remove()
await gifMobile.remove()
await gifDesktop.remove()
await gifWrapper.remove()
await descriptionTitle.remove()
await description.remove()
await toolsTitle.remove()
await tools.remove()
await descriptionWrapper.remove()
await innerDiv.remove()
await divCard.remove()
}
function createExit() {
exit.setAttribute('class', 'card-exit')
exit.textContent = 'x'
exit.addEventListener('click', function() {
destroyCard()
})
return exit
}
function createInnerDiv(i) {
innerDiv.setAttribute('class', 'card-details')
innerDiv.appendChild(createGifWrapper(i))
innerDiv.appendChild(createDescriptionWrapper(i))
return innerDiv
}
// GIF - Return gifWrapper div with gifs mobile and desktop inside it
function createGifWrapper(i) {
gifDesktop.setAttribute('src', cardData[i].gifDesktop)
gifMobile.setAttribute('src', cardData[i].gifMobile)
gifWrapper.setAttribute('class', 'gif-wrapper')
gifWrapper.appendChild(gifDesktop)
gifWrapper.appendChild(gifMobile)
return gifWrapper
}
// Description - return descriptionWrapper div with the description and tools
function createDescriptionWrapper(i) {
// Description
descriptionTitle.textContent = defaultText[0]['DescriptionTitle']
description.setAttribute('class', 'description')
description.textContent = cardData[i].description
// Toools
toolsTitle.textContent = defaultText[0]['Tools']
tools.setAttribute('class', 'description')
tools.textContent = cardData[i].tools
// Wrapper
descriptionWrapper.setAttribute('class', 'card-description')
descriptionWrapper.appendChild(descriptionTitle)
descriptionWrapper.appendChild(description)
descriptionWrapper.appendChild(toolsTitle)
descriptionWrapper.appendChild(tools)
return descriptionWrapper
}
async function createCard(i) {
divCard.appendChild(createExit())
divCard.appendChild(title)
title.textContent = titles[i].innerText
divCard.appendChild(createInnerDiv(i))
divCard.setAttribute('id', i+1)
cards[i].appendChild(divCard)
}
function toggleOpen(i) {
createCard((i.path[2].id.substr(5))-1)
divCard.addEventListener('click', e => e.stopPropagation())
}
for (let i = 0; i < cards.length; i++) {
cards[i].addEventListener('click', toggleOpen)
}
}

Related

How to update data to a div without deleting it and remaking it in a javascript interval?

I have this interval code:
const interval = setInterval(function() {
run(); //Load Divs
$('.parentDiv').remove(); //Remove Divs
}, 10000); //Loop in 10 Seconds
The issue is that this is NOT SMOOTH at all. Is there a better way to refresh the divs with data than to delete them and remake them?
You can see the current code on this test site https://footballify.net/test/. It is not smooth.
code:
//Pull API Data for UTC
var run = async () => {
const res = await fetch(`https://v3.football.api-sports.io/fixtures?date=${isoStr}`, {
headers: {
'X-RapidAPI-Host': "v3.football.api-sports.io",
"X-RapidAPI-Key": "e54f3d3972ca8251c1259694b49948de"
},
});
//Parse JSON
const json = (await res.json())?.response;
//Map desiredOrder onto API Call
const ordered = desiredOrder.map((id) => json.filter(({ league }) => league?.id === id));
//Remove any Null Values
const filtered = ordered.filter(e => e.length);
//arrLeagues created to avoid duplicate leagues
let arrLeagues = [];
console.log(filtered)
//Loop through leagues
for (i = 0; i < filtered.length-1; i++) {
//Loop through games of Leagues
for (x=0; x<filtered[i].length;x++){
//Create Parent Div For Data
let parent = document.createElement("div")
parent.className = 'parentDiv'
//League Duplication not allowed
if (arrLeagues.includes(filtered[i][x].league.id)) {
} else {
arrLeagues.push(filtered[i][x].league.id)
//League Name
let league = document.createElement("div")
league.className = 'league'
league.innerHTML = filtered[i][x].league.name + `<img class='flag' src=${filtered[i][x].league.flag}>`
parent.appendChild(league)
}
//Home Container
let child1 = document.createElement("div")
child1.className = 'childDiv'
//Game Status
let gameStatus = document.createElement("div")
gameStatus.className = 'status'
gameStatus.innerHTML = filtered[i][x].fixture.status.short
parent.appendChild(gameStatus)
//Home Name
let homeTeamName = document.createElement("div")
homeTeamName.className = 'team1'
homeTeamName.innerHTML = filtered[i][x].teams.home.name
parent.appendChild(homeTeamName)
//Home Score
let homeTeamScore = document.createElement("div")
homeTeamScore.className = 'score1'
parent.appendChild(homeTeamScore)
//Away Container
let child2 = document.createElement("div")
child2.className = 'childDiv'
//Away Name
let awayTeamName = document.createElement("div")
awayTeamName.className = 'team2'
awayTeamName.innerHTML = filtered[i][x].teams.away.name
parent.appendChild(awayTeamName)
//Away Score
let awayTeamScore = document.createElement("div")
awayTeamScore.className = 'score2'
parent.appendChild(awayTeamScore)
//Push all Data to DOM
document.querySelector('.parentContainer').appendChild(parent);
if (String(filtered[i][x].fixture.status.short) === 'NS') {
homeTeamScore.innerHTML = 0
homeTeamScore.classList.add('hide')
awayTeamScore.innerHTML = 0
awayTeamScore.classList.add('hide')
} else if (String(filtered[i][x].fixture.status.short) === 'CANC') {
homeTeamScore.classList.add('hide')
homeTeamScore.innerHTML = 0
awayTeamScore.classList.add('hide')
awayTeamScore.innerHTML = 0
gameStatus.innerHTML = 'NA'
} else if (String(filtered[i][x].fixture.status.short) === 'FT') {
homeTeamScore.classList.remove('hide')
homeTeamScore.innerHTML = filtered[i][x].goals.home
awayTeamScore.classList.remove('hide')
awayTeamScore.innerHTML = filtered[i][x].goals.away
} else if (String(filtered[i][x].fixture.status.short) === 'HT') {
homeTeamScore.classList.remove('hide')
homeTeamScore.classList.add('live')
homeTeamScore.innerHTML = filtered[i][x].goals.home
awayTeamScore.classList.remove('hide')
awayTeamScore.classList.add('live')
gameStatus.classList.add('live')
awayTeamScore.innerHTML = filtered[i][x].goals.away
} else if (String(filtered[i][x].fixture.status.short) == '1H') {
homeTeamScore.classList.remove('hide')
homeTeamScore.classList.add('live')
homeTeamScore.innerHTML = filtered[i][x].goals.home
awayTeamScore.classList.remove('hide')
awayTeamScore.classList.add('live')
awayTeamScore.innerHTML = filtered[i][x].goals.away
gameStatus.classList.add('live')
gameStatus.innerHTML = filtered[i][x].fixture.status.elapsed + "′"
} else if (String(filtered[i][x].fixture.status.short) == 'ET') {
homeTeamScore.classList.remove('hide')
homeTeamScore.classList.add('live')
homeTeamScore.innerHTML = filtered[i][x].goals.home
awayTeamScore.classList.remove('hide')
awayTeamScore.classList.add('live')
awayTeamScore.innerHTML = filtered[i][x].goals.away
gameStatus.classList.add('live')
gameStatus.innerHTML = filtered[i][x].fixture.status.elapsed + "′"
} else if (String(filtered[i][x].fixture.status.short) == '2H') {
homeTeamScore.classList.remove('hide')
homeTeamScore.classList.add('live')
homeTeamScore.innerHTML = filtered[i][x].goals.home
awayTeamScore.classList.remove('hide')
awayTeamScore.classList.add('live')
awayTeamScore.innerHTML = filtered[i][x].goals.away
gameStatus.classList.add('live')
gameStatus.innerHTML = filtered[i][x].fixture.status.elapsed + "′"
} else {}
//If Home Wins
if (filtered[i][x].teams.home.winner == true) {
homeTeamName.classList.add('winner')
awayTeamName.classList.add('loser')
homeTeamScore.classList.add('winner')
awayTeamScore.classList.add('loser')
} else if (filtered[i][x].teams.away.winner == true) {
//If Away Wins
awayTeamName.classList.add('winner')
homeTeamName.classList.add('loser')
awayTeamScore.classList.add('winner')
homeTeamScore.classList.add('loser')
//Match Not Started or Cancelled
} else if (filtered[i][x].fixture.status.short == 'NS' || 'CANC'){
homeTeamName.classList.add('winner')
homeTeamScore.classList.add('winner')
awayTeamScore.classList.add('winner')
awayTeamName.classList.add('winner')
} else {
//Draw
homeTeamName.classList.add('loser')
awayTeamName.classList.add('loser')
homeTeamScore.classList.add('loser')
awayTeamScore.classList.add('loser')
}
}
}
};
run();
so it is a fetch of data along with appending the data do dom
Try this:
const interval = setInterval(async function() {
if ($('.parentDiv').length) { // Check if the parent div exists
$('.parentDiv').remove(); // Remove the div
}
await run(); // Fire the run function to create a new div with new data
}, 10000);
Edit:
I think the problem with my above answer is that there is still a delay: The time that it takes for your code to both get the data from your API and build your div with all of its child elements. With this in mind, try deleting the div within the `run()` function right before a new div is created. This way, all of the processing can be done while the old data is still visible. Try this:
Interval code:
const interval = setInterval(async function() {
await run();
});
Run function:
$('.parentDiv').remove(); // Remove the old div
//Push all Data to DOM
document.querySelector('.parentContainer').appendChild(parent);

Local storage in JS not loading items problem

I have a problem with the local storage it seems the items are getting saved to local storage but I cannot make it work to load at start.
Any tips and advice much appreciated.
I am posting the code below.
const input = document.getElementById('input');
const list = document.getElementById('list');
const addButton = document.getElementById('addButton');
const completed = document.getElementById("completed");
let LIST;
let id;
let loadSTORAGE = localStorage.getItem("STORAGE");
if (loadSTORAGE) {
LIST = JSON.parse(loadSTORAGE);
id = LIST.length;
loadList(LIST);
} else {
LIST = [];
id = 0;
}
function loadList() {
LIST.forEach(function() {
addTask();
});
}
addButton.addEventListener("click", addTask);
input.addEventListener("keyup", function(event) {
(event.keyCode === 13 ? addTask() : null)
})
function addTask() {
const newTask = document.createElement("li");
const delBtn = document.createElement("button");
const checkBtn = document.createElement("button");
delBtn.innerHTML = "<button>Reset</button>"
checkBtn.innerHTML = "<button>Done</button>"
if (input.value !== "") {
newTask.textContent = input.value;
list.appendChild(newTask);
newTask.appendChild(checkBtn);
newTask.appendChild(delBtn);
LIST.push({
name: input.value,
id: id,
});
id++
input.value = "";
console.log(LIST);
localStorage.setItem("STORAGE", JSON.stringify(LIST));
}
checkBtn.addEventListener("click", function() {
const parent = this.parentNode
parent.remove();
completed.appendChild(parent);
});
delBtn.addEventListener("click", function() {
const parent = this.parentNode
parent.remove();
});
}
You need to break out the logic of building the item and getting the value. Something like the following where the addTask just makes sure there is input and calls a method that builds an item. Now with the localstorage call, you can call just the code that builds the item.
const input = document.getElementById('input');
const list = document.getElementById('list');
const addButton = document.getElementById('addButton');
const completed = document.getElementById("completed");
const loadSTORAGE = localStorage.getItem("STORAGE");
const LIST = loadSTORAGE ? JSON.parse(loadSTORAGE) : [];
let id = LIST.length;
loadList(LIST);
function loadList() {
LIST.forEach(function(data) {
addTaskElement(data);
});
}
function addTask() {
if (input.value !== "") {
cons newItem = {
name: input.value,
id: id,
};
LIST.push(newItem);
id++;
localStorage.setItem("STORAGE", JSON.stringify(LIST));
input.value = "";
addTaskElement(newItem);
}
}
function addTaskElement(data) {
const newTask = document.createElement("li");
const delBtn = document.createElement("button");
const checkBtn = document.createElement("button");
delBtn.textContent = "Reset"
checkBtn.textContent = "Done"
newTask.textContent = data.name;
newTask.appendChild(checkBtn);
newTask.appendChild(delBtn);
list.appendChild(newTask);
}

addEventListener function firing an increasing amount of times each click

I am trying to make a text based game based on a tutorial on Youtube. I have a div called "HistoryPanel" where I want divs to be appended containing a string from an object property "historyText" when "buyItemPanelbtn" is clicked. While I have managed to achieve this when I click the button again it will append the div twice and three times if I click again after that and so on. Can someone show me how to make it only append once each time the button is clicked?
I have included the code and the object that the code is relevant to. I haven't included the other objects because there are a lot and they are not relevant to this problem.
const textElement = document.getElementById('text');
const titleElement = document.getElementById('title');
const buyItemPanelBackground = document.getElementById('buyItemPanelBackground');
const buyItemPanel = document.getElementById('buyItemPanel');
const buyItemPanelName = document.getElementById('buyPanelName');
const buyItemPanelPrice = document.getElementById('buyPanelPrice');
const buyPanelType = document.getElementById('buyPanelType');
const buyItemPanelbtn = document.getElementById('buyItemPanelbtn');
const buyItemNevermind = document.getElementById('buyItemNevermind');
const backButton = document.getElementById('backButton');
const closeMenuButton = document.getElementById('closeMenuButton');
const optionButtonsElement = document.getElementById('option-buttons');
//home screen
const HistoryPanel = document.getElementById('HistoryPanel');
// home screen buttons
const homeScreen = document.getElementById('homeScreen');
const menuScreen = document.getElementById('menuScreen');
const homeLocationsButton = document.getElementById('homeLocationsButton');
let state = {}
function startGame() {
state = {}
showbrookton("Bakery");
}
//close locations menu
function closeLocationsMenu(){
menuScreen.style.display = "none";
homeScreen.style.display = "block";
}
//close item buy window
function closeItemBuyWindow(){
buyItemPanelBackground.style.display = "none";
}
//hide buy item window when click nevermind
buyItemNevermind.addEventListener("click", closeItemBuyWindow);
// click buy button
buyItemPanelbtn.addEventListener("click", () => {
closeItemBuyWindow();
closeLocationsMenu();
});
//display locations menu
homeLocationsButton.addEventListener("click", () => {
menuScreen.style.display = "block";
homeScreen.style.display = "none";
startGame();
});
//close locations menu
closeMenuButton.addEventListener("click", closeLocationsMenu);
// when clicking an item
function showBuyItemPanel(){
buyItemPanelBackground.style.display = "block";
}
function showbrookton(brooktonIndex){
const brookton = Brookton.find(brookton => brookton.id === brooktonIndex)
textElement.innerText = brookton.text //fill text element
titleElement.innerText = brookton.title //title element
while (optionButtonsElement.firstChild) { //removes all buttons
optionButtonsElement.removeChild(optionButtonsElement.firstChild)
}
brookton.options.forEach(option => { //option is options and the funtion is performed on each option
if(showOption(option) && (option.text)) { //
const button = document.createElement('button')
button.innerText = option.text
button.classList.add('btn')
button.addEventListener('click', () => selectOption(option))
optionButtonsElement.appendChild(button)
}
if(option.backText){ //this goes back to previous page
const backOption = option.backText;
backButton.addEventListener('click', () => showbrookton(backOption))
} //else close window
})
}
function showOption(option){
return option.requiredState == null || option.requiredState(state)
}
function selectOption(option){
const nextbrooktonId = option.nextText
const history = option.historyText
if (nextbrooktonId == "buyItem"){
const buyItemName = option.text;
buyItemPanelName.innerText = buyItemName
buyItemPanelPrice.innerText = "Price: " + option.price
buyPanelType.innerText = option.type
const history = option.historyText;
buyItemPanelbtn.addEventListener('click', () => {
$(HistoryPanel).append("<div>" + history + "</div>")
})
return showBuyItemPanel()
}
state = Object.assign(state, option.setState) //this overrides the current state with the new ones
showbrookton(nextbrooktonId)
}
const Brookton = [
{
id: "Bakery",
title: "Bakery",
text: 'Choose a Location to visit.',
options: [
{
text: 'Bread',
type: 'Food',
price: '$4',
historyText: "You bought bread",
nextText: "buyItem",
}
]
}
]

How to render list according to localStorage input.value?

I wrote a function that trigger on input and get data from API. I need to store last input.value in localStorage and then set input.value to one in localStorage on reload. Everything is fine except when I reload the page I have value that I need but I have to click space then backspace f.e. to render things. I would like to ask where should I pass the getter from localStorage so it will trigger the function on reload, but won't break the listener for input. There is full code underneath :
let country;
let cities = [];
const citiesDiv = document.getElementById("cities");
const countryPicker = document.getElementById("country-picker");
const getData = async () => {
const response = await fetch(
`https://api.openaq.org/v1/cities?country=${country}&limit=10&parameter=no2&order_by=parameter`
);
const data = await response.json();
cities = data.results;
console.log(cities);
citiesDiv.innerHTML = "";
renderCities(cities);
};
countryPicker.value = JSON.parse(localStorage.getItem("inputValue"));
countryPicker.addEventListener("input", function(e) {
if (e.target.value.toLowerCase() === "poland".toLowerCase()) {
country = "PL";
} else if (e.target.value.toLowerCase() === "spain".toLowerCase()) {
country = "ES";
} else if (e.target.value.toLowerCase() === "germany".toLowerCase()) {
country = "DE";
} else if (e.target.value.toLowerCase() === "france".toLowerCase()) {
country = "FR";
}
localStorage.setItem("inputValue", JSON.stringify(e.target.value));
getData();
});
function renderCities(cities) {
cities.forEach(function(city) {
const p = document.createElement("p");
const button = document.createElement("button");
button.classList.add("accordion");
const div = document.createElement("div");
div.classList.add("panel");
const citiesDiv = document.getElementById("cities");
button.textContent = city.city;
citiesDiv.appendChild(button);
citiesDiv.appendChild(div);
div.appendChild(p);
p.textContent = "Lorem ipsum";
});
const acc = document.getElementsByClassName("accordion");
let i;
for (i = 0; i < acc.length; i++) {
console.log(i);
acc[i].addEventListener("click", function() {
this.classList.toggle("active");
var panel = this.nextElementSibling;
if (panel.style.maxHeight) {
panel.style.maxHeight = null;
} else {
panel.style.maxHeight = panel.scrollHeight + "px";
}
});
}
}
Refactor your code in order to make the function that fetches and renders data to be reusable for two cases:
when user changes the input.
when the page is loaded.
Replace the following part
countryPicker.value = JSON.parse(localStorage.getItem("inputValue"));
countryPicker.addEventListener("input", function(e) {
if (e.target.value.toLowerCase() === "poland".toLowerCase()) {
country = "PL";
} else if (e.target.value.toLowerCase() === "spain".toLowerCase()) {
country = "ES";
} else if (e.target.value.toLowerCase() === "germany".toLowerCase()) {
country = "DE";
} else if (e.target.value.toLowerCase() === "france".toLowerCase()) {
country = "FR";
}
localStorage.setItem("inputValue", JSON.stringify(e.target.value));
getData();
});
with
countryPicker.value = JSON.parse(localStorage.getItem("inputValue"));
fetchAndRender(countryPicker.value);
countryPicker.addEventListener("input", e => fetchAndRender(e.target.value));
function fetchAndRender(value) {
if (value.toLowerCase() === "poland".toLowerCase()) {
country = "PL";
} else if (value.toLowerCase() === "spain".toLowerCase()) {
country = "ES";
} else if (value.toLowerCase() === "germany".toLowerCase()) {
country = "DE";
} else if (value.toLowerCase() === "france".toLowerCase()) {
country = "FR";
}
localStorage.setItem("inputValue", JSON.stringify(value));
getData();
}

How do I get items from localStorage and display in my UI?

This is what I have tried. It works with just a small issue. Anytime I add a new item to the localStorage it multiplies the items when displaying it until I refresh the page
const displayStorage = () => {
let values = [],
keys = Object.keys(localStorage),
i = keys.length;
while (i--) {
if (keys[i] === 'theme') continue;
values.push(JSON.parse(localStorage.getItem(keys[i])));
}
values.reverse();
return values.forEach(obj => showNotes(obj));
};
e.g let's say I have 123 stored and I want to add 4. It returns 1231234 instead of just 1234
This is the function that handles the UI display
const showNotes = ({ id, post, date }) => {
const noteSection = document.createElement('div');
noteSection.classList.add('notes-container');
const notes = document.createElement('article');
notes.classList.add('single-note');
notes.textContent = post.substring(0, 100);
const viewMore = document.createElement('a');
viewMore.classList.add('view-more');
viewMore.textContent = '...';
viewMore.setAttribute('title', 'View more');
viewMore.addEventListener('click', e => {
e.preventDefault();
if (notes.textContent.length <= 110) {
notes.textContent = post;
notes.appendChild(viewMore);
viewMore.setAttribute('title', 'View less');
} else {
notes.textContent = post.substring(0, 100);
notes.appendChild(viewMore);
viewMore.setAttribute('title', 'View more');
}
});
const noteActions = document.createElement('span');
noteActions.classList.add('note-actions');
const deleteLink = document.createElement('a');
deleteLink.textContent = 'Delete';
deleteLink.setAttribute('data-id', `${id}`);
deleteLink.addEventListener('click', deleteNote);
const notesDate = document.createElement('aside');
notesDate.classList.add('note-date');
notesDate.textContent = date;
noteActions.appendChild(deleteLink);
notes.appendChild(viewMore);
notes.appendChild(noteActions);
noteSection.appendChild(notesDate);
noteSection.appendChild(notes);
document.querySelector('.notes').appendChild(noteSection);
};
This is the function to save
notesForm.addEventListener('submit', e => {
e.preventDefault();
const save = (sid, spost, sdate) => {
const obj = { id: sid, post: spost, date: sdate };
localStorage.setItem(`${sid}`, JSON.stringify(obj));
};
save(generateId(), post.value, dateFormat());
displayStorage();
});
its a simple solution but may be useful for someone,
just clear items from UI, and again display them from localstorage, this will show the old and new items from localstorage.

Categories

Resources