I have a todo list built with vanilla JavaScript.
As you'd expect, when you click the add button new todos appear on the DOM.
The problem is that the todo list gets pushed upwards each time you add a new todo and eventually it overwrites the navbar and leaves the viewport.
I've tried adding all the possible CSS position properties to the title but the list still keeps moving regardless
For the code and visuals - https://codepen.io/greevesh/pen/gOxNEPy
This is the element I want to prevent from moving -
<div class="d-flex justify-content-center mb-3" style="position: sticky;">
<img class="logo" src="/img/planning.svg" alt="tasktracker-logo">
</div>
You could allow the list to grow but stop moving up when reaching the top by adding margin: auto to your sub-container. See this answer for more details
.sub-container {
margin: auto;
display: block;
}
That is because you make the .container align-items: center;. Please remove that and add padding-top or margin-top as you want. Putting align-items: center; is a bad practice for element that has dynamic height.
const toDoList = document.getElementById("toDoList");
const title = document.getElementById("title");
const toDoContainer = document.getElementById("toDoContainer");
const allCheckboxes = toDoContainer.getElementsByClassName("checkbox");
const allXBtns = toDoContainer.getElementsByClassName("X");
const saveBtn = document.getElementById("save");
const clearBtn = document.getElementById("clear");
const saveMsg = document.getElementById("saveMsg");
const saveTitle = () => {
localStorage.setItem(activeEmail.innerHTML + " Title", JSON.stringify(title.value));
};
function createToDo() {
const createdToDoContainer = document.createElement("div");
createdToDoContainer.id = 'toDo' + new Date().getTime(); // unique ID
const createdCheckbox = document.createElement("INPUT");
createdCheckbox.setAttribute("type", "checkbox");
const createdToDo = document.createElement("INPUT");
const createdXBtn = document.createElement("SPAN");
createdToDoContainer.appendChild(createdCheckbox);
createdToDoContainer.appendChild(createdToDo);
createdToDoContainer.appendChild(createdXBtn);
createdToDoContainer.classList.add("toDoInnerContainer");
createdCheckbox.classList.add("checkbox");
createdToDo.classList.add("input");
createdXBtn.classList.add("X");
createdXBtn.innerHTML = "X";
toDoContainer.appendChild(createdToDoContainer);
}
let checkedToDos = [];
// delete button functionality
toDoContainer.addEventListener("click", (e) => {
const tgt = e.target;
if (tgt.classList.contains("X")) {
const parent = tgt.parentElement;
parent.remove();
const toDoValue = parent.querySelector(".input").value;
if (parent.querySelector(".checkbox").checked) {
if (checkedToDos.includes(toDoValue)) {
checkedToDos = checkedToDos.filter(val => val !== toDoValue);
}
}
}
});
const saveToDos = () => {
// change the global todo object (and make it an array)
toDos = [...document.querySelectorAll(".input")].map(toDo => {
const checked = toDo.parentNode.querySelector(".checkbox").checked;
const id = toDo.closest("div").id;
const val = toDo.value;
if (toDo.parentNode.querySelector(".checkbox").checked == true) {
checkedToDos.push(val);
}
return { id, val, checked }
});
localStorage.setItem((activeEmail.innerHTML), JSON.stringify(toDos));
};
// changes todo styling depending on checkbox state (checked or not checked)
toDoContainer.addEventListener("change", (e) => {
const tgt = e.target;
const chk = tgt.checked;
const toDo = tgt.parentNode.querySelector('.input');
toDo.style.textDecoration = chk ? "line-through" : "none";
toDo.style.opacity = chk && toDo.value !== "" ? "50%" : "100%";
});
document.getElementById("add").addEventListener("click", createToDo);
saveBtn.addEventListener("click", () => {
saveTitle();
saveToDos();
saveMsg.innerHTML = "Your tasks have been saved.";
});
// makes sure save message disappears once user clicks elsewhere
window.addEventListener("click", (e) => {
const tgt = e.target;
if (saveMsg.innerHTML !== "" && saveBtn !== tgt) {
saveMsg.innerHTML = "";
}
})
const allToDos = toDoContainer.getElementsByClassName("input");
const clearToDosAndTitle = () => {
title.value = "";
checkedToDos.splice(0, checkedToDos.length);
[...document.getElementsByClassName("toDoInnerContainer")].map(toDo => {
// remove all todos but leave at least one empty one on the DOM
// the length of the checkbox collection matters most because it's the first part of the todo to be loaded onto the DOM
while (toDo.lastChild && allCheckboxes.length > 1) {
toDo.lastChild.remove();
}
// empties the only todo that was left on the DOM after clearance
if (allToDos[0].value !== "") {
allToDos[0].value = "";
}
});
}
clearBtn.addEventListener("click", () => {
clearToDosAndTitle();
});
const loadEmptyToDoInputs = () => {
const user = JSON.parse(localStorage.getItem(activeEmail.innerHTML));
if (user && user.length > 1) {
// using a while loop instead of a foreach in this case prevents an unrequested duplicate todo being added to the DOM
while (allToDos.length != user.length) {
createToDo();
}
}
}
const loadToDos = () => {
loadEmptyToDoInputs();
loadToDoTextValues();
loadCheckedToDoStyling();
loadDefaultEmptyToDo();
}
const loadCheckedToDoStyling = () => {
const user = JSON.parse(localStorage.getItem(activeEmail.innerHTML));
user.forEach(value => {
let checkedValues = [];
if (value.checked && toDoTextValues.includes(value.val)) {
checkedValues.push(value.val);
}
toDos = [...document.getElementsByClassName("input")].map(toDo => {
if (checkedValues.includes(toDo.value)) {
box = toDo.parentElement.firstChild;
box.checked = true;
toDo.style.textDecoration = "line-through";
toDo.style.opacity = "50%";
}
}
)});
}
const loadTitle = () => {
if (localStorage.getItem(activeEmail.innerHTML + " Title")) {
title.value = JSON.parse(localStorage.getItem(activeEmail.innerHTML + " Title"));
}
}
const loadDefaultEmptyToDo = () => {
// if there are no checkboxes, there are no todos, so load an empty one by default
if (allCheckboxes.length == 0) {
createToDo();
}
}
let toDoTextValues = [];
const loadToDoTextValues = () => {
const user = JSON.parse(localStorage.getItem(activeEmail.innerHTML));
if (user) {
user.forEach(value => {
toDoTextValues.push(value.val);
});
toDos = [...document.getElementsByClassName("input")].map(toDo => {
for (let value = 0; value < toDoTextValues.length - user.length + 1; value++) {
toDo.value = toDoTextValues[value];
}
toDoTextValues.length++;
});
}}
#title {
border: none;
font-size: 45px;
text-align: center;
margin-top: 20px;
}
#title::placeholder {
text-align: center;
}
#title:focus {
text-align: center;
outline: none;
}
#title:focus::placeholder {
visibility: hidden;
}
input[type="checkbox"] {
height: 20px;
width: 20px;;
}
.input {
border-top: none;
border-right: none;
border-left: none;
margin: 0 0 25px 30px;
font-size: 30px;
display: block;
}
.input:focus {
outline: none;
}
.X {
background-color: #E60E0E;
color: white;
border-radius: 50%;
height: 30px;
width: 30px;
font-size: 25px;
font-family: 'Helvetica', 'Arial', sans-serif;
display: flex;
justify-content: center;
align-items: center;
margin: -85px 100px 0 0;
float: right;
}
.X:hover {
cursor: pointer;
background-color: #d81313;
}
#toDoBtnContainer {
margin-top: 35px;
}
#add {
color: #fff;
font-weight: 400;
background-color: #27a348;
}
#add:hover {
background-color: #21af47;
}
#save {
color: #fff;
font-weight: 400;
background-color: #04992b;
}
#save:hover {
background-color: #099b30;
}
#clear {
color: #fff;
font-weight: 400;
background-color: #cc2121;
}
#clear:hover {
background-color: #d10f0f;
}
#saveMsg {
margin-top: 15px;
color: #04992b;
font-weight: 600;
}
.container {
display: flex;
justify-content: center;
height: 100vh;
padding-top: 120px;
}
.sub-container {
display: block;
}
.logo {
height: 75px;
width: 75px;
}
.headings {
text-align: center;
margin-bottom: 30px;
}
.headings h4 {
font-weight: 300;
}
button {
height: 35px;
font-size: 20px;
}
nav {
display: flex;
padding: 20px;
background-color: blue;
}
#signOut {
margin: 0 20px 0 auto;
color: #fff;
font-weight: 400;
background-color: #E22929;
}
#signOut:hover {
background-color: #db2626;
}
#activeEmail {
margin-right: 100px;
font-weight: 500;
}
<nav class="hide">
<button type="button" class="btn btn-lg pt-0 hide" id="signOut">Sign out</button>
<p class="mt-1 hide" id="activeEmail"></p>
</nav>
<div class="container">
<div class="sub-container">
<div class="d-flex justify-content-center mb-3" style="position: sticky;">
<img class="logo" src="/img/planning.svg" alt="tasktracker-logo">
</div>
<div class="headings">
<h1>TaskTracker</h1>
<h4>Leave no task untracked.</h4>
</div>
<div class="hide" id="toDoList">
<h2><input id="title" type="text" placeholder="Add Title"></h2>
<div id="toDoContainer">
</div>
<div id="toDoBtnContainer">
<button type="button" class="btn btn-lg pt-0" id="add">Add</button>
<button type="button" class="btn btn-lg pt-0" id="save">Save</button>
<button type="button" class="btn btn-lg pt-0" id="clear">Clear everything</button>
</div>
<p id="saveMsg"></p>
</div>
</div>
</div>
Your .container is a flex with fixed height. When todo-lists overflow, the flexbox tried to keep them all centered within the container.
An easy fix would be changing to min-height: 100vh. This way the flex can grow when the lists are added.
.container {
display: flex;
justify-content: center;
align-items: center;
min-height: 100vh;
}
Related
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>
I want some help on this problem. I'm trying to get my JavaScript to highlight random picks that I enter in but its not working.
I keep getting a error message when I inspect my JS code on the webpage saying
"Uncaught TypeError: Cannot read properties of undefined (reading 'classList')
at highlightTag (script.js:52:9)
at script.js:38:9"
Here's the all the code below:
const tagsEl = document.getElementById('tags')
const textarea = document.getElementById('textarea')
textarea.focus()
textarea.addEventListener('keyup', (e) => {
createTags(e.target.value)
if (e.key === 'Enter') {
setTimeout(() => {
e.target.value = ''
}, 10)
randomSelect()
}
})
function createTags(input) {
const tags = input.split(',').filter(tag => tag.trim() !==
'').map(tag => tag.trim())
tagsEl.innerHTML = ''
tags.forEach(tag => {
const tagEl = document.createElement('span')
tagEl.classList.add('tag')
tagEl.innerText = tag
tagsEl.appendChild(tagEl)
})
}
function randomSelect() {
const times = 30
const interval = setInterval(() => {
const randomTag = pickRandomTag()
highlightTag(randomTag)
setTimeout(() => {
unHighlightTag(randomTag)
}, 100)
}, 100);
}
function pickRandomTag() {
const tags = document.querySelectorAll('.tag')
return tags[Math.floor(Math.random() * tags.length)]
}
function highlightTag(tag) {
tag.classList.add('hightlight')
}
function unHighlightTag(tag) {
tag.classList.remove('hightlight')
}
* {
box-sizing: border-box;
}
body {
background-color: #2b88f0;
font-family: 'Roboto', sans-serif;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
height: 100vh;
overflow: hidden;
margin: 0;
}
h3 {
color: #fff;
margin: 10px 0 20px;
text-align: center;
}
.container {
width: 500px;
}
textarea {
border: none;
display: block;
width: 100%;
height: 100px;
font-family: inherit;
padding: 10px;
margin: 0 0 20px;
font-size: 16px;
}
.tag {
background-color: #f0932b;
color: #fff;
border-radius: 50px;
padding: 10px 20px;
margin: 0 5px 10px 0;
font-size: 14px;
display: inline-block;
}
.tag.highlight {
background-color: #273c75;
}
<link href="https://fonts.googleapis.com/css2?family=Roboto&display=swap&ext=.css" rel="stylesheet" />
<div class="container">
<h3>Enter all of the choices divided by a comma (','). <br> Press Enter when you are done</h3>
<textarea placeholder="Enter choices here..." id="textarea"></textarea>
<div id="tags">
</div>
</div>
<script src="script.js"></script>
The problem here is you keep creating intervals. So you have an instance where you remove the element and the random code is trying to select them. It finds nothing and you have your problem. You need to cancel the intervals when you alter the tags and you should look to see if an element exists before you try to reference it if you are going to use innerHTML to make new tags.
const tagsEl = document.getElementById('tags')
const textarea = document.getElementById('textarea')
textarea.focus()
textarea.addEventListener('keyup', (e) => {
createTags(e.target.value)
if (e.key === 'Enter') {
setTimeout(() => {
e.target.value = ''
}, 10)
randomSelect()
}
})
function createTags(input) {
const tags = input.split(',').filter(tag => tag.trim() !==
'').map(tag => tag.trim())
tagsEl.innerHTML = ''
tags.forEach(tag => {
const tagEl = document.createElement('span')
tagEl.classList.add('tag')
tagEl.innerText = tag
tagsEl.appendChild(tagEl)
})
}
let selectInterval = null;
function randomSelect() {
const times = 30
// Is there an interval running? cancel it
if (selectInterval) window.clearInterval(selectInterval);
selectInterval = setInterval(() => {
const randomTag = pickRandomTag()
// Do we have something to toggle? If no, exit
if (!randomTag) return;
highlightTag(randomTag)
setTimeout(() => {
unHighlightTag(randomTag)
}, 1000)
}, 1000);
}
function pickRandomTag() {
const tags = document.querySelectorAll('.tag')
return tags[Math.floor(Math.random() * tags.length)]
}
function highlightTag(tag) {
tag?.classList?.add('hightlight')
}
function unHighlightTag(tag) {
tag?.classList?.remove('hightlight')
}
* {
box-sizing: border-box;
}
body {
background-color: #2b88f0;
font-family: 'Roboto', sans-serif;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
height: 100vh;
overflow: hidden;
margin: 0;
}
h3 {
color: #fff;
margin: 10px 0 20px;
text-align: center;
}
.container {
width: 500px;
}
textarea {
border: none;
display: block;
width: 100%;
height: 100px;
font-family: inherit;
padding: 10px;
margin: 0 0 20px;
font-size: 16px;
}
.tag {
background-color: #f0932b;
color: #fff;
border-radius: 50px;
padding: 10px 20px;
margin: 0 5px 10px 0;
font-size: 14px;
display: inline-block;
}
.tag.hightlight {
background-color: #273c75;
}
<link href="https://fonts.googleapis.com/css2?family=Roboto&display=swap&ext=.css" rel="stylesheet" />
<div class="container">
<h3>Enter all of the choices divided by a comma (','). <br> Press Enter when you are done</h3>
<textarea placeholder="Enter choices here..." id="textarea"></textarea>
<div id="tags">
</div>
</div>
<script src="script.js"></script>
The layout starts-off fine when displayed in 751px or greater but does not work fine when it starts-off in 750px or less. I thought this code below in my javascript would work but it doesn't.
// does not seem to work when page is loaded
window.addEventListener("load", () => {
window.innerWidth <= 750 ? columnLayout() : rowLayout();
});
const colorPickerEl = document.getElementById("color-picker");
const colorSchemeContainerEl = document.getElementById(
"color-scheme-container"
);
const colorModeEl = document.getElementById("color-mode");
const headerEl = document.getElementById("header");
// display default scheme
displayColorScheme(colorPickerEl.value.slice(1), "monochrome");
/*-------------
Event Listeners
---------------*/
// listen for when a new scheme is requested
document.getElementById("get-scheme-btn").addEventListener("click", () => {
displayColorScheme(colorPickerEl.value.slice(1));
});
// listen for when a randomized scheme is requested
document
.getElementById("randomize-scheme-btn")
.addEventListener("click", () => {
displayColorScheme(generateRandomColor());
});
// does not seem to work when page is loaded
window.addEventListener("load", () => {
window.innerWidth <= 750 ? columnLayout() : rowLayout();
});
window
.matchMedia("screen and (max-width: 750px)")
.addEventListener("change", (event) => {
if (event.matches) {
columnLayout();
}
});
window
.matchMedia("screen and (min-width: 751px)")
.addEventListener("change", (event) => {
if (event.matches) {
rowLayout();
}
});
function columnLayout() {
document.getElementById(
"spacer"
).style.height = `${headerEl.offsetHeight}px`;
const colorBars = document.getElementsByClassName("color-bar");
let barHeight =
(colorSchemeContainerEl.offsetHeight - headerEl.offsetHeight) / 5;
for (const bar of colorBars) {
console.log(bar);
bar.style.height = `${barHeight}px`;
}
}
function rowLayout() {
console.log("row");
const colorBars = document.getElementsByClassName("color-bar");
for (const bar of colorBars) {
bar.style.height = `${colorSchemeContainerEl.offsetHeight}px`;
}
}
// display color scheme based on user-picked color (or randomized color) and mode
function displayColorScheme(seed) {
const mode = colorModeEl.value;
// fetch the scheme using an api
fetch(`https://www.thecolorapi.com/scheme?hex=${seed}&mode=${mode}`)
// convert the data from json
.then((response) => response.json())
// manipulate the data
.then((data) => {
let html = "";
for (const color of data.colors) {
const totalRGBValue = color.rgb.r + color.rgb.g + color.rgb.b;
// 127 + 127 + 127 (the middle threshold)
const midRGBValue = 381;
const textColor =
totalRGBValue <= midRGBValue ? "white" : "black";
html += `
<div class="color-bar" style="background-color:${color.hex.value};"><p class= "text-color-bar" style="color:${textColor};">${color.hex.clean}<p></div>
`;
}
let spacer = `
<div id="spacer"></div>
`;
colorSchemeContainerEl.innerHTML = spacer + html;
});
}
// generate a random color in hex format
function generateRandomColor() {
const characters = "0123456789ABCDEF";
const maxLength = 6;
let color = "";
for (let i = 0; i < maxLength; i++) {
color += characters.charAt(
Math.floor(Math.random() * characters.length)
);
}
colorPickerEl.value = "#" + color;
return color;
}
html,
body {
margin: 0;
padding: 0;
font-family: sans-serif;
}
form {
display: flex;
justify-content: space-evenly;
}
header {
padding: 30px 0;
background-color: transparent;
position: fixed;
top: 0;
left: 0;
width: 100%;
z-index: 99;
background-color: white;
box-shadow: 0 6px 10px -4px #222;
border-bottom-left-radius: 5px;
border-bottom-right-radius: 5px;
}
input[type="button"],
select {
padding: 0 10px;
font-size: 1.05rem;
}
#color-picker {
height: 3.5em;
width: 10%;
}
p.colorName {
border: 1.5px solid rgb(70, 70, 70);
border-radius: 5px;
padding: 10px;
}
select {
width: 30%;
text-align: center;
}
.color-bar {
display: flex;
justify-content: center;
align-items: center;
}
.text-color-bar {
margin: 0;
font-size: 1.1rem;
letter-spacing: 0.1rem;
}
#color-scheme-container {
height: 100vh;
}
#media screen and (max-width: 750px) {
#color-scheme-container {
flex-direction: column;
}
#spacer {
width: 100%;
/* height: calc(60px + 3.5em - 9px); */
}
.color-bar {
width: 100%;
/* height: 17.94%; */
}
}
#media screen and (min-width: 751px) {
#color-scheme-container {
width: 100%;
display: flex;
position: relative;
}
.color-bar {
width: 20%;
}
}
<header id="header">
<form id="color-form">
<input type="color" id="color-picker" value="#008080" />
<select name="mode" id="color-mode">
<option value="monochrome">Monochrome</option>
<option value="monochrome-dark">Monochrome Dark</option>
<option value="monochrome-light">Monochrome Light</option>
<option value="analogic">Analogic</option>
<option value="complement">Complement</option>
<option value="analogic-complement">
Analogic Complement
</option>
<option value="triad">Triad</option>
<option value="quad">Quad</option>
</select>
<input id="get-scheme-btn" type="button" value="Get Color Scheme" />
<input id="randomize-scheme-btn" type="button" value="Radomize Scheme" />
</form>
</header>
<main>
<div id="color-scheme-container"></div>
</main>
Please note that I can't get the height of my header element and assign it to my spacer div, in CSS. I have to go through my JS and set the height there. Any help is greatly appreciated.
What am I doing wrong? Why isn't the addeventlistener "load" code working?
Here's a link where you can try out the issue:
https://massuhcolorschemegenerator.netlify.app/
I found the answer! All I needed to do was put my code:
window.innerWidth <= 750 ? columnLayout() : rowLayout();
at the end of my displayColorScheme function.
It worked perfectly.
I have an input field that add task. when i add the task a delete button appear next to each task
these task are stored in the local storage as an array
when i click delete button i want to delete the specific item not all the local storage
in general we use localStorage.removeItem(key);
let inputTask = document.querySelector('.input');
let tasks = document.querySelector('.tasks');
let add = document.querySelector('.add');
let alls = [];
//show tasks
function show() {
if (window.localStorage.task) {
let storedTaskes = JSON.parse(localStorage.getItem("task"));
storedTaskes.forEach((item) => {
let paragraph = document.createElement('p');
let deleteBtn = document.createElement('button');
deleteBtn.className = "delete";
deleteBtn.innerHTML = "Delete";
deleteBtn.id = item;
deleteBtn.setAttribute("onclick","deleteBtn(this)");
console.log(deleteBtn.value);
paragraph.innerHTML = item;
paragraph.appendChild(deleteBtn);
tasks.appendChild(paragraph);
});
}
}
// add tasks
add.onclick = function () {
if (inputTask.value.trim() !== "") {
alls.push(inputTask.value);
for (let i = 0; i < alls.length; i++) {
window.localStorage.setItem(`task`, JSON.stringify(alls));
}
}
inputTask.value = '';
show();
}
show();
//Delete Task
function deleteBtn(ob) {
let id = ob.id;
let taskArray = JSON.parse(localStorage.getItem("task"));
for(let i = 0; i < taskArray.length; i++){
if(taskArray[i] === id){
localStorage.removeItem(taskArray[i]);
}
}
}
body{
height: 90vh;
display: flex;
align-items: center;
justify-content: center;
}
.container {
display: flex;
align-items: center;
flex-direction: column;
gap: 20px;
}
.form {
background-color: #f0f0f0;
padding: 20px;
width: 500px;
text-align: center;
display: flex;
align-item: center;
justify-content: center;
gap: 15px;
}
.input{
border: 0;
outline: none;
padding: 10px 20px;
width: 300px;
border-radius: 5px;
}
.add,
.delete{
border: 0;
outline: none;
background-color: red;
color: white;
padding: 10px;
border-radius: 5px;
cursor: pointer;
}
.tasks {
background-color: #f0f0f0;
padding: 20px;
width: 500px;
}
p {
display: block;
background-color: white;
padding: 15px;
border-radius: 5px;
display: flex;
align-items: center;
justify-content: space-between;
}
<div class="container">
<div class="form">
<input type="text" class="input">
<input type="submit" class="add" value="Add Task">
</div>
<div class="tasks"></div>
</div>
How can i delete the specific item from the array in the local storage and show the new result for the user
You need to remove from the array and store the array again since that is how your other part works
function deleteBtn(ob) {
let id = ob.id;
let taskArray = JSON.parse(localStorage.getItem("task"));
taskArray = taskArray.filter(item => item != id)
localStorage.setItem("task", JSON.stringify(taskArray));
show()
}
ALso change
for (let i = 0; i < alls.length; i++) {
window.localStorage.setItem(`task`, JSON.stringify(alls));
}
to
localStorage.setItem(`task`, JSON.stringify(alls));
Here is a better version
I use delegation for the delete and save to localstorage without reading it again
I use a generated ID as id
Note I have to comment out the localstorage part since SO does not allow it
let inputTask = document.querySelector('.input');
let tasks = document.querySelector('.tasks');
let add = document.querySelector('.add');
let storedTasks // = localStorage.getItem("task"); // uncomment on your server
let allTasks = storedTasks ? JSON.parse(storedTasks) : [];
function show() {
tasks.innerHTML = allTasks
.map(task => `<p>${task.input} <button class="delete" data-id="${task.id}">X</button></p>`)
.join('<br/>');
}
// add tasks
add.onclick = function() {
const input = inputTask.value.trim();
if (input !== "") {
const id = allTasks.length; // use the length at this time
allTasks.push({ id:new Date().getTime(), input });
// localStorage.setItem("task",JSON.stringify(allTasks)); // uncomment on your server
inputTask.value = '';
show();
}
};
show();
document.querySelector(".tasks").addEventListener("click", function(e) {
const tgt = e.target;
if (tgt.classList.contains("delete")) {
let id = tgt.dataset.id;
allTasks = allTasks.filter(item => item.id != id)
tgt.closest("p").remove();
// localStorage.setItem("task",JSON.stringify(allTasks)); // uncomment on your server
}
});
body {
height: 90vh;
display: flex;
align-items: center;
justify-content: center;
}
.container {
display: flex;
align-items: center;
flex-direction: column;
gap: 20px;
}
.form {
background-color: #f0f0f0;
padding: 20px;
width: 500px;
text-align: center;
display: flex;
align-item: center;
justify-content: center;
gap: 15px;
}
.input {
border: 0;
outline: none;
padding: 10px 20px;
width: 300px;
border-radius: 5px;
}
.add,
.delete {
border: 0;
outline: none;
background-color: red;
color: white;
padding: 10px;
border-radius: 5px;
cursor: pointer;
}
.tasks {
background-color: #f0f0f0;
padding: 20px;
width: 500px;
}
p {
display: block;
background-color: white;
padding: 15px;
border-radius: 5px;
display: flex;
align-items: center;
justify-content: space-between;
}
<div class="container">
<div class="form">
<input type="text" class="input">
<input type="submit" class="add" value="Add Task">
</div>
<div class="tasks"></div>
</div>
function deleteBtn({id}) {
let taskArray = JSON.parse(localStorage.getItem("task")); // get the array
taskArray = taskArray.filter(ob=> ob.id != id ) // remove the id
localStorage.setItem("task", JSON.stringify(taskArray));
show()
}
but since you are using tasks.appendChild(paragraph)
you will have to change show function to
//show tasks
function show() {
if (window.localStorage.task) {
let storedTaskes = JSON.parse(localStorage.getItem("task"));
tasks.innerHTML = "" // here
storedTaskes.forEach((item) => {
let paragraph = document.createElement('p');
let deleteBtn = document.createElement('button');
deleteBtn.className = "delete";
deleteBtn.innerHTML = "Delete";
deleteBtn.id = item;
deleteBtn.setAttribute("onclick","deleteBtn(this)");
console.log(deleteBtn.value);
paragraph.innerHTML = item;
paragraph.appendChild(deleteBtn);
tasks.appendChild(paragraph)
});
}
}
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>