Building a Utility Weather Widget with JavaScript
Published: 2022-12-21 11:40:52

---
title: Building  a Utility Weather Widget with JavaScript
published: true
date: 2022-12-19 09:31:50 UTC
tags: 
canonical_url: 
---

We've all seen widgets on web pages before, haven't we? Those little icons of varying shapes and sizes usually displayed in a corner of a devices viewport. These widgets are simply small, pre-designed components that can be easily added to a website to provide additional functionality or information. They can be made using **HTML** , **CSS** , and **JavaScript**. By hovering over or clicking on a widget, you can interact with it and see what it does. Examples of widgets include chatbots in the sidebar, pop-ups, and CTA buttons.

In this article, we will learn how to build a widget that displays a user's current weather, date and time using their current location.

## **Why use Widgets?**

For one, you can use widgets to improve the visual organization and presentation of information on a website. Widgets can be added to a website to provide additional features or hide data that may create cluttered visual elements. They can also be used to display pop-ups and other types of information without making changes to the website's underlying code. By using widgets, you can effectively reduce clutter and improve the overall appearance of your website.

## Prerequisites

- Fundamental knowledge of HTML, CSS and JavaScript.

- Fundamental knowledge of JavaScripts Native Internalization and Geolocation API.

- Fundamental knowledge of Asynchronous JavaScript (Using Promises and the **_try catch_** block).

- A code editor and a browser (Preferably Google Chrome).

- Fundamental knowledge of the working of a Browsers Dev Tools.

## **Project Breakdown**

Before we dive into building our project, it is important to know that this project consists of 2 parts:

- **The Interface section**

- **The Functionality section**

### **The Interface Section**

This section consists of all the mockup and styling required to render our widget on the page. Instead of diving head-first into the strange-looking HTML and CSS, I will give you a sneak preview of what the widget will look like in the end. This will help you understand why our codebase is structured the way it is.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1671304428771/quSA7uxxY.png)

Excited right? Lets dive in!

The **_markup_** for this project is shown below.

```
<body>
    <div class="widget">
      <div class="widget__blob">
         <i class="fa-duotone fa-gear"></i>
      </div>
      <div class="widget__sidebar">
        <div class="arrow__pointer"></div>
        <div class="weather">
          <span class="greet">Hi there, </span>
          <i class="fa-solid fa-hand-wave"></i><br />
        </div>
        <div class="date">Placeholder Date</div>
        <div class="color__palette--container">
          <div class="color__palette" data-bgcolor="#5DADE2"></div>
          <div class="color__palette" data-bgcolor="#cf9fed"></div>
          <div class="color__palette" data-bgcolor="#73C6B6"></div>
          <div class="color__palette" data-bgcolor="#EDBB99"></div>
          <div class="color__palette" data-bgcolor="#808B96"></div>
        </div>
      </div>
    </div>

```

