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();
5
  • 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 Commented Jan 18, 2023 at 8:14
  • @JaromandaX The fully deployed code is here Commented Jan 18, 2023 at 8: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 ... Commented Jan 18, 2023 at 8: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. Commented Jan 18, 2023 at 8:24
  • 2
    see that fetch ... why are you not using await in an async function ... then it'll work Commented Jan 18, 2023 at 8:28

1 Answer 1

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()
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.