├── README.md ├── icons ├── clear.svg ├── cloud.svg ├── haze.svg ├── rain.svg ├── snow.svg └── storm.svg ├── index.html ├── script.js └── style.css /README.md: -------------------------------------------------------------------------------- 1 | 2 | # Weather App in JavaScript 3 | 4 | In this app, you can get the weather details of a particular city by entering the city name. You can also get your current location weather details by clicking on the "Get Device Location" button. You'll get many weather details including temperature in celsius, weather conditions, location, feels like, and humidity. 5 | 6 | ![App Screenshot](https://img.youtube.com/vi/c1r-NqYkFPc/maxresdefault.jpg) 7 | 8 | [View Live Demo](https://codingnepalweb.com/demos/weather-app-in-javascript/) 9 | 10 | [Watch it on YouTube](https://youtu.be/c1r-NqYkFPc) 11 | 12 | ## Usage 13 | 14 | Paste your API key to the appid parameter of the given URLs. These URLs are in line.no 27 & 33 of script.js file. You can get an API key from [here](https://openweathermap.org/api) for free and view my [blog](https://www.codingnepalweb.com/build-weather-app-html-javascript/) for detailed information. 15 | 16 | ```javascript 17 | api = `https://api.openweathermap.org/data/2.5/weather?q=${city}&units=metric&appid=your_api_key`; 18 | ``` 19 | ```javascript 20 | api = `https://api.openweathermap.org/data/2.5/weather?lat=${latitude}&lon=${longitude}&units=metric&appid=your_api_key`; 21 | ``` 22 | 23 | ## Related 24 | 25 | Here are some related projects 26 | 27 | [Get User Location in JavaScript](https://www.youtube.com/watch?v=J-lUOFXxG_0) 28 | 29 | [Currency Converter in JavaScript](https://www.youtube.com/watch?v=UY7F37KHyI8) 30 | 31 | [Create Todo List App in JavaScript](https://www.youtube.com/watch?v=2QIMUBilooc) 32 | 33 | ## Feedback 34 | 35 | If you have any feedback, please reach out to me at contact@codingnepalweb.com 36 | -------------------------------------------------------------------------------- /icons/clear.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 6 | 7 | 9 | 11 | 13 | 15 | 18 | 21 | 24 | 27 | 30 | 33 | 36 | 39 | 40 | 43 | 45 | 47 | 49 | 51 | 54 | 57 | 60 | 63 | 66 | 69 | 72 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | -------------------------------------------------------------------------------- /icons/cloud.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /icons/haze.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /icons/rain.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /icons/snow.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /icons/storm.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Weather App in JavaScript | CodingNepal 7 | 8 | 9 | 10 | 11 | 12 | 13 |
14 |
Weather App
15 |
16 |

17 |
18 | 19 |
20 | 21 |
22 |
23 |
24 | Weather Icon 25 |
26 | _ 27 | °C 28 |
29 |
_ _
30 |
31 | 32 | _, _ 33 |
34 |
35 |
36 | 37 |
38 |
39 | _ 40 | °C 41 |
42 |

Feels like

43 |
44 |
45 |
46 | 47 |
48 | _ 49 |

Humidity