I know the HTML markup looks weird with all the nesting and **_data_**  **_attributes_** 😂, so lets go over it. First, the main parent element is a **_div_** with the class **_widget_** containing 2 child elements, ( **_widget\_\_blob_** ) and ( **_widget\_\_sidebar_** ). The **_widget\_\_blob_** contains only one element, which is an icon from [**font awesome**](https://fontawesome.com/) **_._**

Next, the **_widget\_\_sidebar_** contains 4 elements ( **_arrow\_\_pointer, weather, date_** and **_color\_\_palettecontainer_** ), which are all self-explanatory. The only thing left to highlight is the **_data-bgcolor_** attribute attached to each of the **_color-pallette_** elements. This attribute will help us dynamically set each of the elements background to the value set in its **_data-bgcolor_** attribute. With the HTML set, we can now proceed to the CSS part of this section.

The **_Styling_** for this project is shown below.

```
* {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
  font-family: "Ubuntu", sans-serif;
}

body {
  display: flex;
  justify-content: center;
  align-items: center;
  height: 100vh;
  background: #e0e0e0;
}

.widget {
  position: relative;
  width: 220px;
  height: 220px;
  display: flex;
  justify-content: center;
  align-items: center;
}

.widget__blob {
  width: 75%;
  height: 75%;
  cursor: pointer;
  background: #5dade2;
  border-radius: 68% 55% 46% 69% / 58% 74% 44% 59%;
  z-index: 2;
  display: flex;
  justify-content: center;
  align-items: center;
  font-size: 4rem;
  color: #f0f4fd;
  transition: all ease-in 0.2s;
}

.widget__blob:hover > i {
  animation: blobby 10s infinite linear;
}

@keyframes blobby {
  0% {
    transform: rotate(0);
  }
  100% {
    transform: rotate(360deg);
  }
}

.widget__sidebar {
  color: #3d3d3d;
  position: absolute;
  top: 0;
  width: 170%;
  left: -200%;
  height: 100%;
  display: flex;
  flex-direction: column;
  justify-content: center;
  padding: 1rem;
  gap: 1rem;
  line-height: 1.5;
  transition: all ease-in 0.2s;
  background: #5dade2;
  border-radius: 1rem;
}

.arrow__pointer {
  position: absolute;
  content: "";
  top: 50%;
  transform: rotate(45deg) translate(0, -50%);
  right: 0;
  background: #5dade2;
  width: 50px;
  height: 50px;
  transition: all ease-in 0.2s;
  z-index: -1;
}

.weather span {
  color: #fff;
  font-size: 1.6rem;
}

.weather i {
  font-size: 1.6rem;
  color: #fff;
}

.color__palette--container {
  display: flex;
  gap: 0.5rem;
}

.color__palette {
  width: 25px;
  height: 25px;
  border-radius: 50%;
  cursor: pointer;
}

```

There's quite a lot of CSS code there, so feel free to take a moment to analyze it before moving on. The styling above simply arranges the two child elements of the **_widget_** element next to each other and adds some interactivity to the **_widget\_\_blob_** element.

What weve achieved so far can be seen below

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1671307211679/RfdAn6iGJ.gif)

We neatly presented both elements and their contents, with only the elements in **_color\_\_palette--container_** still yet to be displayed, but dont worry, we would fix that in the **Functionality section.**

### **The Functionality Section**

This section consists of all our programs logic and how the widget would work. To help you grasp the totality of everything were going to do, heres a flowchart of our application.

**Flowchart of the Application**

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1671306540295/jOXJlyFuT.png)

Before we begin implementing, we should keep in mind that we will start on the right side with the **_Load Page_** symbol.

Weve already added the widget to the webpage. Now, we will handle the behaviour when the widget is clicked. We want to hide the **_widget\_\_sidebar_** element and only show it again when the **_widget\_\_blob_** is clicked. To achieve this, we will create a CSS class that hides the **_widget\_\_sidebar_** element and add that class to the **_widget\_\_sidebar_** element in our HTML markup.

```
.hidden {
  opacity: 0;
  transform: translateX(-100%);
}

```

Then target the **_widget\_\_sidebar_** element to render the **_widget\_\_sidebar_** visible with JavaScript whenever we click on the **_widget\_\_blob_**.

```
const widgetBlob = document.querySelector(".widget__blob");
const widgetSideBar = document.querySelector(".widget__sidebar");

const revealSideBar = function () { widgetSideBar.classList.toggle("hidden"); 
};
widgetBlob.addEventListener("click", revealSideBar);

```

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1671307263885/EYyYxL995.gif)

Here is a quick recap of how we just achieved this;

First, we selected both elements and stored them in variables.

Next, we created a function called **_revealSideBar_** which toggles the **_hidden_** class on the **_widget\_\_sidebar_** element every time the **_widget\_\_blob_** is clicked. Finally, we attached an event handler function to the **_widget\_\_blob_** and passed the **_revealSideBar_** function as a callback. This means that the **_revealSideBar_** function will be called every time the **_widget\_\_blob_** is clicked.

The next step in our flowchart is to display the color palettes and the current date and time. To do this, we use the following lines of code to dynamically set the color of each palette element to its corresponding HTML data attribute value (recall we stored color codes in their individual **data** attribute in our HTML markup).

```
const colorPalete = document.querySelectorAll(".color__palette");
colorPalete.forEach((el) => (el.style.backgroundColor = el.dataset.bgcolor));

```

This simply loops over each element and changes the background color to the value set in its **_data-bgcolor_** attribute.

Thus producing the interface below.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1671307449022/JSvJg8m3D.png)

Next, we work on displaying live date and time information on the **_widget\_\_sidebar._** To accomplish this, we would make use of [**_JavaScripts Internationalization API_**](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl) **_._** This API is a built-in feature of the JavaScript language that provides a set of functions and objects for formatting and parsing dates, numbers, and strings according to the conventions of a specific locale (ie the language format of the users location).

