Updating books cards after deletions - javascript

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>

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>

How do I use the input from my box to my function?

So I created a 16 x 16 grid where I can etch a sketch on that grid. It's working well. Right now, I have to call the function createGrid(number) everytime I want to change the size of the grid. I have created a text input boxes as you can see on my code. So instead of having to write it again everytime and refresh the page, I want to be able to use the input from this box to change the size of the grid.
One of the ways that I've tried is by creating a new variable such as:
let number = inputFromBox.value;
and then write createGrid(number) . But it doesn't work. Is there any way how to make this work ? by using the input from the box ? Please help. Thank you !
let container = document.querySelector('.container');
let rows = document.getElementsByClassName('gridRow');
let duplicateOfInput = document.getElementById('duplicateOfInput');
let button = document.getElementById('submit');
let inputFromBox = document.getElementsByClassName('size-box');
button.addEventListener('click', createGrid);
createGrid(16);
draw();
// createGrid(anyNumber);
// draw();
// duplicateOfInput.textContent = `${input}`;
// const rainbow = document.getElementsByClassName('rainbow');
let reset = document.getElementById('clear-button');
reset.addEventListener('click', clearGrid);
function createGrid(number) {
makeRow(number);
makeColumn(number);
draw();
}
function makeRow(numberOfRow) {
container.innerHTML = "";
for (let i = 0; i <numberOfRow; i++) {
let row = document.createElement('div');
container.appendChild(row);
row.classList.add('gridRow');
}
}
function makeColumn(numberOfColumn) {
for ( let i = 0; i < rows.length; i++) {
for ( let j = 0; j < numberOfColumn; j++) {
let column = document.createElement('div');
rows[j].appendChild(column);
column.classList.add('gridColumn');
}
}
}
//adds event listener to all divs with class "column"
//added in global scope to allow drawing on page load
//this refers to the element triggering the mouseover event listener
function draw() {
let columns = document.getElementsByClassName("gridColumn");
for (let i = 0; i < columns.length; i++) {
columns[i].addEventListener("mouseover", changeColor);
}
function changeColor() {
let blue = document.getElementById('blue');
let eraser = document.getElementById('eraser');
let black = document.getElementById('black');
let rainbow = document.getElementById('rainbow');
if (blue.checked) {
this.style.backgroundColor = 'blue';
} else if (eraser.checked) {
this.style.backgroundColor = 'beige';
} else if (black.checked) {
this.style.backgroundColor = 'black';
} else if (rainbow.checked) {
let randomColor = Math.floor(Math.random()*16777215).toString(16);
this.style.backgroundColor = '#' + randomColor;
}
}
}
//eraser function loops through all column divs and sets background to "" in DOM
function clearGrid() {
let columns = document.getElementsByClassName("gridColumn");
for (let i = 0; i < columns.length; i++) {
columns[i].style.backgroundColor = '';
}
}
#import url('https://fonts.googleapis.com/css2?family=Asap:wght#400;600;700&display=swap');
body {
display: flex;
height: 100%;
width: 100%;
flex-direction: column;
background-color: beige;
font-family: Asap, sans-serif;
margin: 0;
padding: 0;
justify-content: center;
align-content: center;
align-items: center;
background-repeat: no-repeat;
}
.header {
display: flex;
flex: 1;
justify-content: center;
}
#header-title {
font-family: Asap, sans-serif;
font-size: 18px;
}
#setGridSize {
display: inline-flex;
justify-content: center;
align-items: center;
flex: 1;
gap: 12px;
}
#guide {
text-align: center;
margin: 1px;
font-family: Asap, sans-serif;
color: red;
font-size: 13px;;
}
.canvas {
display: flex;
justify-content: center;
align-items: center;
}
.container {
display: flex;
flex-direction: column;
border: 1px solid green;
width: 550px;
height: 550px;
}
/* .gridColumn {
display: inline-flex;
border: 1px solid beige;
margin: -1px 0;
width: 30px;
height: 30px;
} */
.gridColumn {
flex: 1;
border: 1px solid beige;
}
.gridRow {
display: flex;
flex: 1;
}
.default {
background: beige;
}
#button-container {
margin: 3px;
}
#clear-button {
margin: 2px;
}
<!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>DOM Manipulation and Events</title>
<script src="javascript.js" defer></script>
<link rel="stylesheet" href="styles.css">
</head>
<body>
<h1 class="header"> Let's sketch ! </h1>
<div id="setGridSize">
<p id="header-title"> Grid Size :</p>
<input type="text" placeholder="Size of Board" class="size-box">
<span id = "duplicateOfInput"></span>
<button id="submit" > Submit </button>
</div>
<p id="guide"> Enter a number between 2 to 99 </p>
<div class="canvas">
<div class="container"></div>
</div>
<div class="buttons">
<form id="button-container">
<input type="radio" id="blue" name="pen" value="blue-pen"><label for = "blue-pen"> Blue </label>
<input type="radio" id="eraser" name="pen" value="eraser"><label for = "eraser" > Eraser </label>
<input type="radio" id="black" name="pen" value="black-pen"> <label for = "black" > Black </label>
<input type="radio"id="rainbow" name="pen" value="black-pen"> <label for = "rainbow" > Rainbow </label>
</form>
</div>
<button id = "clear-button" > Clear </button>
</body>
</html>
You can use let inputFromBox = document.getElementById('size-box'); and with let number = inputFromBox.value; you can get the value within the click function.
CodePen
You're using document.getElementsByClassName, which is most likely returning an array. Try using inputFromBox[0].value or switching to finding it by an ID.
You may find it easier to use CSS Grid. It allows you to minimise the amount of code you write because you don't have to create rows and columns separately.
Additionally if you set up some CSS variables you can hook the value from the input directly into the stylesheet to update the dimensions of the grid.
// Cache the elements
const grid = document.querySelector('.grid');
const submit = document.querySelector('.submit');
const input = document.querySelector('.size');
// When the button is clicked call `createGrid`
submit.addEventListener('click', createGrid);
function createGrid() {
// Get the value from the input
const { value } = input;
// Array to hold the box strings
const boxes = [];
// Loop to create a grid of boxes - for each
// box push it into the array
for (let i = 1; i <= value * value; i++) {
boxes.push(`<div class="box">${i}</div>`);
}
// `join` the array and update the innerHTML
// of the grid element
grid.innerHTML = boxes.join('');
// Pass the value directly into the spreadsheet.
// This changes the default `--grid-dimension` to the new value
document.documentElement.style.setProperty('--grid-dimension', value);
}
:root { --box-width: 30px; --grid-dimension: 13; }
.grid { margin-top: 1em; display: grid; grid-template-columns: repeat(var(--grid-dimension), var(--box-width)); gap: 0.2em; }
.box { display: flex; justify-content: center; align-items: center; height: var(--box-width); width: var(--box-width); background-color: lightgreen; }
.box:hover { background-color: lightblue; cursor: pointer; }
<input type="text" placeholder="Size of grid" class="size">
<button type="button" class="submit">Create grid</button>
<div class="grid"></div>

