JQuery cloned element - javascript

I am stuck on this problem. I am coding a task platform app. Whenever I try to save, the task clones itself. After each "Save Changes," there are more and more clones. I have rewritten the code so many times. But still, I am not successful. Please help me to find the error.
$("#taskSave").click(() => {
const task = {
id: Date.now(),
imageUrl: $("#imageInput").val(),
title: $("#titleInput").val(),
description: $("#descriptionInput").val(),
type: $("#typeInput").val(),
};
$("#overlay").hide();
todos.push(task);
saveStorage(todos);
// reset input values
$("#imageInput").val("");
$("#titleInput").val("");
$("#descriptionInput").val("");
$("#typeInput").val("");
});
function saveStorage(todos) {
localStorage.setItem("todos", JSON.stringify(todos));
display(todos);
};
function display(todos) {
$("#taskBoard").innerHTML = "";
// .html("");
todos.forEach(item => {
let c = document.createElement("div");
c.setAttribute("class", "card");
c.setAttribute('id', item.id);
c.innerHTML = `
<div class="cardTop">
<div class="binContainer">
<div class="binImage"></div>
</div>
</div>
<img src="${item.imageUrl}" alt="task image">
<h2>${item.title}<h2>
<p>${item.description}</p>
<div class="cardType">${item.type}</div>
`;
$("#taskBoard").append(c);
// end
});
};

I've created a minimal working example, and the problem is in the cleanup of the HTML. You cannot use innerHTML on the JQuery object, or you use its html function or you need to retrieve the javascript object with $("#taskBoard")[0].
// You can use:
$("#taskBoard").html("");
// or
// document.getElementById("taskBoard").innerHTML = "";
// or
// $("#taskBoard")[0].innerHTML = "";
// But not:
// $("#taskBoard").innerHTML = "";
The working example here on JSFiddle (on SO dont work localStorage)
let todos = [];
$("#taskSave").click(() => {
const task = {
id: Date.now()
};
todos.push(task);
saveStorage(todos);
});
function saveStorage(todos) {
localStorage.setItem("todos", JSON.stringify(todos));
display(todos);
console.log(todos);
};
function display(todos) {
$("#taskBoard").html("");
// or
// document.getElementById("taskBoard").innerHTML = "";
// or
// $("#taskBoard")[0].innerHTML = "";
// But not
// $("#taskBoard").innerHTML = "";
todos.forEach(item => {
let c = document.createElement("div");
c.innerHTML = `
<p>${item.id}</p>
`;
$("#taskBoard").append(c);
});
};
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<button id="taskSave">
SAVE
</button>
<div id="taskBoard">
</div>

Related

how to render appendChild Js without duplicate

I am a new learning JS. Who can help me complete this code. I have 2 problem:
render child Node user Chat when click without duplicate
how to remove child Node user when close chat window
full code is here: Jsfiddle
// event handling when click
handleEvents: function () {
let _this = this;
userChatList.onclick = function (e) {
const userNode = e.target.closest(".user-chat__item");
if (userNode) {
userIndex = Number(userNode.getAttribute("user-num"));
_this.renderUserChat(userIndex);
const getChatWithItems = document.querySelectorAll(".chat-with__item");
getChatWithItems.forEach(item => {
item.onclick = function(e){
const itemNode = e.target.closest(".chat-with__top i");
if(itemNode){
chatWithList.removeChild(chatWithItem);
}
}
})
}
}
},
//render user chat with someone
renderUserChat: function (num) {
// console.log(userIndex);
chatWithItem = document.createElement("li");
chatWithItem.classList.add("chat-with__item");
chatWithItem.setAttribute('user-num', num);
chatWithItem.innerHTML = `
<div class="chat-with__top">
<div class="chat-with__img">
<img src="${this.users[num].img}" alt="${this.users[num].name}">
<span class="user__status ${this.users[num].status}"></span>
</div>
<p class="chat-with__name">${this.users[num].name}</p>
<i class="fa-solid fa-xmark"></i>
</div>
<div class="chat-with__body">
<ul class="chat__text">
<li class="chat-text__user">Hey. 👋</li>
<li class="chat-text__user user__chatting">I am here</li>
<li class="chat-text__user user__chatting">What's going on?</li>
<li class="chat-text__user">Have you finished the "project 2" yet?</li>
<li class="chat-text__user user__chatting">I have been fixed bugs</li>
<li class="chat-text__user">OK.</li>
</ul>
</div>
<div class="chat-width__footer">
<i class="fa-solid fa-image"></i>
<i class="fa-solid fa-folder"></i>
<div class="chat-width__input">
<input type="text" id="send-sms" name="send SMS" placeholder="...">
</div>
<i class="fa-solid fa-paper-plane-top"></i>
</div>
`
chatWithList.appendChild(chatWithItem);
},
<ul class="chat-with__list">
</ul>
I have not still known how to solve it, up to now
Just keep track which chat windows are opened in an object.
To give you basic idea of the concept:
// storage for opened chat windows
// this variable must be accessible by event handlers
const openedChats = {};
In chat opened event handler:
if (openedChats[userId]) //check if chat already opened
return;
const chatWithItem = document.createElement("li");
...
openedChats[userId] = chatWithItem; //store window
chatWithList.appendChild(chatWithItem); //show window
In chat close event handler:
const chatWithItem = openedChats[userId]; // get opened chat
if (chatWithItem)
{
chatWithItem.parentNode.removeChild(chatWithItem); // destroy window
delete openedChats[userId]; // remove window
}
If you need to get list of all userIds that have opened chat windows, use:
const openedChatsIds = Object.keys(openedChats);
Finnaly I find the way to code. This is my way
handleEvents: function () {
let _this = this;
let currentChat = [];
userChatList.onclick = function (e) {
const userNode = e.target.closest(".user-chat__item");
if (userNode) {
userIndex = Number(userNode.getAttribute("user-num"));
// get value 'userIndex' for currentChat array
function getCurrentChat(arr, index) {
arr.push(index);
}
// check value userIndex in a currentChat array
function checkCurrentChat(arr, index) {
if (arr.indexOf(index) < 0) {
getCurrentChat(currentChat, userIndex);
return true;
} else {
return false;
}
}
let isExisted = checkCurrentChat(currentChat, userIndex);
// console.log(isExisted);
if (isExisted) {
_this.renderUserChat(userIndex);
}
const getChatWithItems = chatWithList.querySelectorAll(".chat-with__item");
getChatWithItems.forEach( function(item) {
item.onclick = function (e) {
const closeChat = e.target.closest(".chat-with__top i");
if(closeChat){
const getNum = Number(closeChat.parentElement.getAttribute("user-num"));
chatWithList.removeChild(item);
const findNum = currentChat.indexOf(getNum);
currentChat.splice(findNum, 1);
}
}
})
}
}
}
inside, i add an attribute to get number (userIndex):
<div class="chat-with__top" user-num ="${num}">
if you use second .parentElement, it will ok.
closeChat.parentElement.parentElement.getAttribute("user-num")

