I have this task where I am to build a BMI calculator on specific instructions. I seem to have followed all the instructions except one in letsCalculateBMI. The instruction says:
letsCalculateBMI and get it to obtain the selected value from the SELECT element, pass that value to a getSelectedUser function call, which should return the user object for the selected value. This user object should be assigned to a user variable.
My confusion stems from how to getSelectedUser function call inside letsCalculateBMI to return the user object and the user object assigned to a user.
For a quicker view in computeBMI arrow function, the user parameter is an immediately destruct to weight, height, and country properties.
Currently the error I have is Uncaught ReferenceError: weight is not defined at HTMLButtonElement.letsCalculateBMI
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport"
content="width=device-width, initial-scale=1.0" />
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<title>Mini App</title>
</head>
<body>
<div class="select">
<select class="select-text">
<option disabled selected>Select User</option>
</select>
</div>
<div class="details mdc-elevation--z3">
<p>
<span class="prop" data-age>Age :</span>
<span class="value" data-age-value>23 years</span>
</p>
<p>
<span class="prop" data-height>Height :</span>
<span class="value" data-height-value>169cm</span>
</p>
<p>
<span class="prop" data-weight>Weight :</span>
<span class="value" data-weight-value>68kg</span>
</p>
<p>
<span class="prop" data-gender>Gender :</span>
<span class="value" data-gender-value>Female</span>
</p>
<p>
<span class="prop" data-country>Country :</span>
<span class="value" data-country-value>Nigerian</span>
</p>
</div>
<button id="oracle" class="mdc-button" onclick="letsCalculateBMI()">
Calculate BMI
</button>
<div id="outcome">
<h5 class="mdc-typography--headline5">
BMI
</h5>
<p class ="bmi-text"></p>
</div>
<script>
const users = [];
const countriesWithLowerBmi = ["Chad", "Sierra Leone", "Mali", "Gambia", "Uganda", "Ghana", "Senegal", "Somalia", "Ivory Coast", "Isreal"];
const featToMeter = 0.3048;
const bmiCountryRatio = 0.82;
const computeBMI = ({weight, height, country}) => {
const heightInMeters = height * featToMeter;
let BMI = weight / (heightInMeters^2);
if (countriesWithLowerBmi.includes(country))
BMI *= bmiCountryRatio;
return Math.round(BMI, 2);
};
const getSelectedUser = (userId) => {
return users.find(({id}) => id === userId);
};
const displaySelectedUser = ({target}) => {
const user = getSelectedUser(target.value);
const properties = Object.keys(user);
properties.forEach(prop => {
const span = document.querySelector(`span[data-${prop}-value]`);
if(span) {
span.textContent= user[prop];
}
})
}
const letsCalculateBMI = () => {
const value = document.querySelector('.select-text').value;
getSelectedUser(value);
const user = {weight, height, country}
const bmi = computeBMI(user);
document.querySelector('.bmi-text').innerHTML = bmi
};
const powerupTheUI = () => {
const button = document.querySelector('#oracle');
const select = document.querySelector('.select-text');
select.addEventListener('change', displaySelectedUser);
button.addEventListener('click',letsCalculateBMI);
};
const displayUsers = (users) => {
users.forEach(user => {
const select = document.querySelector('.select-text');
const option = document.createElement('option');
option.text = user.name;
option.value = user.id;
select.appendChild(option);
});
};
const fetchAndDisplayUsers = () => {
users.push(
{
age: 40,
weight: 75,
height: 6,
country: 'Nigeria',
name: 'Charles Odili',
id: 'dfhb454768DghtF'
},
{
age: 23,
weight: 68,
height: 6,
country: 'Nigeria',
name: 'Simpcy',
id: 'gibb12erish'
}
);
displayUsers(users);
};
const startApp = () => {
powerupTheUI();
fetchAndDisplayUsers();
};
startApp();
</script>
</body>
</html>
The error tells you all you need to know: weight isn't defined because, well, you haven't defined it (same goes for height and country). These are properties of the user, so you need to get them from the user object returned by getSelectedUser.
For example:
user = getSelectedUser(value);
computeBMI(user.weight, user.height, user.country);
This should fix your problem, but....
For a quicker view in computeBMI arrow function, the user parameter is an immediately destruct to weight, height, and country properties.
In my opinion, this isn't good OOP design - you've already got an object with all that info; why the need to write more code to split it up?
What I'd rather do would be something like this:
...
computeBMI(getSelectedUser(value));
...
const computeBMI = (user) => {
const heightInMeters = user.height * featToMeter;
let BMI = user.weight / (heightInMeters^2);
if (countriesWithLowerBmi.includes(user.country))
BMI *= bmiCountryRatio;
return Math.round(BMI, 2);
};
letsCalculateBMI should be:
const letsCalculateBMI = () => {
const value = document.querySelector('.select-text').value;
const user = getSelectedUser(value);
const bmi = computeBMI(user);
document.getElementById("bmi-text").innerHTML = bmi;
};
Related
I'm creating a library app where users input data into a form and the values they enter are displayed in its own div.
I have this array
let myLibrary = [
{
title: "The Once and Future King",
author: "White",
pages: 654,
},
{
title: "The Hobbit",
author: "Tolkien",
pages: 304,
},
];
which is automatically displaying each object (for testing purposes) on my page thanks to this forEach()
myLibrary.forEach((book) => {
const bookContent = document.getElementById("content");
const addBook = document.createElement("div");
addBook.className = "book";
bookContent.appendChild(addBook);
addBook.innerHTML = `
<div class="title">
<p class="bookTitle">
<span>${book.title}</span>
</p>
</div>
<div class="body">
<p>
Author: <span>${book.author}</span>
</p>
<p>
Pages: <span>${book.pages}</span>
</p>
</div>`;
});
The forEach makes it so every object inside my array is displayed in its own 280x365 pixel div and the book title, author and page count is displayed in it's own p element. I'm also using flex for organization.
I also have a form which users can input a new book title, author and page number to add to their growing book collection.
const form = document.getElementById("form");
form.addEventListener("submit", updateLibrary);
function updateLibrary(event) {
event.preventDefault();
const title = document.getElementById("title").value;
const author = document.getElementById("author").value;
const pages = document.getElementById("pages").value;
const book = {
title: title,
author: author,
pages: parseInt(pages),
};
myLibrary.push(book);
console.log(myLibrary);
}
When I fill the form out, everything appears to work perfectly. I have console.log in my updateLibrary function and I notice the object is being pushed into the array like I want it to. But every time I hit the submit button, a new div isn't being created for the new book. I'm guessing this has to do with my forEach not triggering the new object but I can't find a way to fix this.
How can I better write my code so a new div is also being created with the new object every time I submit the form?
What I've tried
I've tried rearranging my code so the forEach is below the array, so the updateLibrary function is above and below the forEach.
I've also tried putting the forEach inside the updateLibrary function. That did make it work but it gave me an even worse bug.
That is normal. The DOM (so the HTML within the page) is not automatically updated everytime that you change your array. If you want to work like this, I suggest you to look at ReactJS.
But to solve your problem, you must do 3 easy things here : create a function that will handle the display of your object in HTML, update your forEach to only call this new function, then, when a user create a new object, apply it this function :
const bookContent = document.getElementById("content");
function displayBook(book) {
const addBook = document.createElement("div");
addBook.className = "book";
bookContent.appendChild(addBook);
addBook.innerHTML = `
<div class="title">
<p class="bookTitle">
<span>${book.title}</span>
</p>
</div>
<div class="body">
<p>
Author: <span>${book.author}</span>
</p>
<p>
Pages: <span>${book.pages}</span>
</p>
</div>`;
}
// Display your original object list
myLibrary.forEach((book) => {
displayBook(book)
});
// Handle your object creation
const form = document.getElementById("form");
form.addEventListener("submit", updateLibrary);
function updateLibrary(event) {
event.preventDefault();
const title = document.getElementById("title").value;
const author = document.getElementById("author").value;
const pages = document.getElementById("pages").value;
const book = {
title: title,
author: author,
pages: parseInt(pages),
};
myLibrary.push(book);
// Then, ask to display your book
displayBook(book)
console.log(myLibrary);
}
It should work fine :)
You can also use Proxy objects to update the DOM the moment you add a new book to the myLibrary array. For this, we will convert the myLibrary array into a new proxy object named libraryProxy and configure it.
const myLibrary = [{
title: "The Once and Future King",
author: "White",
pages: 654,
},
{
title: "The Hobbit",
author: "Tolkien",
pages: 304,
},
];
const libraryProxy = new Proxy(myLibrary, {
get(target, prop) {
return target[prop];
},
// When you add a new element to the array,
// the set method will run and here we will update the DOM
// while adding the new element as newVal to the array.
set(target, prop, newVal) {
if (Number(prop)) {
const book = newVal;
// add new book to array
target[prop] = book;
// update the dom
addBookToUI(book);
};
return true;
}
})
const bookContent = document.getElementById("content");
const form = document.getElementById("form");
form.addEventListener("submit", submitHandler);
libraryProxy.forEach((book) => {
addBookToUI(book);
})
function submitHandler(event) {
event.preventDefault();
const [title, author, pages] = document.querySelectorAll('form > input');
// New book will be added to the array and the DOM will be updated
libraryProxy.push({
title: title.value,
author: author.value,
pages: parseInt(pages.value, 10)
});
}
function addBookToUI(book) {
const bookEl = document.createElement('div');
bookEl.className = 'book';
bookContent.appendChild(bookEl);
bookEl.innerHTML += `
<div class="title">
<p class="bookTitle">
<span>${book.title}</span>
</p>
</div>
<div class="body">
<p>
Author: <span>${book.author}</span>
</p>
<p>
Pages: <span>${book.pages}</span>
</p>
</div>
`;
}
.book {
border: 1px solid #000;
margin: 5px 0;
font-size: 14px;
}
<form id="form">
<input type="text" id="title" placeholder="title" />
<input type="text" id="author" placeholder="author" />
<input type="text" id="pages" placeholder="pages" />
<button type="submit">save</button>
</form>
<div id="content"></div>
I'm having a bit of trouble with this problem. I'm working on the project of an e-commerce application that works on several html pages. I managed to push products through the cart html page, but I can't seem to find a way to update on this page only the quantity of a product and not push every elements of said product (images, id, etc). Onclick, if product exists, I only want quantity to be updated. Here's the code if any of you can help me out that'd be greatly appreciated.
setItems(kanap);
function setItems(kanap) {
let cart = JSON.parse(localStorage.getItem('cart'));
let imgKanap = kanap.imageUrl;
let idKanap = kanap._id;
let colorKanap = colors.value;
let quantityKanap = parseInt(quantity.value);
let key = idKanap + ' ' + colorKanap;
let cartItem = {
id: idKanap,
color: colorKanap,
quantity: quantityKanap,
kanap: kanap
};
if (cart === null) {
cart = [];
}
cart.push(cartItem);
localStorage.setItem('cart', JSON.stringify(cart));
function addProduct(cartItem) {
var found = false;
for (key in cartItem) {
if (cartItem[key].idKanap == idKanap) {
cartItem[key].quantityKanap += quantityKanap;
found = true;
break;
}
}
if (!found) {
cart.push(cartItem);
}
}
addProduct();
}
<div class="item__content__addButton">
<button id="addToCart" type="submit">Ajouter au panier</button>
</div>
<section class="cart">
<!-- <section id="cart__items">
<article class="cart__item" data-id="{product-ID}">
<div class="cart__item__img">
<img id ="image" alt="Photographie dun canapé">
</div>
<div class="cart__item__content">
<div class="cart__item__content__titlePrice">
<h2 class=title></h2>
<p class =price></p>
</div>
<div class="cart__item__content__settings">
<div class="cart__item__content__settings__quantity">
<p class= quantity>Qté : </p>
<input type="number" class="itemQuantity" name="itemQuantity" min="1" max="100" value="">
</div>
<div class="cart__item__content__settings__delete">
<p class="deleteItem">Supprimer</p>
</div>
</div>
</div>
</article> -->
</section>
There's a few approaches you can take, but I am using .find to look through your cart.
If the .find() function finds an item with the same id as you're about to add, it will up the quantity of the existing item instead of appending another object with the same ID.
I used a mock local storage since local storage doesn't work in these snippets so just ignore that and use what you've been doing for local storage access.
let mockLS = null;
// guessed at the structure here, you may have something slightly different
const exampleItem = {
_id: "abc",
imageUrl: "imageurlexample",
colors: {
value: "red"
},
quantity: {
value: 1
}
}
const exampleItem2 = {
_id: "abc2",
imageUrl: "imageurlexample2",
colors: {
value: "blue"
},
quantity: {
value: 1
}
}
function setItems(kanap) {
//let cart = JSON.parse(localStorage.getItem('cart'));
// using a mock localstorage here since it doesn't work within this snippet, use what you currently have instead
let cart = mockLS;
let imgKanap = kanap.imageUrl;
let idKanap = kanap._id;
let colorKanap = kanap.colors.value;
let quantityKanap = parseInt(kanap.quantity.value);
let key = idKanap + ' ' + colorKanap;
let cartItem = {
id: idKanap,
color: colorKanap,
quantity: quantityKanap
//kanap: kanap not sure why you want the whole obj here so I left this one out
};
if (cart === null) {
cart = [];
}
// here is the case where cart exists and there may be the same item in it
const itemExists = cart.find(item => {
if(item.id === idKanap) {
item.quantity += quantityKanap;
return true;
}
return false;
})
if (!itemExists) {
cart.push(cartItem);
}
//localStorage.setItem('cart', JSON.stringify(cart));
mockLS = cart;
}
setItems(exampleItem);
setItems(exampleItem2);
setItems(exampleItem);
console.log(mockLS)
I'm creating a to-do list project. where I'm trying to delete a todo when the user clicks on the delete button. I'm using the array's filter method to remove that clicked todo. But when I refresh, that deleted todo comes back. the reason is that It's not getting removed from the local storage. There's something wrong with the event listener at the very bottom of the javascript file. I'm trying to overrides the array with whatever filter method returns and saving it to the local storage but still it doesn't work.
Javascript file
import Todo from './todo.js';
import './style.css';
const TODO_LIST_KEY = 'TODO_LIST_KEY';
const template = document.querySelector('#list-item-template');
const todoListContainer = document.querySelector('#list');
const form = document.querySelector('.form');
const inputField = document.querySelector('#todo-input');
const loadList = () => {
const dataInStringFormat = localStorage.getItem(TODO_LIST_KEY);
return JSON.parse(dataInStringFormat) || [];
};
const renderTodo = (todo) => {
console.log("I'm inside of renderTodo Method");
const templateClone = template.content.cloneNode(true);
const taskContent = templateClone.querySelector('[data-list-item-text]');
taskContent.innerText = todo.description;
const checkBox = templateClone.querySelector('[data-list-item-checkbox]');
checkBox.checked = todo.completed;
checkBox.addEventListener('change', () => {
todo.completed = checkBox.checked;
saveList();
});
const listItem = templateClone.querySelector('.list-item');
listItem.dataset.todoIndex = todo.index;
todoListContainer.appendChild(templateClone);
};
let todoList = loadList();
todoList.forEach((todo) => renderTodo(todo));
const saveList = () => {
localStorage.setItem(TODO_LIST_KEY, JSON.stringify(todoList));
};
const clearField = () => {
inputField.value = '';
};
form.addEventListener('submit', () => {
if (inputField.value === '') return;
// Create a new Todo
const todoTemplate = new Todo(todoList.length, inputField.value, false);
todoList.push(todoTemplate); // new todo gets added to the list
renderTodo(todoTemplate); //Here it adds that new todo to the list
saveList();
clearField();
});
todoListContainer.addEventListener('click', (e) => {
if (!e.target.matches('[data-button-delete]')) return;
// Get the todo that is clicked on
const parent = e.target.closest('.list-item');
const todoIndex = parent.dataset.todoIndex;
// const todoItem = todoList.find((t) => t.index === todoIndex);
parent.remove(); // removes from the screen
todoList = todoList.filter((todo) => todo.index !== todoIndex);
saveList();
});
HTML File
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>To Do List</title>
</head>
<body>
<section class="todo-container">
<ul class="todo-list" id="list">
<li class="heading">
<h3>Today's To Do</h3>
<img
src="https://img.icons8.com/ios-glyphs/30/000000/refresh--v1.png"
alt="refresh-icon"
class="refresh-icon"
/>
</li>
<li>
<form class="form">
<label for="todo-input">
<input
type="text"
id="todo-input"
placeholder="Add to your list..."
/>
</label>
</form>
</li>
</ul>
<article class="footer">
Clear all completed
</article>
</section>
<template id="list-item-template">
<li class="list-item">
<label class="list-item-label">
<input type="checkbox" data-list-item-checkbox />
<span data-list-item-text></span>
</label>
<img
data-button-delete
src="https://img.icons8.com/ios-glyphs/30/000000/trash--v1.png"
alt="delete-icon"
class="delete-icon"
/>
</li>
</template>
</body>
</html>
I'm guessing that todo.index is a number. dataset values are always strings, so todo.index !== todoIndex will always be true when todo.index and todoIndex are different types.
Set todoIndex to an integer:
const todoIndex = parseInt(parent.dataset.todoIndex);
I'm a newbie in javascript, i've been following a youtube video of creating a simple project like booklist app using javascript the tutorial is very well explained but when i tried to do it myself i got stuck at one point i can't figure out what's happening
The project is basically about when i submit the details of the book it will be added to the table in the webpage, also it will stored in the local storage too. same like that i need to remove the details of the book from local storage when it is removed from the table.
Here is the code for setting up the class Store with methods getBooks for getting the books from the local storage, addBook for adding new book to local storage, removeBook for removing the book from local storage
class Store{
static getBooks(){
let books;
if (localStorage.getItem('books') == null) {
books = [];
} else{
books = JSON.parse(localStorage.getItem('books'));
}
return books;
}
static addBook(Book) {
const books = Store.getBooks();
books.push(Book);
localStorage.setItem('books', JSON.stringify(books));
}
static removeBook(isbn){
const books = Store.getBooks();
books.forEach((book, index) => {
if (book.isbn === isbn) {
books.splice(index, 1);
}
});
localStorage.setItem('books', JSON.stringify(books));
}
}
The methods getBooks and addBooks are working perfectly fine, but the removeBook method is not working in a way that i wanted.
Here is how i invoked the method,
document.querySelector('#book-list').addEventListener('click', (e) => {
// Delete book from the table in interface
UI.deleteBook(e.target);
Store.removeBook(e.target.parentElement.previousElementSibling.textContent);
});
e.target.parentElement.previousElementSibling.textContent is getting the correct value i needed, so i did made the call to removeBook successfully but i can't pass through the if block inside the method
Here is my complete HTML script
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title>Book List</title>
<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.15.3/css/all.css" integrity="sha384-SZXxX4whJ79/gErwcOYf+zWLeJdY/qpuqC4cAa9rOGUstPomtqpuNWT9wdPEn2fk" crossorigin="anonymous">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootswatch#4.5.2/dist/yeti/bootstrap.min.css" integrity="undefined" crossorigin="anonymous">
</head>
<body>
<div class="container mt-4">
<h1 class="display-4 text-center">
<i class="fas fa-book-open text-primary"></i> My<span class="text-primary">Book
</Myspan>List</h1>
<form class="book-form">
<div class="form-group">
<label for="title">Title</label>
<input type="text" id="title" class="form-control" autocomplete="off">
</div>
<div class="form-group">
<label for="author">Author</label>
<input type="text" id="author" class="form-control" autocomplete="off">
</div>
<div class="form-group">
<label for="isbn">ISBN#</label>
<input type="text" id="isbn" class="form-control" autocomplete="off">
</div>
<input type="submit" value="Add Book" class="btn btn-primary btn-sm">
</form>
<table class="table table-striped mt-5">
<thead>
<th>Title</th>
<th>Author</th>
<th>ISBN</th>
<th></th>
</thead>
<tbody id="book-list"></tbody>
</table>
</div>
<script type="text/javascript" src="app.js"></script>
</body>
</html>
Here is my complete javascript code,
class Book{
constructor(title, author, isbn){
this.title = title;
this.author = author;
this.isbn = isbn;
}
}
class UI{
static displayBooks(){
const books = Store.getBooks();
books.forEach((book) => UI.addBookToList(book))
}
static addBookToList(book){
const list = document.querySelector("#book-list");
const row = document.createElement('tr');
row.innerHTML = `
<td> ${book.title} </td>
<td> ${book.author} </td>
<td> ${book.isbn} </td>
<td>X</td>
`;
list.appendChild(row);
}
static deleteBook(el){
if(el.classList.contains('delete')){
el.parentElement.parentElement.remove();
}
}
static showAlert(message, className) {
const div = document.createElement('div');
div.className = `alert alert-${className}`;
div.appendChild(document.createTextNode(message));
const container = document.querySelector('.container');
const form = document.querySelector('.book-form');
container.insertBefore(div, form);
setTimeout(()=>
document.querySelector('.alert').remove(),
3000
);
}
static clearFields() {
document.querySelector('#title').value = '';
document.querySelector('#author').value = '';
document.querySelector('#isbn').value = '';
}
}
class Store{
static getBooks(){
let books;
if (localStorage.getItem('books') == null) {
books = [];
} else{
books = JSON.parse(localStorage.getItem('books'));
}
return books;
}
static addBook(Book) {
const books = Store.getBooks();
books.push(Book);
localStorage.setItem('books', JSON.stringify(books));
}
static removeBook(isbn){
const books = Store.getBooks();
books.forEach((book, index) => {
if (book.isbn.toString() === isbn.toString()) {
books.splice(index, 1);
}
});
localStorage.setItem('books', JSON.stringify(books));
}
}
document.addEventListener("DOMContentLoaded", UI.displayBooks());
document.querySelector('.book-form').addEventListener('submit', (e) => {
e.preventDefault();
const title = document.querySelector('#title').value;
const author = document.querySelector('#author').value;
const isbn = document.querySelector('#isbn').value;
if (title === '' || author === '' || isbn === '') {
UI.showAlert("Please fill in all fileds", "danger");
} else {
const book = new Book(title, author, isbn);
UI.addBookToList(book);
Store.addBook(book);
UI.clearFields();
UI.showAlert("Succefully added", 'success');
}
});
document.querySelector('#book-list').addEventListener('click', (e) => {
Store.removeBook(e.target.parentElement.previousElementSibling.textContent);
UI.deleteBook(e.target);
UI.showAlert("Succefully removed", 'success');
});
I spent one and half hour to figure out what's wrong in the code but i still can't, I'm completely new to javascript.
The problem is that in your HTML you pad the book ISBN (and other fields) with spaces:
row.innerHTML = `
<td> ${book.title} </td>
<td> ${book.author} </td>
<td> ${book.isbn} </td>
<td>X</td>
`;
This means that the textContent of those td elements will not match with the properties of your book object. Either trim what you get from textContent, or just remove those spaces from your HTML:
row.innerHTML = `
<td>${book.title}</td>
<td>${book.author}</td>
<td>${book.isbn}</td>
<td>X</td>
`;
If your goal was to give these texts a bit of margin, then do that with CSS styling instead.
There are also 2 other issues I bumped into:
Your HTML has </Myspan>, which should be </span>.
You don't correctly set the handler for the DOMContentLoaded event. The argument should be a function, but you actually execute a function instead (immediately). So remove the parentheses at the end:
document.addEventListener("DOMContentLoaded", UI.displayBooks);
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);
}