To begin, we first select our HTML element where we plan to store our date and time information.

```
const dateEl = document.querySelector(".date");

```

After that, we retrieve the language preference of the user from the browser using **_navigator.locale_** and store it in the **_locale_** variable.

```
const locale = navigator.language;

```

Logging my locale to the console, yields

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1671307563531/_PwxafKlh.png)

Then we create a date using the date constructor function and store the result in the **_now_** variable.

```
const now = new Date();

```

Logging our result to the console gives:

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1671307647647/h98WUA_RF.png)

Remember that the date and time information will vary based on when you implement this.

Next, we create a **_date options_** object which we would use to extract all the information we want from the date object.

```
const dateOptions = {
    hour: "numeric",
    minute: "numeric",
    day: "numeric",
    month: "long",
    year: "numeric",
    weekday: "long",
  };

```

Then, we use our **_Internationalization API_** to format our date to the users locale. To do this, we first create our Internationalization function and store it in a variable with the syntax below.

```
const curDate = new Intl.DateTimeFormat();

```

After that, we pass in our **_locale_** and our **_dateOptions_** object as arguments to our **_Internationalization_** function.

```
const curDate = new Intl.DateTimeFormat(locale, dateOptions);

```

Then, we call the **_format_** method on the date were trying to format.

```
const curDate = new Intl.DateTimeFormat(locale, dateOptions).format(now);

```

Looking at what weve done in the console gives;

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1671307893286/xp-N9mw2X.png)

Finally, we display our well-formatted data on our webpage.

```
 dateEl.textContent = `Your current date and time is ${curDate}`;

```

With that, lets have a look at our interface so far.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1671308558879/RA0nnwAY0.gif)

And yeah, weve successfully implemented live date and time information which is already pretty cool. Lets refactor our new code into a nice helper function **_getDnT_** which contains all our date and time logic.

```
const getDnT = function () {
  const now = new Date();
  const dateOptions = {
    hour: "numeric",
    minute: "numeric",
    day: "numeric",
    month: "long",
    year: "numeric",
    weekday: "long",
  };
  const locale = navigator.language;
  const curDate = new Intl.DateTimeFormat(locale, dateOptions).format(now);
  dateEl.textContent = `Your current date and time is ${curDate}`;
};

```

Finally, we call the **_getDnT_** function with the same event handler function we called the **_revealSideBar_** function, like so

```
widgetBlob.addEventListener("click", function () {
  revealSideBar();
  getDnT();
});

```

**Note** : Instead of calling our function manually, we can attach it to an event handler function. This will prevent the function from constantly running in the background, which can improve performance.

To clarify the next step, we will change the theme of the **_widget\_sidebar_** , **_arrow\_\_pointer_** , and **_widget\_\_blob_** based on the color palette that we click.

Id like to divide this step into 3 sub-steps:

- **Theme Changing** (Changing the theme based on the palette we click).

- **Active Class Addition** (Adding an active class to the current palette we clicked on).

- **Local Storage Use** ( Storing the active theme in local storage to prevent the theme from resetting on reload of the webpage).

### **Theme Changing**

For this first step, we create a **_themeChanger_** function containing all the swapping logic between our target elements.

```
const arrowPointer = document.querySelector(".arrow__pointer");
const themeChanger = function (e) {
  widgetSideBar.style.backgroundColor = e.target.dataset.bgcolor;
  widgetBlob.style.backgroundColor = e.target.dataset.bgcolor;
  arrowPointer.style.backgroundColor = e.target.dataset.bgcolor;
};

```

The **_e_** argument is simply the event object which points to the current element being clicked. Then, we loop over each **_color\_\_palette_** element, attach an event handler, and pass in the **_themeChanger_** function as an argument. This runs our **_themeChanger_** function everytime we click on a palette.

```
colorPalete.forEach((el) => {
  el.addEventListener("click", themeChanger);
});

```

We have successfully implemented the first step of our theme changer function.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1671309562713/JiLpUNkjI.gif)

### **Active Class Addition**

If you paid attention, you may have noticed that whenever we click on a palette, it appears to blend in and disappear with the background. We plan to fix this by adding an active class to any element we click on, which will make it easier to see which element is currently selected. We first create a CSS active class which separates the active palette from the rest.