javascript InnerHTML adding cards only one time

I'm trying to add a bootstrap card inside a div called [itemscontainer] using javascript
by document.getElementById("itemscontainer").innerHTML so i want the cards to be inserted inside the itemscontainer only one time like this :-
but the problem is the items cards keeps reapet them salves more than one time like:-
what i want is to clear the itemscontainer first before adding the cards and this is what i have tried so that the items will be only one cards for each item
// clear function
function clear(){
document.getElementById("ssst").innerHTML = ""
}
// listener append all items to the inventory
window.addEventListener('message', (event) => {
let data = event.data
if(data.action == 'insertItem') {
let name = data.items.name
let count = data.items.count
let icon = data.items.icon
if(document.getElementById("ssst").innerHTML == ""){
clear()
}else{
document.getElementById("ssst").innerHTML +=
"<div class='card holder'>"+
'<div class="card-body">'+
'<img src="icons\\'+icon+'" style="position:absolute;left:15%;width:40px; height:36px;" alt="">'+
'<h4 id="counter">'+count+'</h4>'+
'</div>'+
'<span class="itemname">'+name+'</span>'+
'</div>";'
}
}
})
The real solution is to figure out why you are getting the items more than once. With the information you provided that is impossible for me to answer. So the only thing we can recommend is how to prevent items from being added more than once.
If your messaging system returns duplicates you can determine if you have seen it. If you do, replace it. Otherwise add it.
window.addEventListener('message', (event) => {
const data = event.data;
console.log(data)
if (data.action == 'insertItem') {
let name = data.items.name
let count = data.items.count
let icon = data.items.icon
const html = `
<div class='card holder' data-name="${name}">
<div class="card-body">
<img src="icons/${icon}" style="position:absolute;left:15%;width:40px; height:36px;" alt="${icon}">
<h4 id="counter">${count}</h4>
</div>
<span class="itemname">${name}</span>
</div>`;
const elemExists = document.querySelector(`[data-name="${name}"]`);
if (elemExists) {
const parser = new DOMParser();
const doc = parser.parseFromString(html, 'text/html');
elemExists.replaceWith(doc.body);
} else {
document.getElementById("ssst").insertAdjacentHTML("beforeend", html);
}
}
});
window.postMessage({
action: 'insertItem',
items: {
name: 'foo',
count: 1,
icon: 'foo'
}
});
window.postMessage({
action: 'insertItem',
items: {
name: 'bar',
count: 40,
icon: 'barrrrrr'
}
});
window.postMessage({
action: 'insertItem',
items: {
name: 'foo',
count: 1000,
icon: 'foo'
}
});
<div id="ssst"></div>
Why are you using the if statement, what are you checking for?
remove the if statement, I can't see the reason for it to be used here.
clear()
and the rest of your code.

Why when i am searching for something else is deleting the previous contents

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.

queryselector undefined even after checking it with if conditional

