1

I'm having a problem with showing an overlay div element and then hiding it again after the runSearch() function has completed. In short, the overlay does not appear at all.

What the overlay should look like if it worked: What the overlay should look like if it worked:

If I had to guess, I believe it could relate to a misunderstanding about how to implement async/await correctly in javascript.

Since I am limited for space here, the full Github project is accessible as a fully deployed page here, if you need more context. However, the most relevant excerpts are below:

The overlay div element in index.html:

  <div class="overlay d-flex justify-content-center align-items-center">
    <h5>Please wait...</h5>
    <div class="spinner-border" role="status">
      <span class="visually-hidden">Loading...</span>
    </div>
  </div>

The overlay in CSS:

  .overlay {
    background-color:#EFEFEF;
    position: fixed;
    width: 100%;
    height: 100%;
    z-index: 1000;
    left: 0px;
    display: none!important;
    /* without !important, the overlay would immediately kick into effect */
 }

The JS functions which show and hide the overlay when called upon:

 function loadingOverlayOn() {
    document
      .getElementsByClassName("overlay")[0]
      .style.display = 'block'
  }
    function loadingOverlayOff() {
    document
      .getElementsByClassName("overlay")[0]
      .style.display = 'none'
  }

JS with respect to button #1:

cityInstanceBtn.addEventListener('click',async function(e){

    // for use in headings inside runSearch
    // reset
    globalCityName === null;
    globalCityState === null;
    globalCityCountry === null;

    globalCityName = e.target.dataset.city
    globalCityState = e.target.dataset.state
    globalCityCountry = e.target.dataset.country
    
    loadingOverlayOn();
    await runSearch(cityName, cityState, cityCountry, cityLat, cityLng, units)
    loadingOverlayOff();

})

JS with respect to button #2, which occurs inside of a temporarily displayed Bootstrap modal:

cityInstanceBtn.addEventListener('click', async function(){

            myModal.hide()

        globalCityName = document.getElementById(id).dataset.city
        globalCityState = document.getElementById(id).dataset.state
        globalCityCountry = document.getElementById(id).dataset.country

        loadingOverlayOn();
        await runSearch(cityName, cityState, cityCountry, cityLat, cityLng, units)
        loadingOverlayOff();
 
            
        })

