I Cannot drop generated elements - javascript

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>

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>

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

Remove item and its data from localStorage when I click an icon

I'm making a To-Do List and trying to remove an item when I click a trash bin icon
image
not only the item but also its data from local storage.
However, when I click the icon, only one data is removed.
If I remove another 'li' tag, I have to refresh my page and then click it.
I want to remove items and data, do not refresh it
What is the problem with my page?
Thank you.
HTML
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>TO DO LIST</title>
<link rel="stylesheet" href="style.css" />
<link
rel="stylesheet"
href="https://use.fontawesome.com/releases/v5.13.1/css/all.css"
integrity="sha384-xxzQGERXS00kBmZW/6qxqJPyxW3UR0BPsL4c8ILaIWXva5kFi7TxkIIaMiKtqV1Q"
crossorigin="anonymous"
/>
<script src="main.js" defer></script>
<!-- <script src="data_storage.js" defer></script> -->
</head>
<body>
<section class="container">
<h1>TO DO LIST</h1>
<ul></ul>
<div class="footer">
<input type="text" placeholder="Title..." />
<button class="enter">Enter</button>
</div>
</section>
</body>
</html>
CSS
* {
font-family: Arial, Helvetica, sans-serif;
}
body {
background-color: #ecf0f1;
}
.container {
width: 50%;
height: 100%;
margin: auto;
border: 1px solid green;
border-radius: 15px 15px 0 0;
}
h1 {
margin: 10px 20px;
padding-bottom: 15px;
text-align: center;
font-size: 42px;
color: #98e3a1;
border-bottom: 1px dotted #5e7361;
}
ul {
font-size: 24px;
padding-bottom: 10px;
list-style-type: none;
}
li {
position: relative;
padding-bottom: 8px;
margin-bottom: 3px;
margin-right: 20px;
border-bottom: 1px solid grey;
}
.footer {
display: block;
position: relative;
}
input {
position: relative;
width: 93%;
padding: 10px 0;
border: none;
outline: none;
}
.enter {
position: absolute;
padding: 0;
width: 7%;
height: 100%;
outline: none;
background-color: greenyellow;
border: none;
color: grey;
}
.fas {
font-size: 20px;
position: absolute;
right: 10px;
top: 10px;
cursor: pointer;
transition: transform 200ms ease-in;
}
.fas:hover {
color: red;
transform: scale(1.1);
}
JavaScript
const ul = document.querySelector("ul");
const input = document.querySelector("input");
const enterBtn = document.querySelector(".enter");
const LIST_LS = "lists";
function filterFn(toDo) {
return toDo.id === 1;
}
let lists = [];
function saveStorage() {
localStorage.setItem(LIST_LS, JSON.stringify(lists));
}
function deleteStorage(event) {
const trashBtn = event.target;
const li = trashBtn.parentNode;
ul.removeChild(li);
const cleanStorage = lists.filter((toDo) => {
return toDo.id !== parseInt(li.id);
});
lists = cleanStorage;
saveStorage();
}
function loadStorage() {
const loadStorage = localStorage.getItem(LIST_LS);
if (loadStorage !== null) {
const parsedList = JSON.parse(loadStorage);
parsedList.forEach((list) => {
createItem(list.text);
});
}
}
function onAdd() {
const text = input.value;
if (text === "") {
input.focus();
return;
}
createItem(text);
input.value = "";
input.focus();
}
function createItem(text) {
const itemRow = document.createElement("li");
const newId = lists.length + 1;
itemRow.setAttribute("class", "item__row");
itemRow.innerHTML = `${text} <i class="fas fa-trash-alt" data-id=${itemRow.id}></i>`;
ul.appendChild(itemRow);
itemRow.id = newId;
const delBtn = document.querySelector(".fa-trash-alt");
delBtn.addEventListener("click", deleteStorage);
const listObj = {
text: text,
id: newId,
};
lists.push(listObj);
saveStorage();
return itemRow;
}
loadStorage();
enterBtn.addEventListener("click", () => {
onAdd();
});
input.addEventListener("keypress", (event) => {
if (event.key === "Enter") onAdd();
});
Your problem is this line:
const delBtn = document.querySelector(".fa-trash-alt");
This means it will always select the first trash icon.
You have to write:
const delBtn = itemRow.querySelector(".fa-trash-alt");
Working example here:
https://codesandbox.io/s/damp-morning-csiny

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

Can't access JavaScript method in different "class"