I'm learning the basics with Javascript and I'm trying to do a modal that replace an alert, I'm almost done but I have a problem with the querySelector on the button to close it. It returns undefined even if I check it with the if conditional.
function getTemplate(templateName) {
let template = document.querySelector(templateName);
return template.innerHTML;
}
function createFragment(htmlStr) {
let frag = document.createDocumentFragment();
let temp = document.createElement('div');
temp.innerHTML = htmlStr;
while (temp.firstChild) {
frag.appendChild(temp.firstChild);
}
return frag;
}
function putTemplate(template) {
document.body.appendChild(createFragment(template));
}
function openAlert(alertName, btnOpen) {
let openBtn = document.querySelector(btnOpen);
openBtn.addEventListener('click', function () {
putTemplate(getTemplate(alertName));
});
}
function closeAlert(alertName, btnClose) {
let closeBtn = document.querySelector(btnClose);
if (closeBtn) {
closeBtn.addEventListener('click', function () {
let alertWrapper = document.querySelector(alertName);
alertWrapper.parentNode.removeChild(alertWrapper);
});
}
}
function Alert(alertName, btnOpen, btnClose) {
openAlert(alertName, btnOpen);
closeAlert(alertName, btnClose);
}
Alert('#alertTemplate', '.activeBtn', '.deactive');
And this is the markup:
<template id="alertTemplate">
<div id="alertWrapper">
<h1></h1>
<div class="alertBox confirmAlert" role="alert">
<p></p>
<button class="closeBtn deactive troll"></button>
<button class="acceptBtn deactive"></button>
</div>
</div>
</template>
``

Is there a way to simplify the code using an array?

Is there a way to simplify the below code by using an array? For example, when button 1 (with the index of 0) in the HTML is clicked, could that be used to get a value at index 0 in another array?
function f1() {
document.getElementById("dis").innerHTML = "JoeMae";
}
function f2() {
document.getElementById("dis").innerHTML = "TanakaMae";
}
function f3() {
document.getElementById("dis").innerHTML = "James";
}
function f4() {
document.getElementById("dis").innerHTML = "Deus";
}
<button onclick="f1()">no.1</button>
<button onclick="f2()">no.2</button>
<button onclick="f3()">no.3</button>
<button onclick="f4()">no.4</button>
<p id="dis"></p>
You can simplify without using array:
<button onclick="f('JoeMae')">no.1</button>
<button onclick="f('TanakaMae')">no.2</button>
<button onclick="f('James')">no.3</button>
<button onclick="f('Deus')">no.4</button>
<p id="dis"></p>
function f(str) {
document.getElementById("dis").innerHTML = str;
}
Use another array such that the nth index of that array corresponds to the nth button:
const texts = [
"JoeMae",
"TanakaMae",
"James",
"Deus"
];
const dis = document.getElementById("dis");
document.querySelectorAll('button').forEach((button, i) => {
button.addEventListener('click', () => {
dis.textContent = texts[i];
});
});
<button>no.1</button>
<button>no.2</button>
<button>no.3</button>
<button>no.4</button>
<p id="dis"></p>
Note that unless you're deliberately inserting HTML markup, you should probably use textContent, not innerHTML. (textContent is faster and safer)
Here's an approach that's vanilla JS. I used the dataset API to connect each button to its data, then a single handler to retrieve and display this data.
"use strict";
function byId(id){return document.getElementById(id)}
function newEl(tag){return document.createElement(tag)}
window.addEventListener('load', onLoaded, false);
function onLoaded(evt)
{
var responseArray = ['JoeMae', 'TanakaMae', 'James', 'Deus'];
responseArray.forEach( function(arrElem, elemIndex, arr)
{
var btn = newEl('button');
btn.textContent = `no.${elemIndex+1}`;
btn.dataset.response = arrElem;
btn.addEventListener('click', onClick, false);
document.body.appendChild(btn);
}
);
function onClick(evt)
{
let text = this.dataset.response;
byId('dis').textContent = text;
}
}
<p id='dis'></p>
Here's a slightly cleaner and more flexible example how to implement this type of functionality.
If you are having a lot of rendering functionality like this, I would recommend you to use a library/framework for it, though.
const buttonDefinitions = [
{title: 'no.1', name: 'Monica'},
{title: 'no.2', name: 'Erica'},
{title: 'no.3', name: 'Rita'},
{title: 'no.4', name: 'Tina'}
];
const buttonContainer = document.getElementById('buttonContainer');
const resultContainer = document.getElementById('resultContainer');
for (const buttonDefinition of buttonDefinitions) {
const button = document.createElement('button');
button.innerHTML = buttonDefinition.title;
button.onclick = () => {
resultContainer.innerHTML = buttonDefinition.name;
};
buttonContainer.appendChild(button);
}
<div id="buttonContainer"></div>
<div id="resultContainer"></div>
You can pass the element to the function and access the element data-attributes
In the below example I am passing data-name
function f(element) {
document.getElementById("dis").innerHTML = element.dataset["name"];
}
<button data-name="JoeMae" onclick="f(this)">no.1</button>
<button data-name="TanakaMae" onclick="f(this)">no.2</button>
<button data-name="James" onclick="f(this)">no.3</button>
<button data-name="Deus" onclick="f(this)">no.4</button>
<p id="dis"></p>

Categories