```
.color__palette--active {
  border: 1.5px solid #fff;
}

```

Then, we add that active class to any palette we click on using the code snippet below.

```
 e.target.classList.add("color__palette--active");

```

Finally, to ensure that only the clicked palette has the active class, we need to remove the active class from all other palettes. This way, when a new palette is clicked, it will be the only one palette with an active class. This will help us achieve the desired behaviour where only one palette can be active at a time.

```
colorPalete.forEach((el) => el.classList.remove("color__palette--active"));

```

**Note** : We're adding the above code snippets to our **_themeChanger_** function so that we can add the active class when we toggle between themes.

This leaves our final function looking something like this:

```
const themeChanger = function (e) {
  widgetSideBar.style.backgroundColor = e.target.dataset.bgcolor;
  widgetBlob.style.backgroundColor = e.target.dataset.bgcolor;
  arrowPointer.style.backgroundColor = e.target.dataset.bgcolor;
  colorPalete.forEach((el) => el.classList.remove("color__palette--active"));
  e.target.classList.add("color__palette--active");
};

```

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1671310057679/hNrRKUm1E.gif)

### **Local Storage Use**

In the third step, we will use **_Local Storage_** to save the theme that the user has selected. This way, their theme selection will be retained even if the user refreshes the page or closes it. To do this, we have to store the current theme in **_Local Storage_** and load that theme whenever the user refreshes the page. Seems fairly easy? Lets begin then.

First, we call the **_setItem_** method on the **_LocalStorage_** object.

```
localStorage.setItem();

```

This method enables us to store data in the browser and create a **key: value** pair, with the **_key_** being a name used to reference the stored value in **_Local Storage._**

Next, we pass in **key: value** pair into our **_setItem_** method

```
localStorage.setItem("palette", color);

```

Keep in mind that the **_color_** argument would be the value stored in the palette we click.

Next we wrap our above snippet in a function.

```
const setLocalStorage = function (color) {
  localStorage.setItem("palette", color);
};

```

This way, whatever value is being called with the **_setLocalStorage_** function becomes **_color_** value.

Next, we call the **_setLocalStorage_** function with the element that was clicked.

```
setLocalStorage(e.target.dataset.bgcolor);

```

Note: To ensure that Local Storage is updated with the current theme, we call this function inside the **_themeChanger_** function.

To prove **Local Storage** is operational, head over to your developer tools, open the application tab, and then click on a palette.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1671363431989/oPzUEbvbm.gif)

If you observe closely, each time we click on a palette, the local storage theme value is updated, proving weve successfully saved the current theme value.

To ensure that we dont lose our theme when we reload or exit the webpage, we retrieve our theme from local storage and update our interface. To implement this, we start by listening for the **_load event._**

```
window.addEventListener("load", function () {});

```

Next, we retrieve the value stored in our **_Local Storage_** using its **_key_** and store it in a variable.

```
const paletteColor = localStorage.getItem("palette");

```

After that, we change the theme of all our targetted elements to the value stored in **Local Storage.**

```
widgetSideBar.style.backgroundColor = paletteColor;
widgetBlob.style.backgroundColor = paletteColor;
arrowPointer.style.backgroundColor = paletteColor;

```

Next, we clear all the active classes on the palette elements

```
colorPalete.forEach((el) => el.classList.remove("color__palette--active"));

```

Finaly, we select the palette whose data attribute value matches that value stored in the Local Storage, and add the active class to it.

```
const btn = document.querySelector(
    `.color__palette[data-bgcolor="${paletteColor}"]`
  );
btn.classList.add("color__palette--active");

```

We have successfully completed all the steps on the right side of our flowchart. Congratulations on making it this far 🎈, but we still have more work to do.

Next up, wed be working on adding live weather information using asynchronous JavaScript ( promises and the **_try catch_** block. ).

## Adding Weather functionality to the Widget using Asynchronous JavaScript

A code is asynchronous if it can execute multiple lines of code at the same time, rather than executing them one at a time in the order in which they appear.

In JavaScript, asynchronous code is typically written using **_callback functions_** , **_promises_** , or **_async/await_**. These techniques allow the JavaScript engine to execute multiple lines of code concurrently, rather than executing them sequentially. In this step, were going to use **_promises_** and **_async/await_**. techniques to fetch the users current position and supply the user with weather data corresponding with their location.