50 |
51 |
52 |
53 |
54 |
55 | 56 | 57 | 58 | 59 | 60 | -------------------------------------------------------------------------------- /script.js: -------------------------------------------------------------------------------- 1 | const wrapper = document.querySelector(".wrapper"), 2 | inputPart = document.querySelector(".input-part"), 3 | infoTxt = inputPart.querySelector(".info-txt"), 4 | inputField = inputPart.querySelector("input"), 5 | locationBtn = inputPart.querySelector("button"), 6 | weatherPart = wrapper.querySelector(".weather-part"), 7 | wIcon = weatherPart.querySelector("img"), 8 | arrowBack = wrapper.querySelector("header i"); 9 | 10 | let api; 11 | 12 | inputField.addEventListener("keyup", e =>{ 13 | // if user pressed enter btn and input value is not empty 14 | if(e.key == "Enter" && inputField.value != ""){ 15 | requestApi(inputField.value); 16 | } 17 | }); 18 | 19 | locationBtn.addEventListener("click", () =>{ 20 | if(navigator.geolocation){ // if browser support geolocation api 21 | navigator.geolocation.getCurrentPosition(onSuccess, onError); 22 | }else{ 23 | alert("Your browser not support geolocation api"); 24 | } 25 | }); 26 | 27 | function requestApi(city){ 28 | api = `https://api.openweathermap.org/data/2.5/weather?q=${city}&units=metric&appid=your_api_key`; 29 | fetchData(); 30 | } 31 | 32 | function onSuccess(position){ 33 | const {latitude, longitude} = position.coords; // getting lat and lon of the user device from coords obj 34 | api = `https://api.openweathermap.org/data/2.5/weather?lat=${latitude}&lon=${longitude}&units=metric&appid=your_api_key`; 35 | fetchData(); 36 | } 37 | 38 | function onError(error){ 39 | // if any error occur while getting user location then we'll show it in infoText 40 | infoTxt.innerText = error.message; 41 | infoTxt.classList.add("error"); 42 | } 43 | 44 | function fetchData(){ 45 | infoTxt.innerText = "Getting weather details..."; 46 | infoTxt.classList.add("pending"); 47 | // getting api response and returning it with parsing into js obj and in another 48 | // then function calling weatherDetails function with passing api result as an argument 49 | fetch(api).then(res => res.json()).then(result => weatherDetails(result)).catch(() =>{ 50 | infoTxt.innerText = "Something went wrong"; 51 | infoTxt.classList.replace("pending", "error"); 52 | }); 53 | } 54 | 55 | function weatherDetails(info){ 56 | if(info.cod == "404"){ // if user entered city name isn't valid 57 | infoTxt.classList.replace("pending", "error"); 58 | infoTxt.innerText = `${inputField.value} isn't a valid city name`; 59 | }else{ 60 | //getting required properties value from the whole weather information 61 | const city = info.name; 62 | const country = info.sys.country; 63 | const {description, id} = info.weather[0]; 64 | const {temp, feels_like, humidity} = info.main; 65 | 66 | // using custom weather icon according to the id which api gives to us 67 | if(id == 800){ 68 | wIcon.src = "icons/clear.svg"; 69 | }else if(id >= 200 && id <= 232){ 70 | wIcon.src = "icons/storm.svg"; 71 | }else if(id >= 600 && id <= 622){ 72 | wIcon.src = "icons/snow.svg"; 73 | }else if(id >= 701 && id <= 781){ 74 | wIcon.src = "icons/haze.svg"; 75 | }else if(id >= 801 && id <= 804){ 76 | wIcon.src = "icons/cloud.svg"; 77 | }else if((id >= 500 && id <= 531) || (id >= 300 && id <= 321)){ 78 | wIcon.src = "icons/rain.svg"; 79 | } 80 | 81 | //passing a particular weather info to a particular element 82 | weatherPart.querySelector(".temp .numb").innerText = Math.floor(temp); 83 | weatherPart.querySelector(".weather").innerText = description; 84 | weatherPart.querySelector(".location span").innerText = `${city}, ${country}`; 85 | weatherPart.querySelector(".temp .numb-2").innerText = Math.floor(feels_like); 86 | weatherPart.querySelector(".humidity span").innerText = `${humidity}%`; 87 | infoTxt.classList.remove("pending", "error"); 88 | infoTxt.innerText = ""; 89 | inputField.value = ""; 90 | wrapper.classList.add("active"); 91 | } 92 | } 93 | 94 | arrowBack.addEventListener("click", ()=>{ 95 | wrapper.classList.remove("active"); 96 | }); 97 | -------------------------------------------------------------------------------- /style.css: -------------------------------------------------------------------------------- 1 | /* Import Google Font - Poppins */ 2 | @import url('https://fonts.googleapis.com/css2?family=Poppins:wght@400;500;600;700&display=swap'); 3 | *{ 4 | margin: 0; 5 | padding: 0; 6 | box-sizing: border-box; 7 | font-family: 'Poppins', sans-serif; 8 | } 9 | body{ 10 | display: flex; 11 | align-items: center; 12 | justify-content: center; 13 | min-height: 100vh; 14 | background: #43AFFC; 15 | } 16 | ::selection{ 17 | color: #fff; 18 | background: #43AFFC; 19 | } 20 | .wrapper{ 21 | width: 400px; 22 | background: #fff; 23 | border-radius: 7px; 24 | box-shadow: 7px 7px 20px rgba(0, 0, 0, 0.05); 25 | } 26 | .wrapper header{ 27 | display: flex; 28 | font-size: 21px; 29 | font-weight: 500; 30 | color: #43AFFC; 31 | padding: 16px 15px; 32 | align-items: center; 33 | border-bottom: 1px solid #ccc; 34 | } 35 | header i{ 36 | font-size: 0em; 37 | cursor: pointer; 38 | margin-right: 8px; 39 | } 40 | .wrapper.active header i{ 41 | margin-left: 5px; 42 | font-size: 30px; 43 | } 44 | .wrapper .input-part{ 45 | margin: 20px 25px 30px; 46 | } 47 | .wrapper.active .input-part{ 48 | display: none; 49 | } 50 | .input-part .info-txt{ 51 | display: none; 52 | font-size: 17px; 53 | text-align: center; 54 | padding: 12px 10px; 55 | border-radius: 7px; 56 | margin-bottom: 15px; 57 | } 58 | .input-part .info-txt.error{ 59 | color: #721c24; 60 | display: block; 61 | background: #f8d7da; 62 | border: 1px solid #f5c6cb; 63 | } 64 | .input-part .info-txt.pending{ 65 | color: #0c5460; 66 | display: block; 67 | background: #d1ecf1; 68 | border: 1px solid #bee5eb; 69 | } 70 | .input-part :where(input, button){ 71 | width: 100%; 72 | height: 55px; 73 | border: none; 74 | outline: none; 75 | font-size: 18px; 76 | border-radius: 7px; 77 | } 78 | .input-part input{ 79 | text-align: center; 80 | padding: 0 15px; 81 | border: 1px solid #ccc; 82 | } 83 | .input-part input:is(:focus, :valid){ 84 | border: 2px solid #43AFFC; 85 | } 86 | .input-part input::placeholder{ 87 | color: #bfbfbf; 88 | } 89 | .input-part .separator{ 90 | height: 1px; 91 | width: 100%; 92 | margin: 25px 0; 93 | background: #ccc; 94 | position: relative; 95 | display: flex; 96 | align-items: center; 97 | justify-content: center; 98 | } 99 | .separator::before{ 100 | content: "or"; 101 | color: #b3b3b3; 102 | font-size: 19px; 103 | padding: 0 15px; 104 | background: #fff; 105 | } 106 | .input-part button{ 107 | color: #fff; 108 | cursor: pointer; 109 | background: #43AFFC; 110 | transition: 0.3s ease; 111 | } 112 | .input-part button:hover{ 113 | background: #1d9ffc; 114 | } 115 | 116 | .wrapper .weather-part{ 117 | display: none; 118 | margin: 30px 0 0; 119 | align-items: center; 120 | justify-content: center; 121 | flex-direction: column; 122 | } 123 | .wrapper.active .weather-part{ 124 | display: flex; 125 | } 126 | .weather-part img{ 127 | max-width: 125px; 128 | } 129 | .weather-part .temp{ 130 | display: flex; 131 | font-weight: 500; 132 | font-size: 72px; 133 | } 134 | .weather-part .temp .numb{ 135 | font-weight: 600; 136 | } 137 | .weather-part .temp .deg{ 138 | font-size: 40px; 139 | display: block; 140 | margin: 10px 5px 0 0; 141 | } 142 | .weather-part .weather{ 143 | font-size: 21px; 144 | text-align: center; 145 | margin: -5px 20px 15px; 146 | } 147 | .weather-part .location{ 148 | display: flex; 149 | font-size: 19px; 150 | padding: 0 20px; 151 | text-align: center; 152 | margin-bottom: 30px; 153 | align-items: flex-start; 154 | } 155 | .location i{ 156 | font-size: 22px; 157 | margin: 4px 5px 0 0; 158 | } 159 | .weather-part .bottom-details{ 160 | display: flex; 161 | width: 100%; 162 | justify-content: space-between; 163 | border-top: 1px solid #ccc; 164 | } 165 | .bottom-details .column{ 166 | display: flex; 167 | width: 100%; 168 | padding: 15px 0; 169 | align-items: center; 170 | justify-content: center; 171 | } 172 | .column i{ 173 | color: #5DBBFF; 174 | font-size: 40px; 175 | } 176 | .column.humidity{ 177 | border-left: 1px solid #ccc; 178 | } 179 | .column .details{ 180 | margin-left: 3px; 181 | } 182 | .details .temp, .humidity span{ 183 | font-size: 18px; 184 | font-weight: 500; 185 | margin-top: -3px; 186 | } 187 | .details .temp .deg{ 188 | margin: 0; 189 | font-size: 17px; 190 | padding: 0 2px 0 1px; 191 | } 192 | .column .details p{ 193 | font-size: 14px; 194 | margin-top: -6px; 195 | } 196 | .humidity i{ 197 | font-size: 37px; 198 | } --------------------------------------------------------------------------------