Reputation: 123
So I'm making a simple pokemon app where the front-end makes fetch calls to grab the Pokemon from PokeAPI and displays it, but when loading a pokemon you can see that the fetch info gets loaded at different rates. For example the pokemon loads then the name loads then the background loads then the type loads. Is there anyway to have it so the HTML fills all at one time?
This is what my Javascript fetch function looks like, additionally here is the app so you can see how the information loads slowly / not all at once : https://bui-pokemon.herokuapp.com/
pokeButton.addEventListener('click', (e)=> {
e.preventDefault();
const pokeNameSearch = pokeSearch.value.toLowerCase();
fetch(`https://pokeapi.co/api/v2/pokemon/${pokeNameSearch}`)
.then((response) => response.json())
.then(data => {
if(pokeCard.classList.value === 'poke_card'){
pokeCard.classList.add('border');
};
//If a pokemon was normal / flying type then flying should be shown in the background isntead of normal
//because flying type is more of a defining characteristic of that pokemon rather than normal
if(data.types.length > 1 && data.types[0].type.name === "normal" && data.types[1].type.name === "flying"){
pokeCard.className = `poke_card border ${data.types[1].type.name}_background`
} else {
pokeCard.className = `poke_card border ${data.types[0].type.name}_background`;
}
pokeImg.src = data.sprites.front_default;
pokeName.innerHTML = data.name;
// Fill in Pokemon Type
pokeTypeIcon1.src = "";
pokeTypeIcon2.src = "";
pokeTypeIcon1.className = '';
pokeTypeIcon2.className = '';
pokeTypeIcon2.style.display = "none";
pokeType.innerHTML = `${data.types[0].type.name}`;
pokeNumDisplay.innerHTML = `#${fillInZero(data.id.toString())}`
pokeTypeIcon1.src = `img/icons/${data.types[0].type.name}.svg`
pokeTypeIcon1.className = `type_icon_1 ${data.types[0].type.name}`
if(data.types.length > 1){
pokeType.innerHTML += `/${data.types[1].type.name}`
pokeTypeIcon2.src = `img/icons/${data.types[1].type.name}.svg`
pokeTypeIcon2.style.display = "inline-block";
pokeTypeIcon2.className = `type_icon_2 ${data.types[1].type.name}`
} else {
pokeType.innerHTML = `${data.types[0].type.name}`
}
})
Upvotes: 0
Views: 125
Reputation: 8378
Seems the font and images loading causes the issue.
What you could do, is listen for the load
event on each image and transition the opacity. Then, use document.fonts.ready
and transition the color:
const container = document.querySelector(".container");
const background = document.getElementById("background")
const imgOne = document.getElementById("img-one")
const imgTwo = document.getElementById("img-two")
const fadeIn = (el) => {
el.addEventListener("load", () => el.style.opacity = "1")
}
document.fonts.ready.then(() => container.style.color = "#fff")
fadeIn(background)
fadeIn(imgOne)
fadeIn(imgTwo)
background.src = "https://ak.picdn.net/shutterstock/videos/9589454/thumb/2.jpg"
imgOne.src = "https://toppng.com/public/uploads/thumbnail/ikachu-8-bits-8-bit-pokemon-grid-11563233054e4sqfqyl2l.png"
imgTwo.src = "https://icons.iconarchive.com/icons/paomedia/small-n-flat/1024/lightning-icon.png"
@import url('https://fonts.googleapis.com/css2?family=Press+Start+2P&display=swap');
body {
background: #43464b;
font-family: "Press Start 2P", Arial, Helvetica, sans-serif;
}
.container {
display: flex;
align-items: center;
justify-content: center;
flex-direction: column;
position: relative;
margin: 0 auto;
width: 200px;
height: 300px;
color: transparent;
overflow: hidden;
transition: color 0.25s;
}
.container>*:not(#background) {
z-index: 1;
}
img {
opacity: 0;
transition: opacity 0.25s;
}
#background {
position: absolute;
top: 0;
left: 0;
}
#img-one,
#img-two {
height: 80px;
}
<div class="container">
<img id="background" />
<img id="img-one" />
<p>#25 Pikachu</p>
<img id="img-two" />
<p>Electric</p>
</div>
However, it could still be uneven this way if one thing takes longer to load than the other.
Another solution is to push values to an array once loaded, and check the length of the array before setting the opacity of the container:
const container = document.querySelector(".container");
const background = document.getElementById("background")
const imgOne = document.getElementById("img-one")
const imgTwo = document.getElementById("img-two")
const loaded = []
const setLoadState = (el) => {
loaded.push(el)
if (loaded.length === 4)
container.style.opacity = "1"
}
const imgLoad = (img) => {
img.addEventListener("load", () => setLoadState(img.id))
}
document.fonts.ready.then(() => setLoadState('font'))
imgLoad(background)
imgLoad(imgOne)
imgLoad(imgTwo)
background.src = "https://ak.picdn.net/shutterstock/videos/9589454/thumb/2.jpg"
imgOne.src = "https://toppng.com/public/uploads/thumbnail/ikachu-8-bits-8-bit-pokemon-grid-11563233054e4sqfqyl2l.png"
imgTwo.src = "https://icons.iconarchive.com/icons/paomedia/small-n-flat/1024/lightning-icon.png"
@import url('https://fonts.googleapis.com/css2?family=Press+Start+2P&display=swap');
body {
background: #43464b;
font-family: "Press Start 2P", Arial, Helvetica, sans-serif;
}
.container {
display: flex;
align-items: center;
justify-content: center;
flex-direction: column;
position: relative;
margin: 0 auto;
width: 200px;
height: 300px;
color: #fff;
overflow: hidden;
opacity: 0;
transition: opacity 0.25s;
}
.container>*:not(#background) {
z-index: 1;
}
#background {
position: absolute;
top: 0;
left: 0;
}
#img-one,
#img-two {
height: 80px;
}
<div class="container">
<img id="background" />
<img id="img-one" />
<p>#25 Pikachu</p>
<img id="img-two" />
<p>Electric</p>
</div>
Upvotes: 1