Search filter for PokeAPI - javascript

Hi I'm making a search filter from the PokeAPI, but I'm getting a TypeError: Cannot read property 'filter' of undefined at HTMLInputElement. I want that when searching for a pokemon that it shows up. I probably do something wrong it could be a great help if some could help me with it. Thank you for your help.
const PokemonContainer = document.getElementById('pokemon__containerID');
const SearchContainer = document.getElementById('search__containerID');
const SearchElement = document.createElement('input');
SearchElement.setAttribute('type', 'text');
SearchElement.setAttribute('name', 'searchBar');
SearchElement.setAttribute('placeholder', 'Search...');
SearchContainer.appendChild(SearchElement);
const PokemonNumber = 151;
const createPokemonCard = (pokemon) => {
const PokemonElement = document.createElement('div');
const PokemonName = pokemon.name[0].toUpperCase() + pokemon.name.slice(1);
const PokemonID = pokemon.id;
const PokemonType = pokemon.types[0].type.name;
const PokemonTypeColors = {
fire: '#EE8130',
grass: '#7AC74C',
eletric: '#F7D02C',
water: '#6390F0',
ground: '#E2BF65',
rock: '#B6A136',
fairy: '#D685AD',
poison: '#A33EA1',
bug: '#A6B91A',
dragon: '#6F35FC',
psychic: '#F95587',
flying: '#A98FF3',
fighting: '#C22E28',
normal: '#A8A77A',
ice: '#96D9D6',
ghost: '#735797',
dark: '#705746',
steel: '#B7B7CE',
};
const AddColors = PokemonTypeColors[PokemonType];
PokemonElement.style.backgroundColor = AddColors;
const PokemonInnerHTML = `
<div class="pokemon__imageContainer">
<img src="https://pokeres.bastionbot.org/images/pokemon/${PokemonID}.png" />
</div>
<div class="pokemon__infomationContainer">
<span class="pokemon__id">#${PokemonID.toString().padStart(3, '0')}</span>
<h3 class="pokemon__name">${PokemonName}</h3>
<small class="pokemon__type">Type: <span>${PokemonType}</span></small>
</div>`;
PokemonElement.setAttribute('class', 'pokemon__card');
PokemonElement.innerHTML = PokemonInnerHTML;
PokemonContainer.appendChild(PokemonElement);
};
const getPokemons = async (id) => {
const api_url = `https://pokeapi.co/api/v2/pokemon/${id}`;
const response = await fetch(api_url);
const data = await response.json();
createPokemonCard(data);
createSearchFilter(data);
};
const receivePokemons = async () => {
for (let item = 1; item <= PokemonNumber; item++) {
await getPokemons(item);
}
};
receivePokemons();
const createSearchFilter = (pokemonData) => {
console.log(pokemonData);
SearchElement.addEventListener('keyup', (event) => {
const SearchValue = event.target.value;
const FilteredPokemons = pokemonData.filter((pokemon) => {
return (
pokemon.name.includes(SearchValue) || pokemon.id.includes(SearchValue)
);
});
createPokemonCard(FilteredPokemons);
console.log(FilteredPokemons);
});
};
createSearchFilter();
#import url('https://fonts.googleapis.com/css2?family=Lato:ital,wght#0,100;0,300;0,400;0,700;0,900;1,100;1,300;1,400;1,700;1,900&display=swap');
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
background: #efefbb;
background: -webkit-linear-gradient(to right, #d4d3dd, #efefbb);
background: linear-gradient(to right, #d4d3dd, #efefbb);
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
font-family: 'Lato';
}
h1 {
letter-spacing: 3px;
}
.pokemon__container {
display: flex;
flex-wrap: wrap;
justify-content: center;
align-items: space-between;
max-width: 100vw;
}
.pokemon__card {
background: #eeeeee;
border-radius: 20px;
padding: 20px 40px;
margin: 10px;
box-shadow: 0 3px 15px rgba(100, 100, 100, 0.6);
}
.pokemon__imageContainer {
margin-top: 20px;
width: 120px;
height: 120px;
}
.pokemon__imageContainer img {
width: 100%;
}
.pokemon__infomationContainer {
margin-top: 20px;
text-align: center;
}
.pokemon__id {
background: #ffffff80;
border-radius: 10px;
font-size: 1rem;
padding: 5px 10px;
}
.pokemon__name {
margin: 15px 0 7px 0;
letter-spacing: 1px;
}
<!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>Document</title>
<script src="function.js" defer></script>
<link rel="stylesheet" href="style.css" />
</head>
<body>
<h1>PokeDex</h1>
<div class="search__container" id="search__containerID"></div>
<div class="pokemon__container" id="pokemon__containerID"></div>
</body>
</html>

The problem is you're calling createSearchFilter again and again inside getPokemons which is the root of the problem.
What I would suggest is after you get all the date call createSearchFilter once and no need to pass in any data, we will hide/show the DOM elements.
One additional thing that I would suggest is add an id to the pokemon__card and set it equal to the pokemon's name, this will make searching fairly simple.
PokemonElement.setAttribute("id", PokemonName);
Next, inside createSearchFilter function grab all pokemon cards from the DOM and listen for the keyup event. Inside the event listener check if the card's id includes the search term.
If it does, set the display of the card to block.
If it doesn't, set it to none.
const PokemonContainer = document.getElementById("pokemon__containerID");
const SearchContainer = document.getElementById("search__containerID");
const SearchElement = document.createElement("input");
SearchElement.setAttribute("type", "text");
SearchElement.setAttribute("name", "searchBar");
SearchElement.setAttribute("placeholder", "Search...");
SearchContainer.appendChild(SearchElement);
const PokemonNumber = 10;
const createPokemonCard = (pokemon) => {
const PokemonElement = document.createElement("div");
const PokemonName = pokemon.name[0].toUpperCase() + pokemon.name.slice(1);
PokemonElement.setAttribute("id", PokemonName);
const PokemonID = pokemon.id;
const PokemonType = pokemon.types[0].type.name;
const PokemonTypeColors = {
fire: "#EE8130",
grass: "#7AC74C",
eletric: "#F7D02C",
water: "#6390F0",
ground: "#E2BF65",
rock: "#B6A136",
fairy: "#D685AD",
poison: "#A33EA1",
bug: "#A6B91A",
dragon: "#6F35FC",
psychic: "#F95587",
flying: "#A98FF3",
fighting: "#C22E28",
normal: "#A8A77A",
ice: "#96D9D6",
ghost: "#735797",
dark: "#705746",
steel: "#B7B7CE",
};
const AddColors = PokemonTypeColors[PokemonType];
PokemonElement.style.backgroundColor = AddColors;
const PokemonInnerHTML = `
<div class="pokemon__imageContainer" id="${PokemonName}">
<img src="https://pokeres.bastionbot.org/images/pokemon/${PokemonID}.png" />
</div>
<div class="pokemon__infomationContainer">
<span class="pokemon__id">#${PokemonID.toString().padStart(3, "0")}</span>
<h3 class="pokemon__name">${PokemonName}</h3>
<small class="pokemon__type">Type: <span>${PokemonType}</span></small>
</div>`;
PokemonElement.setAttribute("class", "pokemon__card");
PokemonElement.innerHTML = PokemonInnerHTML;
PokemonContainer.appendChild(PokemonElement);
};
const getPokemons = async (id) => {
const api_url = `https://pokeapi.co/api/v2/pokemon/${id}`;
const response = await fetch(api_url);
const data = await response.json();
createPokemonCard(data);
};
const receivePokemons = async () => {
for (let item = 1; item <= PokemonNumber; item++) {
await getPokemons(item);
}
createSearchFilter();
};
receivePokemons();
const createSearchFilter = (pokemonData) => {
const cards = document.querySelectorAll(".pokemon__card");
SearchElement.addEventListener("keyup", (event) => {
const val = event.target.value.toLowerCase();
cards.forEach((card) => {
if (card.id.toLowerCase().includes(val)) {
card.style.display = "block";
} else {
card.style.display = "none";
}
});
});
};
#import url('https://fonts.googleapis.com/css2?family=Lato:ital,wght#0,100;0,300;0,400;0,700;0,900;1,100;1,300;1,400;1,700;1,900&display=swap');
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
background: #efefbb;
background: -webkit-linear-gradient(to right, #d4d3dd, #efefbb);
background: linear-gradient(to right, #d4d3dd, #efefbb);
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
font-family: 'Lato';
}
h1 {
letter-spacing: 3px;
}
.pokemon__container {
display: flex;
flex-wrap: wrap;
justify-content: center;
align-items: space-between;
max-width: 100vw;
}
.pokemon__card {
background: #eeeeee;
border-radius: 20px;
padding: 20px 40px;
margin: 10px;
box-shadow: 0 3px 15px rgba(100, 100, 100, 0.6);
}
.pokemon__imageContainer {
margin-top: 20px;
width: 120px;
height: 120px;
}
.pokemon__imageContainer img {
width: 100%;
}
.pokemon__infomationContainer {
margin-top: 20px;
text-align: center;
}
.pokemon__id {
background: #ffffff80;
border-radius: 10px;
font-size: 1rem;
padding: 5px 10px;
}
.pokemon__name {
margin: 15px 0 7px 0;
letter-spacing: 1px;
}
<!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>Document</title>
<script src="function.js" defer></script>
<link rel="stylesheet" href="style.css" />
</head>
<body>
<h1>PokeDex</h1>
<div class="search__container" id="search__containerID"></div>
<div class="pokemon__container" id="pokemon__containerID"></div>
</body>
</html>

createSearchFilter() have to take parameter but in your code don't take any param that's why pokemonData is undefined

Related

pokemonApi filter function

Hey everyone I'm trying to use the filter method on the Pokémon api data, but I'm not understanding what I'm doing wrong. What I'm trying to accomplish is when a user clicks the filter buttons, the type of Pokémon is displayed.. for example..(user clicks fire button, fire Pokémon are displayed),
I tried to use the .pokeContainer div to display when user clicks the button but that's not working, any help would be greatly appreciated. Thank you
//Function to fetch 151 pokemon
function fetchPokemon() {
let pokemonArr = [];
// const url = `https://pokeapi.co/api/v2/pokemon?${i}`
// const url = 'Charizard, Mewtwo';
for (let i = 1; i <= 150; i++) {
const url = `https://pokeapi.co/api/v2/pokemon/${i}`
pokemonArr.push(fetch(url).then(data => data.json()))
// pokemonArr.push(url);
}
// console.log(pokemonArr)
//Promise.all to iterate over array and return a single array/Promise
// Promise.all(pokemonArr).then(data => console.log(data))
Promise.all(pokemonArr).then((results) => {
const pokemon = results.map((result) => ({
name: result.name,
id: result.id,
image: result.sprites['front_default'],
type: result.types.map((type) => type.type.name),
}))
createPokeCard(pokemon)
})
}
//Create pokemon div and display pokemon
const createPokeCard = (pokemon) => {
const allPokemonContainer = document.querySelector('.allPokemonContainer');
const pokemonInnerHTML = pokemon.map((pokemon) =>
`<div class="pokeContainer">
<image src="${pokemon.image}">
<h2 class="card-title">${pokemon.id}. ${pokemon.name}</h2>
<p>${pokemon.type}</p>
</div>`
)
allPokemonContainer.innerHTML = pokemonInnerHTML;
// typeFilter(pokemon)
searchFilter()
typeFilter(pokemon)
}
//Search filter for displaying Pokemon
const searchFilter = () => {
const searchBar = document.querySelector('.search-bar');
const pokeContainer = document.querySelectorAll('.pokeContainer');
searchBar.addEventListener('keyup', (event) => {
let value = event.target.value.toLowerCase();
console.log(value)
pokeContainer.forEach((container) => {
if (container.querySelector('h2').textContent.toLowerCase().includes(value)) {
container.style.display = 'block'
} else {
container.style.display = 'none'
}
})
// console.log('Hello')
// console.log(search);
})
}
// a filter for iterating through pokemon types
function typeFilter(pokemon) {
const pokeContainer = document.querySelectorAll('.pokeContainer');
const filterBtn = document.querySelectorAll('.filter-btn');
filterBtn.forEach(function(btn) {
btn.addEventListener('click', (e) => {
// console.log(btn);
// console.log(btn.innerText);
let type = e.currentTarget.innerText;
console.log(type);
// console.log(pokemon)
pokeContainer.forEach((container) => {
if (type === btn.innerText) {
// console.log(type)
container.style.display = 'block'
} else {
container.style.display = 'none'
}
})
let pokemonFilter = pokemon.filter((item) => {
// console.log(type);
if (item === item.type) {
// console.log(item);
return item;
}
})
// pokemon.filter((item) => {
// if (item === type) {
// return item;
// }
// })
// console.log(pokemon)
// console.log(pokemonFilter);
})
});
// console.log(pokemon);
}
// typeFilter();
fetchPokemon();
body {
// background-color: green;
margin: 0;
padding: 0;
}
// .logo {
// width: 50%
// }
// main {
// // background-image: url('imgs/pokeballcollection1.jpg');
// background-image: url('imgs/pokeballcollection2.jpg');
// }
.pokeContainer {
border: 2px solid black;
margin: 1rem;
background-color: rgb(255, 255, 255);
}
.flex-container {
display: flex;
// justify-content: space-between;
// justify-content: center;
text-align: center;
justify-content: space-around;
}
img {
// width: 100px;
width: 100%;
}
h3 {
text-align: center;
}
p {
width: 50px;
border: 1px solid black;
border-radius: 11px;
text-align: center;
background-color: rgb(95, 158, 160);
color: rgb(255, 255, 255);
}
ul {
padding: 0.5rem;
text-transform: uppercase;
}
ul.menu {
display: flex;
text-align: center;
}
li.list {
color: black;
}
li {
// border: 2px solid black;
width: 100px;
border-radius: 5px;
padding: 0.1rem;
list-style: none;
margin: 0.5rem;
// font-size: small;
font-size: 10px;
font-weight: 600;
// color: white;
}
.search {
display: flex;
justify-content: center;
}
.sort-method {
display: flex;
justify-content: center;
margin: 2rem 0;
button.sortAscending {
margin-right: 10px;
}
}
// .search {
// display: flex;
// justify-content: center;
// .search-bar {
// // background: url('../imgs/search-icon3.png') no-repeat 7px;
// background-size: 18px;
// // background-image: url('dist/css/pokemon-logo-png (1).png');
// }
// }
<!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">
<link rel="stylesheet" href="dist/css/styles.css">
<title>Pokemon API</title>
</head>
<body>
<main>
<header>
<nav>
<div class="navContainer">
<div class="logo">
<img src="imgs/pokemon-logo-png (1).png" alt="">
</div>
<div class="navLinks">
<ul class="menu">
<li class="list"><a>Home</a></li>
<li class="list"><a>Archive</a></li>
<li class="list"><a>Pokemon</a></li>
<li class="list"><a>About</a></li>
</ul>
</div>
</div>
</nav>
</header>
<div class="search">
<input type="text" class="search-bar" name="search" placeholder="search pokemon">
</div>
<div class="filterByType">
<button class="filter-btn">Grass</button>
<button class="filter-btn">Fire</button>
<button class="filter-btn">Water</button>
<button class="filter-btn">Electric</button>
<button class="filter-btn">Psyhcic</button>
<button class="filter-btn">Ground</button>
<button class="filter-btn">Steel</button>
<button class="filter-btn">Flying</button>
<button class="filter-btn">Bug</button>
<button class="filter-btn">Rock</button>
<button class="filter-btn">Fairy</button>
<button class="filter-btn">Dark</button>
</div>
<div class="sort-method">
<select name="drop-down" id="drop-down">
<option value="ID ASC">ID ASC</option>
<option value="ID DSC">ID DSC</option>
</select>
<!-- <button class="sortAscending">Sort A-Z</button>
<button class="sortDecending">Sort Z-A</button> -->
</div>
<div class="allPokemonContainer">
</div>
</main>
<script src="app4.js"></script>
</body>
</html>

Updating books cards after deletions

I currently working on Library application and I'm trying to update the data set attribute of each book card after deletion of one book. For example if I have added three books and they all have the values corresponding to their position in the array. 0-2 if I delete the first one . I want the data-book-number attribute to update to the respective positions these items now have in the array . So 0-1, in my current implementation I can delete book cards after adding them but their data-book-number remain the same . I need them to update them as that's how I'm checking if the button and the book match to remove that specific book from the array and DOM.
const addBtn = document.getElementById("add");
const modal = document.querySelector(".addModal");
const cancelBtn = document.getElementById("cancel");
const Name = document.getElementById("Name");
const authorName = document.getElementById("author");
const pageNumber = document.getElementById("number");
const newBookBtn = document.getElementById("bookBtn");
const bookContainer = document.getElementById("content");
// constructor
function Book(title, author, numPages, read) {
this.title = title;
this.author = author;
this.numPages = numPages;
this.read = read;
}
Book.prototype.info = function () {
return `${this.title} by ${this.author}, ${this.numPages} pages, Read? ${this.read}`;
};
// Lib array
let myLibrary = [];
//functions to add books to array
function addBookToLib() {
let title = Name.value;
let author = authorName.value;
let numberPage = pageNumber.value;
let isRead = document.querySelector("input[name=read]:checked").value;
const word = new Book(title, author, numberPage, isRead);
myLibrary.push(word);
}
// functions to loop over the array and display the details on a div
let count = 0;
const loopOver = () => {
for (let i = 0; i < myLibrary.length; i++) {
if (count <= i) {
i + count;
let newCard = document.createElement("div");
createBook(newCard, i);
let delBtn = document.createElement("button");
createDelBtn(delBtn, i);
newCard.appendChild(delBtn);
bookContainer.appendChild(newCard);
count++;
changeClr(newCard, i);
let allDelBtn = document.querySelectorAll(".numberBtn");
deleteBook(allDelBtn, newCard);
}
}
};
// Change books color
function changeClr(item, valueAt) {
if (myLibrary[valueAt].read === "Yes") {
item.classList.add("read");
} else if (myLibrary[valueAt].read === "No") {
item.classList.add("unread");
}
}
// create each book
function createBook(item, valueAt) {
item.dataset.bookNumber = valueAt;
item.classList.add("book");
item.innerHTML = `<h4>Title</h4>
<p>${myLibrary[valueAt].title}</p>
<h4>Author</h4>
<p>${myLibrary[valueAt].author}</p>
<h4>Number of pages</h4>
<p>${myLibrary[valueAt].numPages}</p>
<h4>Have you read this book?</h4>
<p>${myLibrary[valueAt].read}</p> `;
}
// create delete btn
function createDelBtn(btnName, valueAt) {
btnName.innerText = "Remove Book";
btnName.dataset.bookNumber = valueAt;
btnName.classList.add("numberBtn");
}
// delete a book function
function deleteBook(allBtn, book) {
allBtn.forEach((btn1) => {
btn1.addEventListener("click", () => {
let dataB1 = book.dataset.bookNumber;
let dataBT1 = btn1.dataset.bookNumber;
if (dataB1 === dataBT1) {
let selectedDiv = document.querySelector(
`[data-book-number="${dataB1}"]`
);
myLibrary.splice(dataB1, 1);
bookContainer.removeChild(selectedDiv);
count--;
}
});
});
}
// // function to change data-set after delete button has been pressed
// function changeAttr(allBooks, valueAt) {
// allBooks.forEach((book) => {
// book.dataset.bookNumber = valueAt;
// });
// }
// clear the modal box
const clearModal = () => {
Name.value = " ";
authorName.value = " ";
pageNumber.value = 0;
document.querySelector('input[name="read"]:checked').checked = false;
};
// display the modal
addBtn.addEventListener("click", () => {
modal.style.visibility = "visible";
Name.focus();
});
// hide modal and call clear modal function
cancelBtn.addEventListener("click", () => {
modal.style.visibility = "hidden";
clearModal();
});
// Run the method to add new object and pass it into the array
newBookBtn.addEventListener("click", (e) => {
addBookToLib();
loopOver();
clearModal();
modal.style.visibility = "hidden";
});
:root {
--clr1: #737373;
--cl2: ;
}
* {
padding: 0px;
margin: 0px;
}
body {
background-image: url(./images/susan-q-yin-2JIvboGLeho-unsplash.jpg);
background-position: center;
background-repeat: no-repeat;
background-size: cover;
background-attachment: fixed;
}
.container {
display: grid;
height: 100vh;
grid-template-columns: repeat(4, 1fr);
}
header {
grid-column: 2/5;
/* background-color: var(--clr1); */
}
aside {
grid-row: 1/2;
/* background-color: var(--clr1); */
}
/* main books */
main {
grid-area: 2 / 1 / 4 / 5;
position: relative;
display: grid;
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
grid-template-rows: repeat((auto-fit, minmax(300, 1fr)));
row-gap: 10px;
}
.book {
place-self: center;
width: 300px;
height: 200px;
background-color: grey;
border-radius: 5px;
display: flex;
flex-direction: column;
justify-content: center;
}
/* modal style */
.addModal {
visibility: hidden;
position: absolute;
place-self: center;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
width: 250px;
height: 300px;
background-color: lightgrey;
border-radius: 5px;
}
.addModal > h3 {
margin-bottom: 10px;
}
.addModal form {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
gap: 3px;
width: 150px;
}
.newBook {
padding: 5px;
border-radius: 5px;
border-color: lightgrey;
}
/* if read */
.read {
border-left: 8px solid blue;
}
/* if not read */
.unread {
border-left: 8px solid red;
}
.numberBtn {
width: 6rem;
}
/* Responsiveness */
#media screen and (max-width: 500px) {
.book {
width: 200px;
height: 150px;
place-self: center;
}
aside {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
gap: 4rem;
}
.book {
padding: 0.8rem;
}
.book h4 {
font-size: 1.1rem;
margin-left: 5px;
}
.book p {
margin-left: 5px;
}
}
<!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" />
<link rel="stylesheet" href="style.css" />
<title>My Library</title>
</head>
<body>
<div class="container">
<header>
<h1>Welcome to your library</h1>
<button id="add">New Book</button>
</header>
<!-- sidebar -->
<aside>
<h3>Read</h3>
<h3>Unread</h3>
</aside>
<main id="content">
<!-- modal pop up -->
<div class="addModal">
<h3>Add a Book</h3>
<form method="post" id="newForm">
<label for="Name">Book Name</label>
<input type="text" name="Name" id="Name" />
<label for="author">Author</label>
<input type="text" name="author" id="author" />
<label for="numPages">Number of Pages</label>
<input type="number" min="0" id="number" />
<label for="read">Have you Read it?</label>
<div class="answer-container">
<input type="radio" name="read" value="Yes" /> Yes
<input type="radio" name="read" value="No" /> No
</div>
</form>
<div class="button-contain">
<button class="newBook" id="bookBtn" type="submit">Add Book</button>
<button id="cancel" class="newBook">Cancel</button>
</div>
</div>
</main>
</div>
<script src="./script.js"></script>
</body>
</html>

How to add API to my weather web app for the location and temperature to show up once a location is searched

I am struggling to add API to my weather web app for the location and temperature to show up once a location is searched.
I added the API key to my code but once I search for a location the website cannot pick up a location and doesn't show the temperature for that specific location.
This is my index.html file:
```<!DOCTYPE html>
<html lang="en">
<head></head>
<link rel="stylesheet" href="style.css">
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Dani's Weather Web App</title>
</head>
<body>
<div class="topnav">
<form action="/script.js">
<input type="text" placeholder="Location..." name="location">
<button type="search">Search</button>
</form>
</div>
<main id="card-container">
<div id= "location">
<p>Weather in --------------------</p>
<div id="temperature">
<img id="temperature-icon" src="icons8-cloud-64.png" alt="">
<p><span id="Temperature Value">----------</span><span id="Temperature Unit"> °C </span></p>
</div>
</main>
</div>
<script src="script.js">
</script>
</body>
</html>```
This is my style.css file:
background-color: rgb(209, 171, 228);
padding: 0;
margin: 0;
display: flex;
width: 200vh;
height: 100vh;
justify-content: center;
align-items: center;
align-content: center;
font-family: 'Times New Roman', Times, serif;
font-size: large;
}
#card-container {
background: white;
width: 500px;
max-width: 100px;
background-color: rgb(233, 227, 227);
border-radius: 50px;
padding: 50px;
box-shadow: 100px;
}
#search-button {
display: flex;
position: topcenter;
width: 100px;
border: 10px;
background-color: blueviolet;
border-radius: 10px;
padding: 50px;
color: blueviolet;
animation-fill-mode: both;
transition: none;
cursor: auto;
}
#topnav {
display: flex;
width: 20px;
height: 20px;
justify-content: center;
align-items: center;
align-content: center;
}
#temperature-icon {
align-items: center;
width: 100px;
}```
This is my script.js file:
```const api = "ad8e05407ded209790d711d05cbf353a";
const iconImg = document.getElementById('weather-icon');
const loc = document.querySelector('#location');
const tempC = document.querySelector('.c');
const tempF = document.querySelector('.f');
const desc = document.querySelector('.desc');
const sunriseDOM = document.querySelector('.sunrise');
const sunsetDOM = document.querySelector('.sunset');
window.addEventListener ('load'), () => {
let long;
let lat;
if (navigator.geolocation) {
navigator.geolocation.getCurrentPosition((position) => {
long = position.coords.longitude;
lat = position.coords.latitude;
const base = `https://api.openweathermap.org/data/2.5/weather?lat=${lat}&lon=${long}&appid=${api}&units=metric`;
fetch(base)
.then((response) => {
return response.json();
})
.then((data) => {
console.log(data);
const { temp } = data.main;
const place = data.name;
const { description, icon } = data.weather[0];
const { sunrise, sunset } = data.sys;
const iconUrl = `http://openweathermap.org/img/wn/${icon}#2x.png`;
const fahrenheit = (temp * 9) / 5 + 32;
const sunriseGMT = new Date(sunrise * 1000);
const sunsetGMT = new Date(sunset * 1000);
iconImg.src = iconUrl;
loc.textContent = `${place}`;
desc.textContent = `${description}`;
tempC.textContent = `${temp.toFixed(2)} °C`;
tempF.textContent = `${fahrenheit.toFixed(2)} °F`;
sunriseDOM.textContent = `${sunriseGMT.toLocaleDateString()}, ${sunriseGMT.toLocaleTimeString()}`;
sunsetDOM.textContent = `${sunsetGMT.toLocaleDateString()}, ${sunsetGMT.toLocaleTimeString()}`;
});
});
}
};```

Click event on CSS element

I need to add a click event to a css class so when clicked it switches to a different class. Specifically, I have a character class on li items that I would like to change to another class when the li item is clicked. Code:
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>Document</title>
<link rel="stylesheet" href="app.css" />
</head>
<body>
<div class="container">
<h1>Client Search</h1>
<div id="searchWrapper">
<input
type="text"
name="searchBar"
id="searchBar"
placeholder="search for a character"
onkeyup="myFunction()"
/>
</div>
<ul id="charactersList"></ul>
</div>
<script src="app.js"></script>
</body>
</html>
css
body {
font-family: sans-serif;
background-color: #111d4a;
}
* {
box-sizing: border-box;
}
h1 {
color: #eee;
margin-bottom: 30px;
}
.container {
padding: 40px;
margin: 0 auto;
max-width: 1000px;
text-align: center;
}
#charactersList {
padding-inline-start: 0;
display: none;
grid-template-columns: repeat(auto-fit, minmax(320px, 1fr));
grid-gap: 20px;
}
.character {
list-style-type: none;
background-color: #eaeaea;
border-radius: 3px;
padding: 10px 20px;
display: grid;
grid-template-columns: 3fr 1fr;
grid-template-areas:
'name image'
'house image';
text-align: left;
}
.character:hover {
background-color: blue;
cursor: pointer;
}
.character > h2 {
grid-area: name;
margin-bottom: 0px;
}
.character > p {
grid-area: house;
margin: 0;
}
#searchBar {
width: 100%;
height: 32px;
border-radius: 3px;
border: 1px solid #eaeaea;
padding: 5px 10px;
font-size: 12px;
}
#searchWrapper {
position: relative;
}
#searchWrapper::after {
content: '🔍';
position: absolute;
top: 7px;
right: 15px;
}
javaScript
const charactersList = document.getElementById('charactersList');
const searchBar = document.getElementById('searchBar');
let clientNames = [];
searchBar.addEventListener('keyup', (e) => {
const searchString = e.target.value.toLowerCase();
const filteredCharacters = clientNames.filter((character) => {
return (
character.name.toLowerCase().includes(searchString) ||
character.house.toLowerCase().includes(searchString)
);
});
displayCharacters(filteredCharacters);
});
const loadCharacters = async () => {
try {
const res = await fetch('https://hp-api.herokuapp.com/api/characters');
clientNames = await res.json();
displayCharacters(hpCharacters);
} catch (err) {
console.error(err);
}
};
const displayCharacters = (characters) => {
const htmlString = characters
.map((character) => {
return `
<li class="character">
<h2>${character.name}</h2>
<p>House: ${character.house}</p>
</li>
`;
})
.join('');
charactersList.innerHTML = htmlString;
};
loadCharacters();
//change the display of characterListfrom none to grid
function myFunction() {
var charactersList = document.getElementById("charactersList");
charactersList.style.display = "grid";
//also check if searchBar is empty and set display back to none
var searchBar = document.getElementById("searchBar").value;
if (searchBar === ""){
charactersList.style.display = "none";
}
}
Great question!
I just made a CodePen to illustrate how to programmatically change CSS class names when a li item is clicked. Check out this example and let me know if this clarifies the problem : )
JS
let list = document.getElementById('myList');
let items = ['First', 'Second', 'Third', 'Fourth', 'Fifth'];
for(let item of items){
let li = document.createElement("LI");
li.appendChild(document.createTextNode(item));
li.classList.add('blue');
li.addEventListener("click", () => {
li.classList.remove('blue');
li.classList.add('red');
});
list.appendChild(li);
}
HTML
<ul id="myList"></ul>
CSS
.blue{
color: blue;
}
.red{
color: red;
}
https://codepen.io/CrowlsYung/pen/eYJRPjx
Suggested Change
<li class="character" onclick="handleItemClick(event)">
<h2>${character.name}</h2>
<p>House: ${character.house}</p>
</li>
function handleItemClick(e){
e.currentTarget.classList.toggle('prevClass')
e.currentTarget.classList.toggle('newClass');
}

Stop underline from getting bigger

I'm creating a currency converter app using html,css and javascript, and when text is entered into the <input> on the left, the converted value will appear in the input element on the right: <p id = "converted">.
I want to keep the underline(border-bottom) the same length on the <p id = "converted">. Currently, when you enter text into the input element on the left, the one on the right increases in size and makes the underline larger. I want the underline to stay the same as when there is no text in the element.
I am currently styling the <p id = "converted"> element like so:
padding-right: 40%;
border-bottom: 0.5vh solid white;
I do not think the API I am using will work correctly with the stack overflow snippets, but I will include a link to a codepen: https://codepen.io/oliknight/pen/XLvQow
let currlet currencyArr = [];
let ratesArr = [];
window.addEventListener("load", () => {
const api = "https://api.exchangeratesapi.io/latest?base=GBP";
fetch(api)
.then(response => {
return response.json();
})
.then(data => {
for (currency in data.rates) {
currencyArr.push(currency);
ratesArr.push(data.rates[currency]);
// create 'option' element here
var optionLeft = document.createElement("option");
var optionRight = document.createElement("option");
optionLeft.textContent = currency;
optionRight.textContent = currency;
document.querySelector("#left-select").appendChild(optionLeft);
document.querySelector("#right-select").appendChild(optionRight);
}
document.querySelector("#input").addEventListener("keyup", convert);
function convert() {
const input = document.querySelector("#input");
let leftSelectValue = document.querySelector("#left-select").value;
let convertedNumber = document.querySelector("#converted");
for (let i = 0; i < currencyArr.length; i++) {
if (leftSelectValue === currencyArr[i]) {
convertedNumber.textContent = ratesArr[i].toFixed(4) * input.value;
}
}
}
});
});
encyArr = [];
let ratesArr = [];
window.addEventListener("load", () => {
const api = "https://api.exchangeratesapi.io/latest?base=GBP";
fetch(api)
.then(response => {
return response.json();
})
.then(data => {
for (currency in data.rates) {
currencyArr.push(currency);
ratesArr.push(data.rates[currency]);
// create 'option' element here
var optionLeft = document.createElement("option");
var optionRight = document.createElement("option");
optionLeft.textContent = currency;
optionRight.textContent = currency;
document.querySelector("#left-select").appendChild(optionLeft);
document.querySelector("#right-select").appendChild(optionRight);
}
document.querySelector("#input").addEventListener("keyup", convert);
function convert() {
const input = document.querySelector("#input");
let leftSelectValue = document.querySelector("#left-select").value;
let convertedNumber = document.querySelector("#converted");
for (let i = 0; i < currencyArr.length; i++) {
if (leftSelectValue === currencyArr[i]) {
convertedNumber.textContent = ratesArr[i].toFixed(4) * input.value;
}
}
}
});
});
html {
font-family: "Lato", sans-serif;
font-weight: thin;
background-image: linear-gradient(to right top, #90d0ff, #008ef7);
color: white;
height: 100vh;
overflow: hidden;
}
h1 {
text-align: center;
font-size: 5em;
position: absolute;
left: 0;
right: 0;
margin: auto;
}
.container {
width: 50%;
display: flex;
justify-content: center;
align-items: center;
}
.container p {
font-size: 8em;
display: inline-block;
padding-right: 40%;
border-bottom: 0.5vh solid white;
max-width: 50%;
}
.parent {
display: flex;
justify-content: center;
height: 100vh;
align-items: center;
}
.container select {
background: transparent;
color: white;
padding: 20px;
width: 80px;
height: 60px;
border: none;
font-size: 20px;
box-shadow: 0 5px 25px rgba(0, 0, 0, 0.5);
-webkit-appearance: button;
outline: none;
margin-left: 10%;
}
.original {
background: transparent;
border: none;
border-bottom: 0.5vh solid white;
font-size: 8em;
max-width: 50%;
outline: none;
font-family: "Lato", sans-serif;
font-weight: thin;
color: white;
}
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<title>Currency Converter</title>
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link rel="stylesheet" href="main.css" />
<link href="https://fonts.googleapis.com/css?family=Lato:300&display=swap" rel="stylesheet" />
</head>
<body>
<h1>Currency Converter</h1>
<div class="parent">
<div class="container">
<input type="text" class="original" id="input" />
<select id="left-select"> </select>
</div>
<div class="container" id="ctn">
<p id="converted">0</p>
<select id="right-select"> </select>
</div>
</div>
<script src="main.js"></script>
</body>
</html>
Thanks!
If you replace max-width with width in your css, the percentage (e.g. 50%) width will stay the same, regardless of how long the converted number is. However this poses a problem regarding your overflow.
You could remove the padding, but you could still run into issues whereby only half a digit is showing at the end, depending on how big the device is:
So you may want to either reduce the font size or experiment with width percentages (maybe 49 or 51 .. ) , or both ..
If you knew exactly how many decimal digits there would be in the returned converted number, this would help determine an adequate size font.
Good luck, and hope this helps

Categories