how to render appendChild Js without duplicate - javascript

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")

Related

JQuery cloned element

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>

Already known weather for city should not repeat again

I'm trying my first weather api APP. Here I'm trying to achive that if the city weather is already displayed , It should give the message "You already know the weather" . and should not repeat the weather
Here is my code. Anyone Please look at my code ...
What is the mistake I have been made.
<div class="main">
<div class="container">
<div class="search_por">
<h2>Weather </h2>
<div class="validate_msg color_white"></div>
<form>
<label for=""></label>
<input type="search" class="input_text" value="">
<button type="submit" id="sub_button" class="srh_button">Search</button>
</form>
<!-- <canvas id="icon1" width="150" height="75"></canvas> -->
<div class="dat_weather">
<ul id="list_it">
</ul>
</div>
</div>
</div>
</div>
var get_text=document.querySelector("form");
get_text.addEventListener("submit",e=>{
e.preventDefault();
var input_val=document.querySelector('input').value;
const apiKey="bc4c7e7826d2178054ee88fe00737da0";
const url=`https://api.openweathermap.org/data/2.5/weather?q=${input_val}&appid=${apiKey}&units=metric`;
fetch(url,{method:'GET'})
.then(response=>response.json())
.then(data=>{console.log(data)
const{main,sys,weather,wind}=data;
//icons-end
var error_ms=document.getElementsByClassName("validate_msg")[0];
var iconcode = weather[0].icon;
console.log(iconcode);
var li=document.createElement("Li");
var weatherinfo=`<div class="nameci font_40" data-name="${data.name},${sys.country}"><span>${data.name}</span><sup>${sys.country}</sup></div>
<div class="temp_ic">
<img class="weat_icon" src="http://openweathermap.org/img/w/${iconcode}.png">
<div class="deg">${Math.floor( main.temp )}<sup>o</sup></div>
</div>
<div class="clear">
<div>${weather[0].description}</div>
</div>
`;
li.innerHTML=weatherinfo;
var ulid=document.getElementById("list_it");
ulid.appendChild(li);
var city_name=data.name;
console.log(skycons);
var listitems=document.querySelectorAll('#list_it');
const listArray=Array.from(listitems);
if(listArray.length>0)
{
var filtered_array=listArray.filter(el=>{
let content="";
if(input_val.includes(','))
{
if(input_val.split(',')[1].length>2)
{
alert("hving 2 commos");
inputval=input_val.split(',')[0];
content=el.querySelector(".nameci span").textContent.toLowerCase();
//content=el.querySelector(".nameci").innerHTML.toLowerCase();
//content=inputval.toLowerCase();
}
else
{
content=el.querySelector(".nameci").dataset.name.toLowerCase();
}
alert(filtered_array);
}
else
{
content=el.querySelector(".nameci span").textContent.toLowerCase();
}
console.log(inputval.toLowerCase());
return inputval.toLowerCase();
});
if(filtered_array.length>0)
{
console.log(filtered_array.length);
error_ms.innerHTML="You Already know the weather of this country....";
get_text.reset();
return;
}
}
})
.catch((error)=>{
error_ms.innerHTML="Please Enter a valid city Name";
});
var error_ms=document.getElementsByClassName("validate_msg")[0];
error_ms.innerHTML="";
//var get_text=document.querySelector("form");
get_text.reset();
});
My full code is here:
https://codepen.io/pavisaran/pen/wvJaqBg
Let's try keeping track of a list of displayed locations outside of the callback:
var get_text = document.querySelector("form");
// Keep Track Of Displayed Cities Here Instead
let displayed = [];
get_text.addEventListener("submit", e => {
e.preventDefault();
var input_val = document.querySelector('input').value;
const apiKey = "bc4c7e7826d2178054ee88fe00737da0";
const url = `https://api.openweathermap.org/data/2.5/weather?q=${input_val}&appid=${apiKey}&units=metric`;
fetch(url, {method: 'GET'})
.then(response => response.json())
.then(data => {
var error_ms = document.getElementsByClassName("validate_msg")[0];
const {main, sys, weather, wind, name} = data;
if (displayed.length > 0) {
// Filter Displayed Based on Current vs name from data (response)
const filtered_array = displayed.filter(el => el === name);
if (filtered_array.length > 0) {
error_ms.innerHTML = "You Already know the weather of this country....";
get_text.reset();
return Promise.resolve();
}
}
// Add City To Array of Displayed Cities
displayed.push(name);
// Do Rest of Code to Add New City
var iconcode = weather[0].icon;
var li = document.createElement("Li");
var weatherinfo = `<div class="nameci font_40" data-name="${data.name},${sys.country}"><span>${data.name}</span><sup>${sys.country}</sup></div>
<div class="temp_ic">
<img class="weat_icon" src="http://openweathermap.org/img/w/${iconcode}.png">
<div class="deg">${Math.floor(main.temp)}<sup>o</sup></div>
</div>
<div class="clear">
<div>${weather[0].description}</div>
</div>
`;
li.innerHTML = weatherinfo;
var ulid = document.getElementById("list_it");
ulid.appendChild(li);
})
.catch((error) => {
error_ms.innerHTML = "Please Enter a valid city Name";
});
var error_ms = document.getElementsByClassName("validate_msg")[0];
error_ms.innerHTML = "";
get_text.reset();
});
You have to just check for the value which is coming from api whether it's present on your list or not. you can try this.
li.innerHTML=weatherinfo;
var ulid=document.getElementById("list_it");
var isPresent = false;
var items = ulid.getElementsByTagName("li");
for (var i = 0; i < items.length; i++){
if(items[i].innerHTML == li.innerHTML){
alert("you already know the weather")
isPresent = true;
}
}
if(!isPresent){
ulid.appendChild(li);
}

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 :)