So I'm making a calculator in JavaScript as a way to learn JavaScript. I'd like to add some sort of Object Orientated into the project.
My 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>The Unconventional Calculator</title>
<link
href="https://fonts.googleapis.com/css?family=Roboto:400,700&display=swap"
rel="stylesheet"
/>
<link rel="stylesheet" href="assets/styles/app.css" />
</head>
<body>
<header>
<h1>The Unconventional Calculator</h1>
</header>
<section id="calculator">
<input type="number" id="input-number" />
<div id="calc-actions">
<button type="button" id="btn-add">+</button>
<button type="button" id="btn-subtract">-</button>
<button type="button" id="btn-multiply">*</button>
<button type="button" id="btn-divide">/</button>
<button type="button" id="btn-equals">=</button>
</div>
</section>
<section id="results">
<h2 id="current-calculation">0</h2>
<h2>Result: <span id="current-result">0</span></h2>
</section>
<section class="credit">
<h1>Check out my code on GitHub
<br>Type your number, press enter, repeat until you're done and then press enter.</h1>
</section>
<!-- So the site loads first then the js runs -->
<script src="assets/scripts/vendor.js"></script>
<script src="assets/scripts/app.js"> </script>
</body>
</html>
MY CSS
* {
box-sizing: border-box;
}
html {
font-family: 'Roboto', open-sans;
}
body {
margin: 0;
}
header {
background: #6d0026;
color: white;
padding: 1rem;
text-align: center;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.26);
width: 100%;
}
#results,
#calculator {
margin: 2rem auto;
width: 40rem;
max-width: 90%;
border: 1px solid #6d0026;
border-radius: 10px;
padding: 1rem;
color: #6d0026;
}
#results {
text-align: center;
}
#calculator input {
font: inherit;
font-size: 3rem;
border: 2px solid #6d0026;
width: 10rem;
padding: 0.15rem;
margin: auto;
display: block;
color: #6d0026;
text-align: center;
}
#calculator input:focus {
outline: none;
}
#calculator button {
font: inherit;
background: #6d0026;
color: white;
border: 1px solid #6d0026;
padding: 1rem;
cursor: pointer;
}
#calculator button:focus {
outline: none;
}
#calculator button:hover,
#calculator button:active {
background: #6d2d1b;
border-color: #6d2d1b;
}
#calc-actions button {
width: 4rem;
}
#calc-actions {
margin-top: 1rem;
text-align: center;
}
.credit {
margin: 70px 0 0 0;
text-align: center;
}
app.js
// Base Variables
let result = 0;
let number = 0;
//Store equation as string
let calculationDescription = "";
//Event listeners
document.querySelector("#btn-add").addEventListener('click', sumNumbs);
document.querySelector("#btn-subtract").addEventListener('click', subtractNumbs);
document.querySelector("#btn-multiply").addEventListener('click', multiplyNumbs);
document.querySelector("#btn-divide").addEventListener('click', divideNumbs);
document.querySelector("#btn-equals").addEventListener('click', equals);
document.querySelector('#input-number').addEventListener('keypress', numbersInput);
function numbersInput(e) {
if(e.key === 'Enter' && userInput !== null) {
number = e.target.value;
e.target.value = '';
calculationDescription += number + " ";
console.log(calculationDescription);
}
}
function sumNumbs() {
calculationDescription += "+ ";
}
function subtractNumbs() {
calculationDescription += "- ";
}
function multiplyNumbs() {
calculationDescription += "x ";
}
function divideNumbs() {
calculationDescription += "/ ";
}
function equals() {
let finalCalculation = calculationDescription.split(" ");
//Goes to errorHandler to remove whitespace and make array ready for equation
errorHandler.removeWhiteSpace(finalCalculation);
}
errorHandler.js
class errorHandler {
static removeWhiteSpace(arraySplit) {
return console.log(arraySplit);
}
}
vendor.js(this one isn't to important for the solution)
const userInput = document.getElementById('input-number');
const addBtn = document.getElementById('btn-add');
const subtractBtn = document.getElementById('btn-subtract');
const multiplyBtn = document.getElementById('btn-multiply');
const divideBtn = document.getElementById('btn-divide');
const equalsBtn = document.getElementById('btn-equals');
const currentResultOutput = document.getElementById('current-result');
const currentCalculationOutput = document.getElementById('current-calculation');
function outputResult(result, text) {
currentResultOutput.textContent = result;
currentCalculationOutput.textContent = text;
}
So in my equals method I'd like to send the array finalCalculation which is in the app.js class into the removeWhiteSpace method that's in my errorHandler.js
This is the error I keep getting
app.js:44 Uncaught ReferenceError: errorHandler is not defined
at HTMLButtonElement.equals (app.js:44)
I've tried turning both of them into classes and then creating a constructor with an instance variable for errorHandler to take in the array, but that doesn't seem to work.

Categories