The steps to achieving this include:

- Promisifying the Geolocation API.

- Catching the error produced when the **_Geolocation API_** promise fails.

- Creating an async function to contain all our logic.

- Extracting the users coordinates.

- Fetching the users city data with the users coordinates.

- Throwing an error if the users city data cannot be obtained.

- Fetching the users weather data with the users coordinates.

- Throwing an error if the users city weather data cannot be obtained.

- Destructuring the weather data object to get the weather value.

- Storing the final weather data string in a variable.

- Inserting the weather data in our webpage.

```
const getPosition = function () {
  return new Promise(function (resolve, reject) {
    navigator.geolocation.getCurrentPosition(resolve, reject);
  });
};

```

Keep in mind that calling this function right now returns a promise and not the data gotten from the **Geolocation** function.

Next, we catch any errors formed when the Geolocation promise fails (i.e, the user refuses to share their location data).

```
getPosition().catch((err) =>
  alert(`${err.message}. Please grant location access to see weather data`)
);

```

After that, we create our async function which includes a **_trycatch_** block to contain all our logic.

```
const whereAmI = async function () {
  try {
  } catch {}
};

```

To extract the users coordinates (latitude and longitude), we simply **_await_** the values from the **_getPosition_** function, then deconstruct the result into 2 variables.

```
const position = await getPosition();
const { latitude, longitude } = position.coords;

```

Next, we use a **_Reverse Geolocation API_** to convert our coordinates into a defined city. Ill be using [Geocode XYZs Reverse Geolocation API](https://geocode.xyz/) to perform this operation.

```
const responseGeo = await fetch( `https://geocode.xyz/${latitude},${longitude}?geoit=json&auth=YOUR_API_KEY`
    );

```

Our result in the console becomes

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1671440107535/o-no-E2th.png)

If an error occurs during the **_fetch_** operation, we handle that by using an error constructor function to throw an error message.

```
if (!responseGeo.ok) throw new Error("Problem getting location data");

```

If no error occurs, we convert the result of our fetch function to an object using the **_.json()_** method.

```
const dataGeo = await responseGeo.json();

```

Next, we use a **_Weather API_** to fetch the users weather information based on their current position. Well be using [Open Metros API](https://open-meteo.com/) to get our weather data.

```
const weatherResponse = await fetch(
      `https://api.open-meteo.com/v1/forecast?latitude=${latitude}&longitude=${longitude}&hourly=temperature_2m`
    );

```

Our result in the console becomes

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1671440393416/eSLxl9elC.png)

We also account for errors during the fetch operation.

```
if (!weatherResponse.ok) throw new Error("Problem getting Weather data");

```

Then convert the value of the fetch function to an object if we do not encounter any errors.

```
const weatherData = await weatherResponse.json();

```

We then deconstruct our **_weatherData_** object to obtain the exact current weather value.

```
const [curWeather] = weatherData.hourly.temperature_2m;

```

After that, we create a string which contains the format for displaying our weather on our webpage.

```
const finalWeather = `The weather in ${dataGeo.region} right now is ${curWeather} ${weatherData.hourly_units.temperature_2m}`;

```

Logging our progress to the console so far gives:

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1671440593174/_08_A0t-s.png)

The resulting data will vary based on your location, but the format would likely be similar.

Then, we insert the above string into our webpage

```
const weatherEl = document.querySelector(".weather");
 weatherEl.insertAdjacentText("beforeend", finalWeather);

```

And with this, weve successfully completed our widget, congratulations!🎉🎉

Only thing left to do is catch any errors that we may not have caught earlier using the catch block and log that to the console.

```
catch (err) {
    console.log(`${err} 💥💥💥`);
  }

```

Then calling our function in the global scope.

```
whereAmI();

```

Our final codebase looks something like this

```
const setLocalStorage = function (color) {
  localStorage.setItem("palette", color);
};

const widget = document.querySelector(".widget");
const widgetBlob = document.querySelector(".widget__blob");
const widgetSideBar = document.querySelector(".widget__sidebar");
const arrowPointer = document.querySelector(".arrow__pointer");
const weatherEl = document.querySelector(".weather");
const paletteContainer = document.querySelector(".color__palette--container");
const colorPalete = document.querySelectorAll(".color__palette");
const dateEl = document.querySelector(".date");

