Build a Live Currency Converter with plain JS
Tags: javascript, scrimba, beginners
Published: 2021-01-17 17:40:33

Welcome!

If you've taken a peak at my previous posts you would have seen how much I enjoyed the daily challenges [Scrimba's #Javascriptmas](https://scrimba.com/learn/adventcalendar) provided. Luckily there was no need to miss out in the New Year by signing up to receive their weekly javascript challenge directly to my inbox - the [Weekly Web Dev Challenge](https://weeklychallenge.scrimba.com/)!

Week 1 was animating a mini clock project, perfect timing after just recently running through a similar project in Wes Bos' [JavaScript 30](https://javascript30.com/).

But for this week's challenge (a currency converter) I really wanted to push the limits of my current Javascript knowledge and learn even more. Learn things I certainly did and wanted to share with you all here. Is it the most efficient code? Doubtful. Are there better methods to do this? I fully expect so, and hope you have the time to share them or advise on how to improve in the comments. So finally, let's dig in [or check out the final code here](https://scrimba.com/scrim/coa584a2e987da3502f2b3fae).

The amazing folks over at Scrimba provide you with some starter HTML, CSS and Javascript (obviously empty) files which you can start editing straight away in the online coder. This week's starting point:

![Starting HTML](https://dev-to-uploads.s3.amazonaws.com/i/piw6j5f66e3h21rviimy.PNG)

The challenge, take the user inputs and calculate the new currency amount based on the inputted exchange rate. Not too difficult, but first I need to tackle that HTML and CSS to get something looking vaguely professional (if you think it look's worse please don't tell me).

![Finished HTML](https://dev-to-uploads.s3.amazonaws.com/i/0o1bviybzij9s7gvjwes.PNG)

Nothing crazy on the styling here, just some flexbox positioning, a money related background image and amending the currency text inputs to a select dropdown of options. But refresh the page and something I think is particularly fun happens, that money related image changes? 

See, cool! But **how**?
If you haven't already checked out [unsplash](https://unsplash.com/) for stunning free to use images I highly recommend it. Hidden away in between the main site and the API is their simple to use [Unsplash Source embedding tool](https://source.unsplash.com/) with a plethora of options to choose from. Adding this to your CSS couldn't be easier, simply place the URL with any query parameters into your background style and you're away! Example code I utilized with just the image size and search related word in the query: `background: url("https://source.unsplash.com/1600x900/?currency");`

So why change the currency text inputs to **dropdowns**?
Well firstly, it's easier on the user to select from the range of options than to manually type in the currency (a theory slowly reflected on after providing 150+ currencies to select from). Secondly to prevent accidental typos, possible different spellings and ensure the currency selected is aligned to the [ISO 4217](https://en.wikipedia.org/wiki/ISO_4217) alphabetic 3 digit code. Which is great, but how can we make sure the user knows exactly which currency is selected? I certainly wasn't aware that the *East Caribbean dollar* was under *XCD*. Let's use our first bit of Javascript to update the html with the full currency name. Not only that I've been looking for a good case to use **data-sets** to easily connect the selected code and the related currency name together.

In the HTML we have each select option containing both the full name of the currency (data-name) and the ISO 4217 code as the value:
`<option value="AED" data-name="United Arab Emirates dirham">AED</option>`
Using Javascript we can simply assign to a variable the corresponding full currency name when the option is selected (event listener) then update the HTML with this variable.
`const currencyName = this.selectedOptions[0].dataset.name;`
Are there other ways to do this? Sure, but with this method we can easily add/remove further options into the HTML and data-sets allow us to easily grab these corresponding values.

Seb, **I think you're forgetting about the other user input....**
Thanks, this was originally a number input only, since we'll only be dealing with numbers in here however we'll see later on how to play with the **Intl.NumberFormat** which provides us with the relevant currency symbol (or ISO code if not available) and formatting, sweet! But all this means we have to change the input from number to text to allow the string of characters being provided from the Intl.NumberFormat to be inserted into the input.
But wait, **can't the user type any character into this field**?
Not with javascript! A keydown event listener can stop the value being populated if it doesn't match the list of allowed characters. Hopefully the commented code block explains what's happening at each part. Bonus check and allow only 1 decimal point.
```
function checkNumberKey(e){
  // stop default adding typed value to input
  e.preventDefault();
  // set allowed values
  const allowedKeys = "0123456789";
  const keyArray = allowedKeys.split("");
  const allowOnce = ".";
  // adds to input if matches allowed characters
  if(keyArray.includes(e.key)){
    inputAmount.value += e.key;
  }else if(!inputAmount.value.includes(".") && e.key === allowOnce){ // allows . if not present
    inputAmount.value += e.key;
  }
}
```
Tell us more about this [Intl.NumberFormat](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/NumberFormat)?
Using the constructor this creates a new NumberFormat object which returns a string outputting the desired currency format. [MDN Docs](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/NumberFormat) is a much better explainer than I could ever be but let's see how I used it to for our desired outcome.
Multiple options can be placed into an object as such:
```
const formatOptions = {
    style: "currency",
    currency: currencyCode,
    minimumFractionDigits: 2,
    currencyDisplay: "symbol",
  };
```
Which will format the returned string into a currency format ( **,** & **.00** included) to 2 d.p. and add the currency symbol, awesome stuff!

Next we need to use the constructor (and above options) to create the NumberFormat and stick it into a variable which we can use to update the HTML:
`const currencyFormatText = new Intl.NumberFormat("en-US", formatOptions).format(number);`
If you're comfortable with RegEx you can always use this to format the number directly into currency but this was a great test case to dip into Intl.NumberFormat for the first time.

Brilliant, our front is set with our selected currency all formatted and inputs ready to select/ tap tap into.
![Formatted Inputs](https://dev-to-uploads.s3.amazonaws.com/i/ozlwi3tj0bs2elfs8mzr.PNG)

Now the final part, the actual challenge itself - to exchange the currencies! The original challenge asked the user to input the desired exchange rate but we can do better, let's grab the actual exchange rate using an API call. I went with [currencyconverterapi.com](https://www.currencyconverterapi.com/) who provide a free API to hit responding with JSON containing the exchange rate, but don't feel tied down there's plenty of other API's in the sea.

First you need to head over there and sign up with an email to receive your API key. Next we can actually start to build our URL query. Thankfully because we earlier used the ISO 4217 codes we can simply grab these, encode them and add to our query.
```
const apiKey = "not-an-actual-api-key";
  // encode currency and build the query
  const fromCurrencyURI = encodeURIComponent(fromCurrency);
  const toCurrencyURI = encodeURIComponent(toCurrency);
  const query = fromCurrencyURI + "_" + toCurrencyURI;
  // add the key and query to final url
```
The full URL is just the API endpoint plus the query parameters, a return option (compact) and our API key.
```
  // add the key and query to final url
  const url =
    "https://free.currconv.com/api/v7/convert?q=" +
    query +
    "&compact=ultra&apiKey=" +
    apiKey;
```
Now we have our URL we can use the fetch API to send it, receive the JSON response, parse it to data and use the returned exchange rate. There's many ways we can do this but I wanted to play with Async/ Await for better practice like so:
```
async function getExchangeRate() {
  // grab selections
  const fromCurrency = inputOriginalCurrency.value;
  const toCurrency = inputNewCurrency.value;
  // personal key
  const apiKey = "still-not-an-actual-api-key";
  // encode currency and build the query
  const fromCurrencyURI = encodeURIComponent(fromCurrency);
  const toCurrencyURI = encodeURIComponent(toCurrency);
  const query = fromCurrencyURI + "_" + toCurrencyURI;
  // add the key and query to final url
  const url =
    "https://free.currconv.com/api/v7/convert?q=" +
    query +
    "&compact=ultra&apiKey=" +
    apiKey;
  // send it
  const response = await fetch(url);
  const data = await response.json();
  const FXRate = data[query];
  // update html
  inputFXRate.innerHTML = FXRate;
  // actually calculate the new amount
  const toAmount = amount * FXRate;
  // format currency
  const fromText = formatToCurrency(amount, fromCurrency);
  const toText = formatToCurrency(toAmount, toCurrency);
  // update html with xchange details
  const msg = `${fromText} = ${toText}`;
  outputAmount.innerHTML = msg;
}
```
Throw it all together and we get a Frankenstein of a mini project which works :) I doubt that my approach is the greatest and yet I'm still proud I could push the limit and learn new methods to make it work. It's been great to see how others have completed the challenge and shared under [#WeeklyWebDevChallenge](https://twitter.com/hashtag/WeeklyWebDevChallenge?src=hashtag_click).

[So check out the finished product here](https://scrimba.com/scrim/coa584a2e987da3502f2b3fae) and let me know what you think!