Home > Back-end >  How to use javascript to display a loading animation before the api has fully loaded the data?
How to use javascript to display a loading animation before the api has fully loaded the data?

Time:02-05

How to make the API data load the load animation first before all the data is returned, and then open the popup after the API has passed all the data in? Because currently when the load disappears, the data in the popup has not been fully loaded, which will cause the user to see the moment when the data is loaded and the size of the popup will change. I hope that the load animation will be displayed before all the data is uploaded to the popup , After the data is loaded, open the popup and close the loading animation. How should I modify this to make my user experience better? Thanks

let btn = document.querySelector('.btn');
let popup = document.querySelector('.popup');
let wrap = document.querySelector('.wrap');
let photo = document.querySelector('.photo');
let svg = document.querySelector('svg');

btn.addEventListener('click', function(e) {
  svg.style.display = "block"

  axios.get("https://dog.ceo/api/breeds/image/random")
    .then((response) => {
      let Data = response.data.message;
      photo.src = Data;
      svg.style.display = "none"
      popup.style.display = "flex";
    })
    .catch((error) => console.log(error));

});

wrap.addEventListener('click', function(e) {
  e.stopPropagation();
});

popup.addEventListener('click', function() {
  popup.style.display = "none";
});
.popup {
  background-color: rgba(0, 0, 0, 0.5);
  width: 100%;
  height: 100vh;
  display: none;
  justify-content: center;
  align-items: center;
  position: absolute;
  top: 0;
  right: 0;
  bottom: 0;
  left: 0;
}

.popup .wrap {
  width: 300px;
  background-color: #fff;
  text-align: center;
  border-radius: 20px;
  padding: 20px;
}

.popup .wrap .photo {
  width: 100%;
  height: 100%;
  object-fit: conver;
  border-radius: 20px;
}

svg {
  display: none;
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
}

svg .load {
  stroke-dasharray: 0 500;
  animation: rot 1s linear infinite;
}

@keyframes rot {
  100% {
    stroke-dasharray: 500 500;
  }
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/axios/1.3.2/axios.min.js"></script>
<button class='btn'>open data</button>


<div >
  <div >
    <h1>Pet</h1>
    <img  src="" alt="">
  </div>
</div>

<svg width="240px" height="240px" version="1.1" xmlns="http://www.w3.org/2000/svg">
        <circle cx="110" cy="110" r="90" stroke-width="10" stroke="gainsboro" fill="none"></circle>
        <circle cx="110" cy="110" r="90" stroke-width="10" stroke="darkturquoise" fill="none" ></circle>
    </svg>

CodePudding user response:

Your code looks good, but you are missing one little piece. The tricky part is that image loading also takes time, and you want to show a loading indicator while the image itself is loading.

Based on the above, you need to wait for the image to load fully and only then show the final view.

Here is the change that you need to make:

btn.addEventListener('click', function(e) {
  svg.style.display = "block"

  axios.get("https://dog.ceo/api/breeds/image/random")
    .then((response) => {
      // set handler to show the image once it is loaded
      photo.addEventListener('load', () => {
        svg.style.display = "none"
        popup.style.display = "flex";
      });
      // set image src after adding 'load' handler
      photo.src = response.data.message;
    })
    .catch((error) => console.log(error));

});

Update based on additional questions in comments

To handle multiple images, you will need a bit more complex approach. Since your code was just an example for a single image, for simplicity and demonstration's sake, based on your question in the comments, I'm assuming that your server can return an array of image URLs.

  btn.addEventListener('click', function(e) {
  svg.style.display = "block"

  axios.get("https://dog.ceo/api/breeds/image/random")
    .then((response) => {
      // assuming your API returns tens of images
      // this is an array of image URLs
      const imageURLs = response.message.imageURLs;
      // retrieving array of img elements from the page
      // I would recommend actually generating img tags here
      const imageElements = document.querySelector('.photo');
      // we create a Promise for each img element
      const imagePromises = imageElements.map((element, index) => {
        return new Promise((resolve, reject) => {
         element.addEventHandler('load', resolve);
         element.addEventHandler('error', (e) => reject(e));
         element.src = imageURLs[index];
       });
      });
      // this will wait for all the promises to settle
      Promise.allSettled(imagePromises).then((results) => {
        // go through 'results' and handle errors if needed
        // otherwise, this is the place where you know that
        // all the images are loaded/failed
        svg.style.display = "none"
        popup.style.display = "flex";
      });
    })
    .catch((error) => console.log(error));
});

Regarding what you should do in the catch, it largely depends on the behavior and visual feedback you need to make in the case of errors. If you want to notify a user about an error, you can use catch to show some popup, for example.

Please let me know if this helps.

CodePudding user response:

To solve that problem, you can try adding the onload property to the photo variable. The onload event occurs when an object has been loaded. Reference: https://www.w3schools.com/jsref/event_onload.asp

let btn = document.querySelector('.btn');
let popup = document.querySelector('.popup');
let wrap = document.querySelector('.wrap');
let photo = document.querySelector('.photo');
let svg = document.querySelector('svg');

btn.addEventListener('click', function(e) {
  svg.style.display = "block"

  axios.get("https://dog.ceo/api/breeds/image/random")
    .then((response) => {
      let Data = response.data.message;
      photo.src = Data;
      photo.onload = () => {
        svg.style.display = "none";
        popup.style.display = "flex";
      }
    })
    .catch((error) => console.log(error));

});

wrap.addEventListener('click', function(e) {
  e.stopPropagation();
});

popup.addEventListener('click', function() {
  popup.style.display = "none";
});
.popup {
  background-color: rgba(0, 0, 0, 0.5);
  width: 100%;
  height: 100vh;
  display: none;
  justify-content: center;
  align-items: center;
  position: absolute;
  top: 0;
  right: 0;
  bottom: 0;
  left: 0;
}

.popup .wrap {
  width: 300px;
  background-color: #fff;
  text-align: center;
  border-radius: 20px;
  padding: 20px;
}

.popup .wrap .photo {
  width: 100%;
  height: 100%;
  object-fit: conver;
  border-radius: 20px;
}

svg {
  display: none;
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
}

svg .load {
  stroke-dasharray: 0 500;
  animation: rot 1s linear infinite;
}

@keyframes rot {
  100% {
    stroke-dasharray: 500 500;
  }
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/axios/1.3.2/axios.min.js"></script>
<button class='btn'>open data</button>


<div >
  <div >
    <h1>Pet</h1>
    <img  src="" alt="">
  </div>
</div>

<svg width="240px" height="240px" version="1.1" xmlns="http://www.w3.org/2000/svg">
        <circle cx="110" cy="110" r="90" stroke-width="10" stroke="gainsboro" fill="none"></circle>
        <circle cx="110" cy="110" r="90" stroke-width="10" stroke="darkturquoise" fill="none" ></circle>
    </svg>

  • Related