Buttons not working xxxxx.addEventListener is not a function

I created a 16 x 16 grid where I can etch a sketch on that grid. It's working with the default colour that I use. When I try adding buttons to change colours to sketch. I can't seem to make it work. I've tried various methods and writing it with various ways but again and again I failed. I want when I click on the buttons, it changes colour when I sketch. I'll include below the previous code that's working and one of the ways that I've tried. Any expert/master please have a look on my code.
let container = document.querySelector('.container');
let rows = document.getElementsByClassName('gridRow');
let columns = document.getElementsByClassName('gridColumn');
const blue = document.getElementsByClassName('blue');
const eraser = document.getElementsByClassName('eraser');
const black = document.getElementsByClassName('black');
let reset = document.getElementById('reset');
function createGrid(number) {
makeRow(number);
makeColumn(number);
changeColours();
}
function makeRow(numberOfRow) {
for (let i = 0; i <numberOfRow; i++) {
let row = document.createElement('div');
container.appendChild(row);
row.classList.add('gridRow');
}
}
function makeColumn(numberOfColumn, selection) {
for ( let i = 0; i < rows.length; i++) {
for ( let j = 0; j < numberOfColumn; j++) {
let column = document.createElement('div');
The part below is what I tried, erase it if you want it to work with just one colour
if (selection == 'blue') {
column.addEventListener('mouseenter', function() {
column.classList.add('blue');
})
} else if (selection == 'eraser') {
column.addEventListener('mouseenter', function() {
column.classList.add('eraser');
})
} else if (selection == 'black') {
column.addEventListener('mouseenter', function() {
column.classList.add('black');
})
} else {
column.addEventListener('mouseenter', function() {
column.classList.add('colored');
})
}
// column.addEventListener('mouseleave', () => {
// column.classList.remove('colored');
// })
Just erase part of the code above if you want to make it work
rows[j].appendChild(column);
column.classList.add('gridColumn');
}
}
}
The part below is what I tried, erase it if you want it to work with just one colour
blue.addEventListener('click', function() {
makeColumn(number, 'blue');
})
eraser.addEventListener('click', function() {
makeColumn(number, 'white');
})
black.addEventListener('click', function() {
makeColumn(number, 'black');
})
Just erase part of the code above if you want to make it work
createGrid(16);
#importurl('https://fonts.googleapis.com/css2family=Asap:wght#400;600;700&display=swap');
body {
display: flex;
height: 100%;
width: 100%;
flex-direction: column;
background-color: beige;
font-family: Asap, sans-serif;
margin: 0;
padding: 0;
justify-content: center;
align-content: center;
align-items: center;
}
.header {
display: flex;
flex: 1;
justify-content: center;
}
#setGridSize {
display: inline-flex;
justify-content: center;
flex: 1;
gap: 12px;
}
#guide {
text-align: center;
margin: 1px;
font-family: Asap, sans-serif;
color: red;
font-size: 13px;;
}
.container {
display: flex;
justify-content: center;
align-content: center;
align-items: center;
border: 1px solid black;
width: 550px;
height: 550px;
}
.gridColumn {
display: inline-flex;
border: 1px solid beige;
margin: -1px 0;
width: 30px;
height: 30px;
}
.colored{
background: red;
}
.buttons {
display: flex;
flex: 1;
gap: 20px;
margin: 10px;
}
.blue {
background: blue;
}
.eraser {
background: white;
}
.black {
background: black;
}
<!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>DOM Manipulation and Events</title>
<script src="javascript.js" defer></script>
<link rel="stylesheet" href="styles.css">
</head>
<body>
<h1 class="header"> Let's sketch ! </h1>
<div id="setGridSize">
<p> Grid size </p> <input type="text" placeholder="Size of Board" class="size-box">
<button id="submit" > Submit </button>
</div>
<p id="guide"> Enter a number between 2 to 99</p>
<div class="container"></div>
<div class="buttons">
<button class="blue"> Blue </button>
<button class="eraser" > Eraser </button>
<button class="black"> Black </button>
<button class="rainbow" > Rainbow </button>
<button class="reset" > Reset</button>
</div>
</body>
</html>
Maybe this small example will help you
const grid = document.querySelector('.grid');
const colorSelector = document.querySelector('input[type="color"]')
const COUNT = 8
for (let i = 0; i < COUNT; i++) {
for (let j = 0; j < COUNT; j++) {
const btn = document.createElement('button')
grid.appendChild(btn)
}
}
const btns = Array.from(grid.children)
btns.forEach(btn => {
btn.addEventListener('mouseenter', event => {
btn.style.backgroundColor = colorSelector.value;
})
})
#import "https://cdn.jsdelivr.net/gh/KunalTanwar/normalize/css/normalize.inter.min.css";
body {
height: 100%;
display: grid;
place-items: center;
}
.grid {
--btn-count: 8;
--btn-size: 24px;
display: grid;
grid-template-columns: repeat(var(--btn-count), var(--btn-size));
grid-template-rows: repeat(var(--btn-count), var(--btn-size));
}
.grid button {
border: 1px solid #d1d1d1;
}
<input type="color">
<div class="grid"></div>

Search filter for PokeAPI

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

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');
}

Categories