Fade in whole div containing multiple divs

Issue

Im trying to build a weather app to learn JS. I want to make the weather stats change with a fade out and then fade in. I cant seem to get it to work, based on what i googled its complicated because i used a "display:none" to hide these elements at first. In case that’s the issue what should i’ve written instead of "display:none"? and how can i do the fadeout/in effect? in case the "display:none" isnt a real issue here, just answer the latter question. Which is the one making me suffer right now.

Its my first post here if i forgot something tell me and i’ll edit it into the post! i’ll leave a JSFiddle of my code so you can run the code and make the running window bigger. In case you want to read the code here without testing for some reason here’s some parts of it (since posting the whole code would take A LOT of lines).

HTML

<div class="containerStats" class="hidden">

  <p id="bigPadding">Look up a city for it's weather stats.</p>

  <div id="divTemperature" class="hidden">

    <p id="temperature">19°</p>
    <p id="metricCelcius">C</p>

  </div>

  <div class="hidden">

    <p id="city">Rosario</p>

    <div id="iconText">

      <p id="weatherText">Sunny</p>
      <img id="weatherIcon" src="http://openweathermap.org/img/wn/[email protected]" alt="">

    </div>
  </div>

  <div id="spaceBetween" class="hidden">

    <div>

      <p id="windSpeed"> Wind</p>
      <p id="humidity">Humidity</p>

    </div>

    <div>

      <p id="windSpeedKM"> 32km/h</p>
      <p id="humidityPorcentage">62%</p>

    </div>
  </div>



</div>

CSS

.containerStats {

  display: flex;
  position: absolute;
  bottom: 22px;
  margin: 20px;
  margin-left: 28px;
  padding: 10px;
  text-align: left;
  align-items: center;
  justify-content: space-around;
  background-color: rgba(34, 32, 32, 0.253);
  backdrop-filter: blur(12px);
  border-radius: 10px;


}

#divTemperature {

  display: flex;
  vertical-align: baseline;
}

#temperature {

  font-size: 82px;
  vertical-align: bottom;
}

#metricCelcius {

  font-size: 62px;
  font-weight: 300;
  margin-right: 22px;
  vertical-align: center;
}


#spaceBetween {

  display: flex;
  justify-content: space-between;
  margin-left: 2vw;
}

#windSpeedKM {

  margin-left: 4vw;
}

#humidityPorcentage {

  margin-left: 4vw;
}

.hidden {

  display: none !important;
  opacity: 0;

  transition: 1s;

}

.show {

  transition: 1s;
  opacity: 1;
}

#bigPadding {

  padding: 25px;
}

JS (ALL)

//Var declarations for search bar.
let inputCity = document.getElementById("searchBar");
let cityName = inputCity.value;
let searchIcon = document.getElementById("searchIcon");
let recentCitySearches = document.querySelectorAll("p.recentCity");

//Var declarations for stats output.
let temperatureOutput = document.getElementById("temperature");
let cityOutput = document.getElementById("city");
let iconOutput = document.getElementById("weatherIcon");
let adjectiveOutput = document.getElementById("weatherText");
let windOutput = document.getElementById("windSpeedKM");
let humidityOutput = document.getElementById("humidityPorcentage");


//API link-creation vars
let api_url = "https://api.openweathermap.org/data/2.5/weather?q=";
let api_key = "&appid=8d6d613f6cb4621a5c237a580219c44c";
let unit = "&units=metric";


let i = 0;
let start = true;
let finishedStatsChange = true;


//Send input to by pressing enter.
inputCity.addEventListener("keydown", enter => {

  if (enter.key == "Enter") {

    //Change city name variable, and call function for upper case.
    cityName = inputCity.value;
    let latestSearch = firstUpperCase(cityName);

    //Update history to show the city name.
    recentCitySearches[i].style.opacity = 0;
    recentCitySearches[i].innerHTML = latestSearch;
    recentCitySearches[i].style.opacity = 1;

    //Clean input text.
    inputCity.value = "";
    cityName = latestSearch;
    i++;

    //Finish api url by adding the city name from input, call getData() function-
    let full_url = api_url + cityName + unit + api_key;
    getData(full_url);

    if (i > 2) {

      i = 0;


    }
  }

})

//Send input to by clicking search icon instead of pressing enter.
searchIcon.addEventListener("click", search => {


  //Change city name variable, and call function for upper case.
  cityName = inputCity.value;
  let latestSearch = firstUpperCase(cityName);

  //Update history to show the city name.

  recentCitySearches[i].style.opacity = 0;
  recentCitySearches[i].innerHTML = latestSearch;
  recentCitySearches[i].style.opacity = 1;

  //Clean input text.
  inputCity.value = "";
  i++;
  cityName = latestSearch;

  //Finish api url by adding the city name from input, call getData() function-
  let full_url = api_url + cityName + unit + api_key;
  getData(full_url);

  //Execute hideShow()
  hideShow();


  if (i > 2) {

    i = 0;


  }
})