The JS function during which the overlay should be shown, and hidden once its execution is complete:

    async function runSearch(
  cityName,
  cityState,
  country,
  cityLat,
  cityLng,
  detectedUnits
) {
  console.log("check cityState: " + cityState);
  console.log("check globalCityState: " + globalCityState);
  var h2Today = document.getElementById("today-title");
  var h2Next5Days = document.getElementById("next-5-days-title");

  if (globalCityState != "undefined" && globalCityName && globalCityCountry) {
    h2Today.innerHTML = `<span class="orange">Today's</span> forecast for <span class="cornflowerblue">${globalCityName}, ${globalCityState}, ${globalCityCountry}</span>`;
    h2Next5Days.innerHTML = `<span class="orange">4-day</span> outlook for <span class="cornflowerblue">${globalCityName}, ${globalCityState}, ${globalCityCountry}</span>`;
  } else if (
    (globalCityState = "undefined" && globalCityName && globalCityCountry)
  ) {
    h2Today.innerHTML = `<span class="orange">Today's</span> forecast for <span class="cornflowerblue">${globalCityName},${globalCityCountry}</span>`;
    h2Next5Days.innerHTML = `<span class="orange">4-day</span> outlook for <span class="cornflowerblue">${globalCityName}, ${globalCityCountry}</span>`;
  }

  var newSearchObject = {
    cityName: cityName,
    cityState: cityState,
    cityCountry: country,
    cityLat: cityLat,
    cityLng: cityLng,
    detectedUnits: detectedUnits,
  };

  var retrievedLocalStorage = localStorage.getItem("savedCities");
  retrievedLocalStorage = JSON.parse(retrievedLocalStorage);
  // const arr = retrievedLocalStorage.map(a => {a.cityLat, a.cityLng})

  if (retrievedLocalStorage === null) {
    localStorage.setItem("savedCities", JSON.stringify([newSearchObject]));

    generatePrevCitiesList();
  } else if (
    retrievedLocalStorage.length > 0 &&
    retrievedLocalStorage.length < 5
  ) {
    retrievedLocalStorage.reverse();

    if (
      !retrievedLocalStorage.some((s) => {
        return (
          s.cityLat == newSearchObject.cityLat &&
          s.cityLng == newSearchObject.cityLng
        );
      })
    ) {
      // Check if an array of objects contains another object: https://stackoverflow.com/a/63336477/9095603
      // this solution which converts objects to string first isn't entirely reliable if you can't guarantee the same order is preserved, for example: https://stackoverflow.com/a/201305/9095603

      retrievedLocalStorage.push(newSearchObject);

      retrievedLocalStorage.reverse();
      console.log("existingSearchObject2: " + retrievedLocalStorage);
      localStorage.setItem(
        "savedCities",
        JSON.stringify(retrievedLocalStorage)
      );
    }

    generatePrevCitiesList();
  } else if (retrievedLocalStorage.length >= 5) {
    retrievedLocalStorage.reverse();

    if (
      !retrievedLocalStorage.some((s) => {
        return (
          s.cityLat == newSearchObject.cityLat &&
          s.cityLng == newSearchObject.cityLng
        );
      })
    ) {
      retrievedLocalStorage.push(newSearchObject);
    }

    while (retrievedLocalStorage.length > 5) {
      retrievedLocalStorage.shift();
    }

    retrievedLocalStorage.reverse();
    localStorage.setItem("savedCities", JSON.stringify(retrievedLocalStorage));

    generatePrevCitiesList();
  }

  fetch(
    `https://api.openweathermap.org/data/2.5/forecast?lat=${cityLat}&lon=${cityLng}&units=${detectedUnits}&appid=${apiKey}`
  )
    .then((response) => response.json())
    .then((data) => {
      console.log(data);
      console.table(data.list);
      console.log(JSON.stringify(data));

      var timezone = data.city.timezone;
      console.log({ timezone });
      var country = data.city.country;
      console.log({ country });
      var cityName = data.city.name;
      console.log({ cityName });

      var datesArray = [];
      console.log({ datesArray });

      const days = [
        "Sunday",
        "Monday",
        "Tuesday",
        "Wednesday",
        "Thursday",
        "Friday",
        "Saturday",
      ];
      // var h2Today = document.getElementById('today-title')

      //     h2Today.innerHTML = `<span class="orange">Today's</span> forecast for <span class="cornflowerblue">${globalCityName}, ${globalCityState}, ${globalCityCountry}</span>`
      // }
      //     h2Today.innerHTML = `<span class="orange">Today's</span> forecast for <span class="cornflowerblue">${globalCityName},${globalCityCountry}</span>`
      // }

      for (let i = 0; i < data.list.length; i++) {
        var unixTimestamp = data.list[i].dt;
        console.log(data.list[i].dt);
        // you don't need it for dt_txt but if you want to use the unix timestamp in the data, you can do this conversion:

        var jsTimestamp = unixTimestamp * 1000;
        var date = new Date(jsTimestamp);
        var basicDateLocalAU = date.toLocaleDateString("en-AU");
        var basicDateLocalUS = date.toLocaleDateString("en-US");
        var basicDateLocalUser = date.toLocaleDateString(`en-${country}`);

        console.log(basicDateLocalAU); // Prints: 5/6/2022
        console.log(basicDateLocalUS); // Prints: 6/5/2022
        console.log(basicDateLocalUser); // Prints: 6/5/2022

        var timeLocalAU = date.toLocaleTimeString("en-AU", {
          hour: "2-digit",
          minute: "2-digit",
        }); // Prints: 13:10:34
        // https://stackoverflow.com/a/20430558/9095603
        // https://bobbyhadz.com/blog/javascript-typeerror-date-getday-is-not-a-function#:~:text=getDay%20is%20not%20a%20function%22%20error%20occurs%20when%20the%20getDay,method%20on%20valid%20date%20objects.

        data.list[i].basicDateLocalAU = basicDateLocalAU;
        data.list[i].basicDateLocalUS = basicDateLocalUS;
        data.list[i].basicDateLocalUser = basicDateLocalUser;
        data.list[i].dayOfWeekIndex = date.getDay();
        data.list[i].dayOfWeekValue = days[date.getDay()];
        data.list[i].basicTime = timeLocalAU;

        // https://bobbyhadz.com/blog/javascript-array-push-if-not-exist

        if (!datesArray.includes(basicDateLocalUser)) {
          datesArray.push(basicDateLocalUser);

          var dayOfWeek = days[date.getDay()];
          console.log(dayOfWeek);
        }
      }

      console.log({ date });

      console.log({ data });

      var datalist = data.list;
      console.log({ datalist });

      var obj = groupBy(datalist, "basicDateLocalAU");
      console.log({ obj });
      // const result = data.list.group(({ basicCalendarDateAU }) => basicCalendarDateAU);

      for (let i = 0; i < obj.length; i++) {
        var dayTableEle = document.querySelector(`#day${i} table`);
        // var textNode = document.createTextNode(`${dayOfWeekValue}`);

        dayTableEle.innerHTML = `<row><th>Time</th><th>Temp</th><th></th><th>Conditions</th><th>Humidity</th><th>Wind speed</th></row>`;
        for (let j = 0; j < obj[i].length; j++) {
          console.log(obj[i].length);
          if (!document.querySelector(`#day${i} h5`).innerText) {
            document.querySelector(
              `#day${i} h5`
            ).innerText = `${obj[i][j].dayOfWeekValue}`;
          }
          if (
            !document.querySelector(`#day${i} span#usercountry-dateformat`)
              .innerText
          ) {
            document.querySelector(
              `#day${i} span#usercountry-dateformat`
            ).innerText = `${obj[i][j].basicDateLocalUser}`;
          }
          if (
            !document.querySelector(`#day${i} span#AU-dateformat`).innerText
          ) {
            document.querySelector(
              `#day${i} span#AU-dateformat`
            ).innerText = `${obj[i][j].basicDateLocalAU}`;
            document
              .querySelector(`#day${i} span#AU-dateformat`)
              .style.setProperty("display", "none");
          }
          if (
            !document.querySelector(`#day${i} span#US-dateformat`).innerText
          ) {
            document.querySelector(
              `#day${i} span#US-dateformat`
            ).innerText = `${obj[i][j].basicDateLocalUS}`;
            document
              .querySelector(`#day${i} span#US-dateformat`)
              .style.setProperty("display", "none");
          }
          // var kelvinToCelcius = obj[i][j].main.temp - 273.15;

          var tempMetric;
          var tempImperial;

          var windSpeedImperial;
          var windSpeedMetric;

          if (units == "metric") {
            var tempMetric = obj[i][j].main.temp;
            tempMetric = roundedToFixed(tempMetric, 1);
            var tempImperial = tempMetric * 1.8 + 32;
            tempImperial = roundedToFixed(tempImperial, 1);

            var windSpeedMetric = obj[i][j].wind.speed;
            windSpeedMetric = roundedToFixed(windSpeedMetric, 1);
            var windSpeedImperial = windSpeedMetric * 2.23694;
            windSpeedImperial = roundedToFixed(windSpeedImperial, 1);

            var metricDisplay = "inline";
            var imperialDisplay = "none";
          } else if (units == "imperial") {
            var tempImperial = obj[i][j].main.temp;
            tempImperial = roundedToFixed(tempImperial, 1);
            var tempMetric = (tempImperial - 32) / 1.8;
            tempMetric = roundedToFixed(tempMetric, 1);

            var windSpeedImperial = obj[i][j].wind.speed;
            windSpeedImperial = roundedToFixed(windSpeedImperial, 1);
            var windSpeedMetric = windSpeedImperial / 2.23694;
            windSpeedMetric = roundedToFixed(windSpeedMetric, 1);

            var metricDisplay = "none";
            var imperialDisplay = "inline";
          }

          dayTableEle.innerHTML += `
            <row>
                <td id="tdTime">${obj[i][j].basicTime}</td>
                <td id="tdTemp">
                    <span class="temp-metric metric" style="display:${metricDisplay};">${tempMetric} ${tempUnitsMetric}</span>
                    <span class="temp-imperial imperial" style="display:${imperialDisplay};">${tempImperial} ${tempUnitsImperial}</span>
                </td>
                <td><img src="https://openweathermap.org/img/wn/${obj[i][j].weather[0].icon}.png" alt="weather icon"></td>
                <td id="tdConditions">${obj[i][j].weather[0].description}</td>
                <td id="tdHumidity">${obj[i][j].main.humidity} %</td>
                <td id="tdWindSpeed">
                    <span class="windspeed-metric metric" style="display:${metricDisplay};">${windSpeedMetric} ${windSpeedUnitsMetric}</span>
                    <span class="windspeed-imperial imperial" style="display:${imperialDisplay};">${windSpeedImperial} ${windSpeedUnitsImperial}</span>
                </td>
                <td id="tdWindDir"><i style="transform: rotate(${obj[i][j].wind.deg}deg)" class="fa-solid fa-arrow-up"></i></td>
            </row>
            `;
        }
      }



    });


}