window.addEventListener("load", function () {
  const paletteColor = localStorage.getItem("palette");
  widgetSideBar.style.backgroundColor = paletteColor;
  widgetBlob.style.backgroundColor = paletteColor;
  arrowPointer.style.backgroundColor = paletteColor;

  const btn = document.querySelector(
    `.color__palette[data-bgcolor="${paletteColor}"]`
  );
  colorPalete.forEach((el) => el.classList.remove("color__palette--active"));
  btn.classList.add("color__palette--active");
});

const revealSideBar = function () {
  widgetSideBar.classList.toggle("hidden");
};

widgetBlob.addEventListener("click", function () {
  revealSideBar();
  getDnT();
});

colorPalete.forEach((el) => (el.style.backgroundColor = el.dataset.bgcolor));

const themeChanger = function (e) {
  widgetSideBar.style.backgroundColor = e.target.dataset.bgcolor;
  widgetBlob.style.backgroundColor = e.target.dataset.bgcolor;
  arrowPointer.style.backgroundColor = e.target.dataset.bgcolor;
  colorPalete.forEach((el) => el.classList.remove("color__palette--active"));
  e.target.classList.add("color__palette--active");
  setLocalStorage(e.target.dataset.bgcolor);
};

colorPalete.forEach((el) => {
  el.addEventListener("click", themeChanger);
});

document.addEventListener("keydown", function (e) {
  if (e.key === "Escape" && !widgetSideBar.classList.contains("hidden")) {
    widgetSideBar.classList.toggle("hidden");
  }
});

const getDnT = function () {
  const now = new Date();
  const dateOptions = {
    hour: "numeric",
    minute: "numeric",
    day: "numeric",
    month: "long",
    year: "numeric",
    weekday: "long",
  };

  const locale = navigator.language;

  const curDate = new Intl.DateTimeFormat(locale, dateOptions).format(now);

  dateEl.textContent = `Your current date and time is ${curDate}`;
};

const getPosition = function () {
  return new Promise(function (resolve, reject) {
    navigator.geolocation.getCurrentPosition(resolve, reject);
  });
};

getPosition().catch((err) =>
  alert(`${err.message}. Please grant location access`)
);

const whereAmI = async function () {
  try {
    const position = await getPosition();
    const { latitude, longitude } = position.coords;
    const responseGeo = await fetch(
      `https://geocode.xyz/${latitude},${longitude}?geoit=json&auth=YOUR_API_KEY`
    );

    if (!responseGeo.ok) throw new Error("Problem getting location data");
    const dataGeo = await responseGeo.json();

    const weatherResponse = await fetch(
      `https://api.open-meteo.com/v1/forecast?latitude=${latitude}&longitude=${longitude}&hourly=temperature_2m`
    );
    if (!weatherResponse.ok) throw new Error("Problem getting Weather data");

    const weatherData = await weatherResponse.json();
    const [curWeather] = weatherData.hourly.temperature_2m;

    const finalWeather = `The weather in ${dataGeo.region} right now is ${curWeather} ${weatherData.hourly_units.temperature_2m}`;

    weatherEl.insertAdjacentText("beforeend", finalWeather);
  } catch (err) {
    console.log(`${err} 💥💥💥`);
  }
};
whereAmI();

```

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1671440861456/A0QY2rb-o.gif)

As you can see, our utility widget is fully operational, good going! It displays accurate time, date and weather information all at the click of a button, pretty cool, dont you think?

**Limitation**

Despite our best efforts to build a cool application, it does have a limitation. Both APIs used during implementation were free APIs, meaning that they can only handle a limited number of API calls per second. This hinders our widget from being a full-scale application that can be used by a large number of people simultaneously.

### Project Link

Source Code: [GitHub](https://github.com/Daiveedjay/Utility-Widget-Article)

## **Conclusion**

We successfully built a utility widget using JavaScript that can provide us with the current weather, date, and time. This widget utilizes the **_fetch()_** function to retrieve data from a **_Weather API_** and the **_Date object_** to access the current date and time. By implementing this widget, we have demonstrated the power of JavaScript in creating dynamic and useful web applications. With a few lines of code, we are able to access real-time data and display it in a user-friendly manner, making it easy for users to stay informed and up-to-date. Overall, this widget serves as a useful tool for anyone looking to quickly check the current weather, date and time.