//Function always make first letter upper case.
function firstUpperCase(cityName) {

  //Assign touppercase() to first letter of string, then add the rest of the sentence by using the actual sentence with the first letter sliced. 
  latestSearch = cityName[0].toUpperCase() + cityName.slice(1);

  return latestSearch;

}

//Click a city from history and see its weather again. (City 1)
recentCitySearches[0].addEventListener("click", clickHistory => {

  cityName = recentCitySearches[0].innerText;
  let full_url = api_url + cityName + unit + api_key;
  getData(full_url);



})

//Click a city from history and see its weather again. (City 2)
recentCitySearches[1].addEventListener("click", clickHistory => {

  cityName = recentCitySearches[1].innerText;
  let full_url = api_url + cityName + unit + api_key;
  getData(full_url);

})

//Click a city from history and see its weather again. (City 3)
recentCitySearches[2].addEventListener("click", clickHistory => {

  cityName = recentCitySearches[2].innerText;
  let full_url = api_url + cityName + unit + api_key;
  getData(full_url);

})

//Hide initial message and show weather stats.
function hideShow() {

  if (start == true) {

    let statsHidden = document.querySelectorAll(".hidden");

    for (let i = 0; i < statsHidden.length; i++) {

      statsHidden[i].classList.replace('hidden', 'show');

    }

    let initialMessage = document.getElementById("bigPadding");
    initialMessage.classList.add("hidden");
  }

  start = false;

}


//Change background img depending on city.


//Get info with API.
async function getData(full_url) {

  const api_respone = await fetch(full_url);
  const data = await api_respone.json();

  //Save stats in vars.
  const cityTemperature = data.main.temp;
  const cityHumidity = data.main.humidity;
  const cityWindSpeed = data.wind.speed;
  const weatherAdjective = data.weather[0].description;
  const weatherIcon = data.weather[0].icon;
  changeOutput(cityTemperature, cityHumidity, cityWindSpeed, weatherAdjective, weatherIcon);

  //Once all stats are replaced, execute hideShow()
  hideShow();

}


//Change weather stats info depending on city
function changeOutput(cityTemperature, cityHumidity, cityWindSpeed, weatherAdjective, weatherIcon) {

  temperatureOutput.innerText = Math.round(cityTemperature) + "°";
  cityOutput.innerText = cityName;
  iconOutput.src = "http://openweathermap.org/img/wn/" + weatherIcon + "@2x.png";
  adjectiveOutput.innerText = firstUpperCase(weatherAdjective);
  humidityOutput.innerText = Math.round(cityHumidity) + "%";
  windOutput.innerText = Math.round(cityWindSpeed * 3.6) + "km/h";

  finishedStatsChange = true;

}

Solution

So while getting this to work i found a number of bad practices which may have been confusing you in your html:

  • declared some divs with multiple class types:
    <div class="class1" class="class2"> this should instead be written as a div within a div:

<div class="class1"><div class="class2"></div></div>

or in one div separated by a space

<div class="class1 class2 class3"></div>

  • declared nested divs as the same classes, in some scenarios this is okay but for your purpose this was incorrect

so i fixed these in your html and then added some extra css to continue the previous styling:

.containerStats {left: 22px;}

.hidden {
  opacity: 0;
  -webkit-transition: all 1s ease-in-out;
  -moz-transition: all 1s ease-in-out;
  -o-transition: all 1s ease-in-out;
  transition: all 1s ease-in-out;
}

.show {
  opacity: 1;
  -webkit-transition: all 1s ease-in-out;
  -moz-transition: all 1s ease-in-out;
  -o-transition: all 1s ease-in-out;
  transition: all 1s ease-in-out;
}

then in order to make the html div contaning the weather stats fade in and out with the css we put in, we need to fade out the block of html, wait for the animation and then fade it back in. this was best achieved through a new function call and a change to a previous function to chain them together.

async function getData(full_url) {
  // get data
  animateChangeOutput(cityTemperature, cityHumidity, cityWindSpeed, weatherAdjective, weatherIcon);
}

function animateChangeOutput(cityTemperature, cityHumidity, cityWindSpeed, weatherAdjective, weatherIcon) {
    weatherStats = document.getElementById('weatherStats');
  weatherStats.setAttribute('class', 'hidden');
  
  if (i == 1){
    changeOutput(cityTemperature, cityHumidity, cityWindSpeed, weatherAdjective, weatherIcon)
  }else {
    setTimeout(() => { changeOutput(cityTemperature, cityHumidity, cityWindSpeed, weatherAdjective, weatherIcon); }, 1000);
  }
  
  setTimeout(() => {  weatherStats.setAttribute('class', 'show'); }, 1000);
}

I feel like this whole page could have been restructured to make these jobs a lot simpler and easier, but this was the best that i could do without making major changes to anything you did

A full JSFiddle can be found here https://jsfiddle.net/q59n37rx/

Answered By – liam

This Answer collected from stackoverflow, is licensed under cc by-sa 2.5 , cc by-sa 3.0 and cc by-sa 4.0

Leave a Reply

(*) Required, Your email will not be published