How to remove child element from div using jquery or normal JS

I have a list of user cards. That card contains add and remove button.
I want to remove that card from list of card when I click at remove button.
Code is similar to following:
// function to generate card
function generateUserCard(id) {
return `
<div class="user-card" id="${id}">
<button data-id="${id}" class="add" >Add</button>
<button data-id="${id}" class="remove" >Remove</button>
</div>
`;
}
// function to generate list of user
function generateUsers(users) {
const userGrid = $("#user-grid");
for(let user of users) {
const userCard = generateUserCard(user.id);
userGrid.append(userCard);
// adding event listeners
$(`[data-id=${user.id}]`).on("click", function() {
// I did something like this
(`#${user.id}`).remove(); // But this didn't work
})
}
}
Please help!
There are several issues in the logic used in your click event callback:
The variable id is not accessible in the callback. A quick fix will be to fix the reference so that you are using user.id in the selector instead. Also, you can simply remove it by ID without needing to search for it inside its parent element, since it is unique.
Your selector [data-id]=${user.id} is syntacically incorrect. I suppose you meant [data-id=${user.id}]
You should be using .remove() to remove a node
A quick fix will look like this:
$(`button[data-id=${user.id}].remove`).on("click", function() {
$(`#${user.id}`).remove();
});
See proof-of-concept below:
function generateUserCard(id) {
return `
<div class="user-card" id="${id}">
User ID: ${id}
<button data-id="${id}" class="add" >Add</button>
<button data-id="${id}" class="remove" >Remove</button>
</div>
`;
}
function generateUsers(users) {
const userGrid = $("#user-grid");
for (let user of users) {
const userCard = generateUserCard(user.id);
userGrid.append(userCard);
$(`button[data-id=${user.id}].remove`).on("click", function() {
$(`#${user.id}`).remove();
})
}
}
// For demo only
let i = 0;
$('#btn').on('click', function() {
const userArray = [];
for (let j = 0; j < 3; j++) {
i++;
userArray.push({ id: i });
}
generateUsers(userArray);
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<button id="btn">Generate users</button>
<div id="user-grid"></div>
However, an improvement to your code will be to avoid adding new click event listeners to all your newly appended elements. You can simply listen to the click event bubbling up to a parent that is already present at runtime (e.g. #user-grid), and you can bind it outside of your generateUsers function:
$('#user-grid').on('click', 'button.add, button.remove', function() {
const id = $(this).attr('data-id');
$(`#${id}`).remove();
});
See proof-of-concept below:
function generateUserCard(id) {
return `
<div class="user-card" id="${id}">
User ID: ${id}
<button data-id="${id}" class="add" >Add</button>
<button data-id="${id}" class="remove" >Remove</button>
</div>
`;
}
function generateUsers(users) {
const userGrid = $("#user-grid");
for (let user of users) {
const userCard = generateUserCard(user.id);
userGrid.append(userCard);
}
}
// Listen to event bubbling instead!
$('#user-grid').on('click', 'button.remove', function() {
const id = $(this).attr('data-id');
$(`#${id}`).remove();
});
// For demo only
let i = 0;
$('#btn').on('click', function() {
const userArray = [];
for (let j = 0; j < 3; j++) {
i++;
userArray.push({
id: i
});
}
generateUsers(userArray);
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<button id="btn">Generate users</button>
<div id="user-grid"></div>

The click on the div element with role='button' isn't operate

The click on the div element with role='button' isn't operate.I need to click on the icon, but I can't do it.
html:
<div class="list">
<div class="item">
<div role="button" tabindex="-1">
<strong>ItemName2</strong>
</div>
<div class="d">
<div class="item-icon" role="button" tabindex="-1" style="display: none">
<i aria-label="icon: edit" class="edit"></i>
</div>
</div>
</div>
<div class="item"> ... </div>
<div class="item"> ... </div>
<div class="item"> ... </div>
</div>
js:
try {
await driver.get("http://127.0.0.1");
let findButtons = await driver.findElements(By.tagName('strong'));
let buttons = findButtons.map(elem => elem.getText());
const allButtons = await Promise.all(buttons);
console.log(allButtons); // It is displayed all button values, such as ItemName1
let tButton;
for (let i = 0; i < findButtons.length; i++) {
if (allButtons[i] == 'ItemName2') {
tButton = await findButtons[i];
tButton.click(); // I try to click on this button, where value = ItemName2
console.log(allButtons[i]); // It is displayed button value 'ItemName2'
}}}
Console error:
(node:12254) UnhandledPromiseRejectionWarning: StaleElementReferenceError: stale element reference: element is not attached to the page document
You are getting stale element exception because you are trying to get the element with old references. Each time you click on the element in your loop, the elements reference will be updated and allButtons[i] does not work. In order to handle this you have to get the latest refers of buttons. Try the below.
js:
const { By, Key, until } = require("selenium-webdriver");
const webdriver = require("selenium-webdriver");
require("chromedriver");
async () => {
let driver = await new webdriver.Builder().forBrowser("chrome").build();
try {
await driver.get("http://10.203.201.77:8000/login");
let findButtons = await driver.findElements(By.tagName('strong'));
let buttons = findButtons.map(elem => elem.getText());
const allButtons = await Promise.all(buttons);
console.log(allButtons); // It is displayed all button values, such as ItemName1
let tButton;
for (let i = 0; i < findButtons.length; i++) {
buttons = findButtons.map(elem => elem.getText()); # getting the button so that the elements refererence will refresh
if (allButtons[i] == 'ItemName2') {
tButton = await findButtons[i];
tButton.click(); // I try to click on this button, where value = ItemName2
console.log(allButtons[i]); // It is displayed button value 'ItemName2'
}
}
console.log("DONE");
} catch (e) {
console.log(e);
} finally {
await driver.quit();
}
}
}
I found solution:
let findButtons = await driver.findElements(By.tagName('strong'));
let buttons = findButtons.map(async elem => await elem.getText()); // I add async & await
const allButtons = await Promise.all(buttons);
console.log(allButtons); // There are all itemName

Categories