We can see here that the event listener is properly attached - this is true of both buttons but I'll show one here just to be representative:

enter image description here

Full Github project is accessible as a fully deployed page here.

To reiterate, the problem is that the overlay does not appear at all during this sequence of events and I'm seeing page elements prematurely before the page is built:

    loadingOverlayOn();
    await runSearch(cityName, cityState, cityCountry, cityLat, cityLng, units)
    loadingOverlayOff();
ptrcao
  • 421
  • 1
  • 5
  • 19
  • 1
    `omitted due to size` - too bad that's probably the code you've got wrong - also too bad you haven't even indicated where that function is written ... nobody wants to trawl through you github project - trust me – Jaromanda X Jan 18 '23 at 08:14
  • @JaromandaX The fully deployed code is [here](https://github.com/ptrcao/06-Server-Side-APIs-02-Challenge) – ptrcao Jan 18 '23 at 08:16
  • so? post the relevant code in the question like everyone else does ... the issue is probably that you have some fetch in `runSearch` which you've written poorly, i.e. not awaiting the result inside that function, therefore that function resolves too early ... – Jaromanda X Jan 18 '23 at 08:18
  • @JaromandaX fair call - I have posted the full `runSearch` function as you've suggested. Luckily it is not so long that it reaches the character limit. – ptrcao Jan 18 '23 at 08:24
  • 2
    see that `fetch` ... why are you not using `await` in an `async` function ... then it'll work – Jaromanda X Jan 18 '23 at 08:28

1 Answers1

1

You are not awaiting fetch, you are using then instead.

You have to await fetch

See below example

const response = await fetch(url);
const jsonData = await response.json()
Alen.Toma
  • 4,684
  • 2
  • 14
  • 31