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>
Related
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>
I am working on a website that can generate templates with drag and drop. I have made an element generator. But the problem is that I can only drag and drop the elements that are already in the "container" as an example " already button" but the generated items are not droppable.
I'm new to JavaScript so don't know much about it. Please solve this problem. So I may continue to work on my website Here is my code
<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>
</head>
<style>
body {
margin: 0;
}
.container {
background-color: #333;
padding: 1rem;
margin-top: 1rem;
}
.draggable {
padding: 1rem;
background-color: white;
border: 1px solid black;
cursor: move;
}
.draggable.dragging {
opacity: .5;
}
/* Background Styles Only */
#import url('https://fonts.googleapis.com/css?family=Raleway');
* {
font-family: Raleway;
}
.side-links {
position: absolute;
top: 15px;
right: 15px;
}
.side-link {
display: flex;
align-items: center;
justify-content: center;
text-decoration: none;
margin-bottom: 10px;
color: white;
width: 180px;
padding: 10px 0;
border-radius: 10px;
}
.side-link-youtube {
background-color: red;
}
.side-link-twitter {
background-color: #1DA1F2;
}
.side-link-github {
background-color: #6e5494;
}
.side-link-text {
margin-left: 10px;
font-size: 18px;
}
.side-link-icon {
color: white;
font-size: 30px;
}
</style>
<body>
<textarea id="ambtnhtml" name="generatedCode1" class="generatedCode1"> <button type="button" class="draggable" id="" draggable ="true"> Button</button></textarea>
<!-- area -->
<button type="button" id="generatehtml">generate button </button>
<div class="container pipp" style="margin: auto;">
<button type="button" class="draggable AM-btnhj" id="" draggable="true">Already Button</button>
</div>
<div class="container">
</div>
</body>
<!-- drag and drop able script -->
<script>
const draggables = document.querySelectorAll('.draggable')
const containers = document.querySelectorAll('.container')
draggables.forEach(draggable => {
draggable.addEventListener('dragstart', () => {
draggable.classList.add('dragging')
})
draggable.addEventListener('dragend', () => {
draggable.classList.remove('dragging')
})
})
containers.forEach(container => {
container.addEventListener('dragover', e => {
e.preventDefault()
const afterElement = getDragAfterElement(container, e.clientY)
const draggable = document.querySelector('.dragging')
if (afterElement == null) {
container.appendChild(draggable)
} else {
container.insertBefore(draggable, afterElement)
}
})
})
function getDragAfterElement(container, y) {
const draggableElements = [...container.querySelectorAll('.draggable:not(.dragging)')]
return draggableElements.reduce((closest, child) => {
const box = child.getBoundingClientRect()
const offset = y - box.top - box.height / 2
if (offset < 0 && offset > closest.offset) {
return {
offset: offset,
element: child
}
} else {
return closest
}
}, {
offset: Number.NEGATIVE_INFINITY
}).element
}
</script>
<!-- add new button -->
<script src="http://code.jquery.com/jquery-1.8.1.min.js"></script>
<script>
$(document).ready(function() {
$("#generatehtml").click(function() {
$(".pipp").append($("#ambtnhtml").val());
$("ambtnhtml").val("");
});
});
</script>
</html>
Please Help
You missed adding event handlers to the new elements. I modified your example by adding event handlers to a separate function initDraggable so that later it would be easier to use it when generating new elements.
<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>
</head>
<style>
body {
margin: 0;
}
.container {
background-color: #333;
padding: 1rem;
margin-top: 1rem;
}
.draggable {
padding: 1rem;
background-color: white;
border: 1px solid black;
cursor: move;
}
.draggable.dragging {
opacity: .5;
}
/* Background Styles Only */
#import url('https://fonts.googleapis.com/css?family=Raleway');
* {
font-family: Raleway;
}
.side-links {
position: absolute;
top: 15px;
right: 15px;
}
.side-link {
display: flex;
align-items: center;
justify-content: center;
text-decoration: none;
margin-bottom: 10px;
color: white;
width: 180px;
padding: 10px 0;
border-radius: 10px;
}
.side-link-youtube {
background-color: red;
}
.side-link-twitter {
background-color: #1DA1F2;
}
.side-link-github {
background-color: #6e5494;
}
.side-link-text {
margin-left: 10px;
font-size: 18px;
}
.side-link-icon {
color: white;
font-size: 30px;
}
</style>
<body>
<textarea id="ambtnhtml" name="generatedCode1" class="generatedCode1"> <button type="button" class="draggable" id="" draggable ="true"> Button</button></textarea>
<!-- area -->
<button type="button" id="generatehtml">generate button </button>
<div class="container pipp" style="margin: auto;">
<button type="button" class="draggable AM-btnhj" id="" draggable="true">Already Button</button>
</div>
<div class="container">
</div>
</body>
<!-- drag and drop able script -->
<script>
const draggables = document.querySelectorAll('.draggable')
const containers = document.querySelectorAll('.container')
function initDraggable (draggable) {
draggable.addEventListener('dragstart', () => {
draggable.classList.add('dragging')
})
draggable.addEventListener('dragend', () => {
draggable.classList.remove('dragging')
})
}
draggables.forEach(initDraggable)
containers.forEach(container => {
container.addEventListener('dragover', e => {
e.preventDefault()
const afterElement = getDragAfterElement(container, e.clientY)
const draggable = document.querySelector('.dragging')
if (afterElement == null) {
container.appendChild(draggable)
} else {
container.insertBefore(draggable, afterElement)
}
})
})
function getDragAfterElement(container, y) {
const draggableElements = [...container.querySelectorAll('.draggable:not(.dragging)')]
return draggableElements.reduce((closest, child) => {
const box = child.getBoundingClientRect()
const offset = y - box.top - box.height / 2
if (offset < 0 && offset > closest.offset) {
return {
offset: offset,
element: child
}
} else {
return closest
}
}, {
offset: Number.NEGATIVE_INFINITY
}).element
}
</script>
<!-- add new button -->
<script src="http://code.jquery.com/jquery-1.8.1.min.js"></script>
<script>
$(document).ready(function() {
$("#generatehtml").click(function() {
const newDraggable = $($("#ambtnhtml").val())
initDraggable(newDraggable.get(0))
$(".pipp").append(newDraggable);
$("ambtnhtml").val("");
});
});
</script>
</html>
I have an API app that lists of names that clicked on a modal pops up and shows the data.
There is another button that is connected to a second modal with more data.
The problem is that this second modal runs through all the data instead of only displaying the proper data for the name selected. I was told the reason is because I have two event listeners but I should only have 1.
Here is my codepen and here is where I was told the problem was.
https://codepen.io/drxl/pen/WNjJQXa
function addListItem(pokemon) {
let pokeUl = document.querySelector('.list-group');
let listItem = document.createElement('li');
let button = document.createElement('button');
let baseStatsButton = document.querySelector('#base-stats-button');
button.innerText = pokemon.name;
button.classList.add('btn');
button.classList.add('btn-primary');
listItem.classList.add('group-list-item');
button.setAttribute("data-target", "#my-modal");
button.setAttribute("data-toggle", "modal");
listItem.appendChild(button);
pokeUl.appendChild(listItem);
button.addEventListener('click', function() {
showDetails(pokemon);
});
baseStatsButton.addEventListener('click', function() {
showStatModal(pokemon);
});
}
Right now when you add the pokemon, you add the same click event listener to the stats button every time. That ends up executing the event handler number-of-pokemon times when clicked. There is only one button so only one event listener is needed
What we can do is to set up a global currentPokemon outside the functions. Then when you click a pokemon button, you save the pokemon and when you click base stats, show the base stats for currentPokemon
So let's move some constants out of the add function and save the pokemon clicked
const pokeUl = document.querySelector('.list-group');
const baseStatsButton = document.querySelector('#base-stats-button');
let currentPokemon; // define a reusable variable
function addListItem(pokemon) {
let listItem = document.createElement('li');
let button = document.createElement('button');
button.innerText = pokemon.name;
button.classList.add('btn');
button.classList.add('btn-primary');
listItem.classList.add('group-list-item');
button.setAttribute("data-target", "#my-modal");
button.setAttribute("data-toggle", "modal");
listItem.appendChild(button);
pokeUl.appendChild(listItem);
button.addEventListener('click', function () {
showDetails(pokemon);
currentPokemon = pokemon; // save
});
};
then use it in the stats
function showStatModal() {
if (!currentPokemon) return; // for some reason we have invoked the modal before clicking
const item = currentPokemon;
let pokemonRepository = (function () {
let pokemonList = [];
let apiUrl = 'https://pokeapi.co/api/v2/pokemon/?limit=150';
let searchInput = document.querySelector("#searchIn");
function add(pokemon) {
pokemonList.push(pokemon);
}
function getAll() {
return pokemonList;
}
const pokeUl = document.querySelector('.list-group');
const baseStatsButton = document.querySelector('#base-stats-button');
let currentPokemon;
function addListItem(pokemon) {
let listItem = document.createElement('li');
let button = document.createElement('button');
button.innerText = pokemon.name;
button.classList.add('btn');
button.classList.add('btn-primary');
listItem.classList.add('group-list-item');
button.setAttribute("data-target", "#my-modal");
button.setAttribute("data-toggle", "modal");
listItem.appendChild(button);
pokeUl.appendChild(listItem);
button.addEventListener('click', function () {
showDetails(pokemon);
currentPokemon = pokemon;
});
}
baseStatsButton.addEventListener('click', showStatModal);
function loadList() {
return fetch(apiUrl).then(function (response) {
return response.json();
}).then(function (json) {
json.results.forEach(function (item) {
let pokemon = {
name: item.name,
detailsUrl: item.url
};
add(pokemon);
});
}).catch(function (e) {
console.error(e);
})
}
function loadDetails(item) {
let url = item.detailsUrl;
return fetch(url).then(function (response) {
return response.json();
}).then(function (details) {
//Add details to item
item.imageUrl = details.sprites.front_default;
item.imageUrlBack = details.sprites.back_default;
item.height = details.height / 10;
item.weight = details.weight / 10;
// pokemon types
item.types = [];
for (var i = 0; i < details.types.length; i++) {
item.types.push(details.types[i].type.name);
}
item.types = item.types.join(', ');
//pokemon abilities
item.abilities = [];
// eslint-disable-next-line no-redeclare
for (var i = 0; i < details.abilities.length; i++) {
item.abilities.push(details.abilities[i].ability.name);
}
item.abilities = item.abilities.join(', ');
}).catch(function (e) {
console.error(e);
});
}
//loads the stats for 2nd modal
function loadStats(item) {
let url = item.detailsUrl;
return fetch(url).then(function (response) {
return response.json();
}).then(function (details) {
//add details to stats
item.stats = details.stats.map(({ base_stat, stat: { name } }) =>
`${name}: ${base_stat}`).join("<br/>")
}).catch(function (e) {
console.error(e);
});
}
function showDetails(item) {
pokemonRepository.loadDetails(item).then(function () {
// console.log("item:", item);
showModal(item);
});
}
function showModal(item) {
pokemonRepository.loadDetails(item).then(function () {
// eslint-disable-next-line no-undef
let modalBody = $(".modal-body");
// eslint-disable-next-line no-undef
let modalTitle = $(".modal-title");
//clears previous content in modal
modalTitle.empty();
modalBody.empty();
//create elenebtb for pokemon name
// eslint-disable-next-line no-undef
let nameElement = $("<h1>" + item.name + "</h1>");
//create img element
// eslint-disable-next-line no-undef
let imageElementFront = $('<img class="modal-img" style="width:50%">');
imageElementFront.attr("src", item.imageUrl);
// eslint-disable-next-line no-undef
let imageElementBack = $('<img class="modal-img" style="width:50%">');
imageElementBack.attr("src", item.imageUrlBack);
//create element for pokemon height
// eslint-disable-next-line no-undef
let heightElement = $("<p>" + "Height: " + item.height + "m</p>");
//for pokemon weight
let weightElement = $("<p>" + "Weight: " + item.weight + "kgs</p>");
//pokemon types
// eslint-disable-next-line no-undef
let typesElement = $("<p>" + "Types: " + item.types + "</p>");
//pokemon abilities
// eslint-disable-next-line no-undef
let typesAbilities = $("<p>" + "Abilities: " + item.abilities + "</p>");
//eventlistener to for search bar
searchInput.addEventListener('input', function () {
let listPokemon = document.querySelectorAll('.group-list-item');
let value = searchInput.value.toUpperCase();
listPokemon.forEach(function (pokemon) {
if (pokemon.innerText.toUpperCase().indexOf(value) > -1) {
pokemon.style.display = '';
} else {
pokemon.style.display = 'none'
}
})
});
modalTitle.append(nameElement);
modalBody.append(imageElementFront);
modalBody.append(imageElementBack);
modalBody.append(heightElement);
modalBody.append(weightElement);
modalBody.append(typesElement);
modalBody.append(typesAbilities);
// eslint-disable-next-line no-undef
$('#my-modal').modal('toggle');
});
}
function loadStatDetails(item) {
pokemonRepository.loadStats(item).then(function () {
showStatModal(item);
});
}
function showStatModal() {
if (!currentPokemon) return; // for some reason we have invoked the modal before clicking
const item = currentPokemon;
pokemonRepository.loadStats(item).then(function () {
// eslint-disable-next-line no-undef
let StatmodalBody = $(".Statmodal-body");
// eslint-disable-next-line no-undef
let StatmodalTitle = $(".Statmodal-title");
//clears previous content in modal
StatmodalTitle.empty();
StatmodalBody.empty();
//create elenebtb for pokemon name
// eslint-disable-next-line no-undef
let nameElement = $("<h1>" + item.name + "</h1>");
//add stats
let statsElement = $("<p>" + item.stats + "<p>");
StatmodalTitle.append(nameElement);
StatmodalBody.append(statsElement);
$('#my-Statmodal').modal('show');
});
}
return {
add: add,
getAll: getAll,
addListItem: addListItem,
loadList: loadList,
loadDetails: loadDetails,
showDetails: showDetails,
loadStats: loadStats,
loadStatDetails: loadStatDetails,
};
})();
pokemonRepository.loadList().then(function () {
pokemonRepository.getAll().forEach(function (pokemon) {
pokemonRepository.addListItem(pokemon);
});
});
let link = document.getElementById("back-to-top");
var amountScrolled = 250;
//makes button show
window.addEventListener('scroll', function (e) {
if (this.window.pageYOffset > amountScrolled) {
link.classList.add('show');
} else {
link.className = 'back-to-top';
}
});
//scrolls to top
link.addEventListener('click', function (e) {
e.preventDefault();
var distance = 0 - window.pageYOffset;
var increments = distance / (500 / 16);
function animateScroll() {
window.scrollBy(0, increments);
if (window.pageYOffset <= document.body.offsetTop) {
clearInterval(runAnimation);
}
};
// Loop the animation function
var runAnimation = setInterval(animateScroll, 16);
});
/*
* Prefixed by https://autoprefixer.github.io
* PostCSS: v7.0.29,
* Autoprefixer: v9.7.6
* Browsers: last 4 version
*/
* {
-webkit-box-sizing: border-box;
box-sizing: border-box;
}
body {
background-color: rgb(3, 136, 180);
}
.navbar {
background-color: #ffcb05!important;
}
.navbar-brand {
color: #3d7dca!important;
}
.navbar-logo {
width: 42px;
height: 42px;
margin-left: 1rem;
}
.modal-header {
border-bottom: 1px solid black;
-webkit-box-pack: justify;
-ms-flex-pack: justify;
justify-content: space-between;
}
.modal-content {
background-color: #ffcb05!important;
border: #3d7dca solid 6px!important;
text-align: center;
}
.modal-close {
background-color: #ee1515;
border: white solid 2px;
color: white;
padding: 8.5px 16.5px;
border-radius: 50%;
font-size: 1.25rem;
}
.modal-close:active {
border-color: #ee1515;
background-color: white;
color: #ee1515;
}
.modal-title h1 {
margin-bottom: 0;
}
.modal h1 {
margin-top: 0;
text-align: left;
font-family: 'Gill Sans', 'Gill Sans MT', Calibri, 'Trebuchet MS', sans-serif;
text-transform: uppercase;
font-size: 3rem;
color: #3d7dca;
margin-left: 2rem;
}
.modal p {
margin-bottom: 0;
text-align: left;
font-weight: 700;
font-family: 'Gill Sans', 'Gill Sans MT', Calibri, 'Trebuchet MS', sans-serif;
font-size: 1.5rem;
color: #3d7dca;
text-transform: capitalize;
}
.list-group {
list-style-type: none;
text-align: center;
-webkit-padding-start: 0;
padding-inline-start: 0;
margin: 2rem;
text-transform: capitalize;
display: -webkit-box;
display: -ms-flexbox;
display: flex;
-webkit-box-orient: horizontal;
-webkit-box-direction: normal;
-ms-flex-direction: row;
flex-direction: row;
-ms-flex-wrap: wrap;
flex-wrap: wrap;
-webkit-box-pack: center;
-ms-flex-pack: center;
justify-content: center;
}
.list-group li {
width: auto;
margin: 1rem;
}
.Statmodal-body p {
margin-left: 3%;
}
.btn {
/*👉 background-color: #cc1313;👈*/
background-color: #db0606;
border: black solid 2px;
color: white;
text-transform: capitalize;
padding: 24px 72px;
width: 300px;
}
.btn:hover {
background-color: white;
border: black solid 2px;
color: #ee1515;
}
.btn-outline-success:hover {
background-color: white;
color: #ee1515;
border: black 2px solid;
}
.btn-primary {
font-size: 1.5rem;
}
.btn-primary:hover {
color: #ee1515;
background-color: white;
}
#search-button {
background-color: #db0606;
border: black solid 2px;
color: white;
padding: 4px 25px;
}
.modal-footer {
justify-content: center;
border-top: none;
}
.modal-button {
text-align: center;
width: auto;
}
.back-to-top {
background-color: #ee1515;
color: #FFFFFF;
opacity: 0;
transition: opacity .6s ease-in-out;
z-index: 999;
position: fixed;
right: 20px;
bottom: 20px;
width: 50px;
height: 50px;
box-sizing: border-box;
border-radius: 0%;
}
a.back-to-top {
font-weight: 1000;
letter-spacing: 2px;
font-size: 14px;
text-transform: uppercase;
text-align: center;
line-height: 1.6;
padding-left: 2px;
padding-top: 14px;
text-decoration: none;
}
.back-to-top:hover,
.back-to-top:focus,
.back-to-top:visited {
color: #FFFFFF;
}
.back-to-top.show {
opacity: 1;
}
#media screen and (max-width: 727px) {
.btn {
width: 250px;
}
}
#media screen and (max-width:627px) {
.btn {
padding: 10px 10px;
width: 200px;
font-size: 1.15rem;
}
}
#media screen and (max-width:575px) {
.justify-content-between {
-webkit-box-pack: center!important;
-ms-flex-pack: center!important;
justify-content: center!important;
}
.form-inline {
-webkit-box-pack: center;
-ms-flex-pack: center;
justify-content: center;
}
#search-button {
margin-top: .5rem;
padding: 4px 42px;
}
.modal p {
font-size: 1.5rem!important;
}
}
#media screen and (max-width:500px) {
.modal p {
font-size: 1.5rem!important;
}
}
#media screen and (max-width: 493px) {
.justify-content-between {
-webkit-box-pack: center!important;
-ms-flex-pack: center!important;
justify-content: center!important;
}
}
#media screen and (max-width:450px) {
.modal-header {
-webkit-box-align: center!important;
-ms-flex-align: center!important;
align-items: center!important;
}
.modal-title h1 {
font-size: 1.75rem;
}
button {
font-size: .85rem;
}
.modal p {
font-size: 1rem!important;
}
}
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>PokemonAPI</title>
<link href="img/Poke_Ball.png" rel="shortcut icon" type="image/x-icon" />
<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://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous">
<link href="css/styles.css" rel="stylesheet">
</head>
<body>
<!--Nav-->
<nav class="navbar navbar-light bg-light justify-content-between">
<a class="navbar-brand">PokemonAPI<img class="navbar-logo" src="img/Poke_Ball.png"></a>
<form class="form-inline">
<input id="searchIn" class="form-control mr-sm-2" type="search" placeholder="Search for a Pokemon" aria-label="Search for Pokemon">
<button id="search-button" type="submit">Search</button>
</form>
</nav>
<!--list of pokemon-->
<ul class="list-group"></ul>
<div class="modal fade" id="my-modal" aria-hidden="true" tabindex="-1" role="dialog" aria-labelledby="pokemonModalLabel">
<div class="modal-dialog modal-dialog-centered" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" aria-labelledby="pokemonModalLabel">Modal 1 title</h5>
<button type="button" class="modal-close" data-bs-dismiss="modal" aria-label="Close">X</button>
</div>
<div class="modal-body">
</div>
<div class="modal-footer">
<button id="base-stats-button" class="btn modal-button" data-bs-target="#my-Statmodal" data-bs-toggle="modal" data-bs-dismiss="modal">Base Stats</button>
</div>
</div>
</div>
</div>
<div class="modal fade" id="my-Statmodal" aria-hidden="true" aria-labelledby="pokemonStatModalLabel" tabindex="-1" role="dialog">
<div class="modal-dialog modal-dialog-centered" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="Statmodal-title" id="exampleModalToggleLabel2" aria-labelledby="pokemonStatModalLabel">Modal 2 title</h5>
<button type="button" class="modal-close" data-bs-dismiss="modal" aria-label="Close">X</button>
</div>
<div class="Statmodal-body">
</div>
<div class="modal-footer">
<button class="btn modal-button" data-bs-target="#my-modal" data-bs-toggle="modal" data-bs-dismiss="modal">Back to Pokemon</button>
</div>
</div>
</div>
</div>
<!--Top of Page Button-->
<i class="fas fa-arrow-up fa-2x"></i>
<!-- jQuery first, then Popper.js, then Bootstrap JS -->
<script src="https://code.jquery.com/jquery-3.5.1.js" integrity="sha256-QWo7LDvxbWT2tbbQ97B53yJnYU3WhH/C8ycbRAkjPDc=" crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap#5.0.2/dist/js/bootstrap.bundle.min.js" integrity="sha384-MrcW6ZMFYlzcLA8Nl+NtUVF0sA7MsXsP1UyJoMp4YLEuNSfAP+JcXn/tWtIaxVXM" crossorigin="anonymous"></script>
<!--PolyFills-->
<script src="js/promise-polyfill.js"></script>
<script src="js/fetch-polyfill.js"></script>
<!--JS-->
<script src="js/scripts.js"></script>
</body>
</html>
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
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');
}