DEV Community

Cover image for Create a currency converter using Javascript and API
Ashutosh Tiwari
Ashutosh Tiwari

Posted on • Originally published at incoderweb.blogspot.com

Create a currency converter using Javascript and API

Hello friends, today in this blog, we will learn how to create a currency converter using Javascript and API. Our previous blog showed how to create a random color generator with a 3D Effect. You can check my other javascript projects after reading this blog.

In today's globalized world, it's not uncommon to need to convert currency regularly. Whether you're traveling abroad, making international transactions, or simply want to keep track of the value of your investments in different currencies, having access to an accurate and easy-to-use currency converter is essential.

In this blog post, we'll show you how to create a currency converter using JavaScript and an API. JavaScript is a powerful and widely used programming language that can be used to create dynamic and interactive web applications, while an API (Application Programming Interface) allows us to connect to external services and retrieve data in a structured way. By combining these two tools, we can create a simple yet effective currency converter that can be used by anyone with an internet connection.

Whether you're a beginner or an experienced developer, this tutorial is designed to be accessible and easy to follow. We'll walk you through each step of the process, from setting up the project to fetching data from the API and displaying the results. By the end of this tutorial, you'll have a fully functional currency converter that you can use and customize to suit your needs. So, let's get started!

In this project, there is a currency converter as you can see in the image above. You just need to enter the amount and select the currency you want to convert. For example, let's assume I want to convert Indian Rupees. So I will click on the county Flag and a pop-up modal will be opened and I will be able to select a currency, there is a search option, and I can search currencies. I no result is found then this message will be shown:-
Error Image
After selecting the currency, the results of all currencies will be shown and you can search which currency result you want.

You may like these:

Note:
You can check live demo and download code files from here.

Code of HTML, CSS, and JavaScript Files

Here's the good news: you don't have to write all the code of this project from scratch! I have created a GitHub repository that contains all the HTML, CSS, and JavaScript code needed to build the app. You can check it out and use it as a starting point for your own project.

HTML CODE

Just paste this code into HTML File which you have created.

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Currency Converter - InCoder</title>
    <link rel="stylesheet" href="main.css" />
    <link
      rel="stylesheet"
      href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css"
    />
  </head>
  <body>
    <div id="loading">
      <img src="loader.gif" alt="Loading..." />
    </div>
    <div class="mainContainer">
      <div class="title">Currency Converter</div>
      <div class="fromCurrencyInput">
        <span id="flag">
          <img
            src="https://static.vecteezy.com/system/resources/previews/011/571/519/original/circle-flag-of-india-free-png.png"
            alt="Country Flag"
          />
          <p>INR</p>
          <input type="hidden" id="selectedCurrency" value="INR" />
        </span>
        <input
          type="number"
          placeholder="Enter Amount"
          id="conversionAmount"
          value="100"
          onkeydown="getConversion()"
        />
      </div>
      <div class="convertedCurrencyBox">
        <span></span>
        <div class="convertedList">
          <div class="searchBox" style="margin-top: 1rem">
            <input
              type="text"
              placeholder="Search here..."
              class="searchInput"
              id="convertedFilter"
            />
            <i class="fa-solid fa-magnifying-glass"></i>
          </div>
          <div class="loadingScreen">
            <i class="fa-solid fa-spinner fa-spin"></i>
            <p>Loading Data...</p>
          </div>
          <div class="notFound">
            <i class="fa-solid fa-face-frown"></i>
            <h2>No Result Found</h2>
            <p>
              There are no currency that match your current filters. Try removing
              some of them to get better results.
            </p>
          </div>
          <div class="convertedListTag"></div>
        </div>
      </div>
    </div>

    <div class="countriesModal">
      <button class="closeModal"><i class="fa-solid fa-xmark"></i></button>
      <div class="modalBox">
        <div class="searchBox">
          <input
            type="text"
            placeholder="Search Currency..."
            class="searchInput"
            id="searchInp"
          />
          <i class="fa-solid fa-magnifying-glass"></i>
        </div>
        <div class="notFound">
          <i class="fa-solid fa-face-frown"></i>
          <h2>No Result Found</h2>
          <p>
            There are no currency that match your current filters. Try removing
            some of them to get better results.
          </p>
        </div>
        <div class="countriesBox"></div>
      </div>
    </div>

    <script src="countriesData.js"></script>
    <script src="script.js"></script>
  </body>
</html>
Enter fullscreen mode Exit fullscreen mode

CSS CODE

@import url("https://fonts.googleapis.com/css2?family=Poppins:ital,wght@0,100;0,200;0,300;0,400;0,500;0,600;0,700;0,800;0,900;1,100;1,200;1,300;1,400;1,500;1,600;1,700;1,800;1,900&display=swap");

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

body {
  display: flex;
  height: 100vh;
  align-items: center;
  justify-content: center;
  background: rgb(185 26 88 / 45%);
}

::-webkit-scrollbar {
  width: 5px;
}

::-webkit-scrollbar-track {
  background: rgb(185 26 88 / 18%);
}

::-webkit-scrollbar-thumb {
  border-radius: 5px;
  background: rgb(185 26 88 / 45%);
}

::-webkit-scrollbar-thumb:hover {
  background: rgb(185 26 88 / 70%);
}

.mainContainer {
  width: 18rem;
  height: 30rem;
  padding: 1rem;
  overflow: hidden;
  position: relative;
  border-radius: 1.9rem;
  background: rgb(185 26 88);
}

.mainContainer .title {
  color: #ffffff;
  font-size: 1.6rem;
  margin-top: 0.5rem;
  text-align: center;
}

.fromCurrencyInput {
  display: flex;
  margin-top: 1rem;
  padding-top: 0.2rem;
  border-radius: 0.5rem;
  padding-bottom: 0.2rem;
  border: 2px dashed #ffffffc4;
  justify-content: space-between;
}

.fromCurrencyInput input {
  border: 0;
  width: 9.5rem;
  font-size: 1rem;
  color: #ffffff;
  text-align: right;
  padding-left: 0.5rem;
  padding-right: 0.5rem;
  background: transparent;
}

input[type="number"]::-webkit-inner-spin-button,
input[type="number"]::-webkit-outer-spin-button {
  -webkit-appearance: none;
  -moz-appearance: none;
  appearance: none;
  margin: 0;
}

.fromCurrencyInput input:focus {
  border: 0;
  outline: none;
}

.fromCurrencyInput input::placeholder {
  color: #ffffff;
  font-size: 1rem;
}

#flag {
  display: flex;
  height: 2.5rem;
  cursor: pointer;
  color: #ffffff;
  margin-left: 0.4rem;
  align-items: center;
  border-radius: 0.5rem;
  max-width: fit-content;
  justify-content: center;
  transition: background 0.2s ease-in-out;
}

#flag:hover {
  background: #ffffff29;
}

#flag img {
  height: 1.8rem;
  margin-left: 0.5rem;
  margin-right: 0.5rem;
}

#flag p {
  margin-right: 0.5rem;
}

.countriesModal {
  top: 0;
  left: 0;
  opacity: 0;
  width: 100vw;
  display: flex;
  height: 100vh;
  position: fixed;
  align-items: center;
  pointer-events: none;
  justify-content: center;
  backdrop-filter: blur(2px);
  transition: opacity .1s ease;
  background-color: #ffffff47;
}

.countriesModal.active {
  opacity: 1;
  pointer-events: all;
}

.modalBox {
  margin: 2rem;
  width: 35rem;
  height: 25rem;
  padding: 2rem;
  border-radius: 2.5rem;
  background: #ffffff;
  transform: translateY(-1.5rem);
  transition: transform .38s ease-in-out;
}

.countriesModal.active .modalBox {
  transform: translateY(0rem);
}

.searchBox {
  border: 0;
  width: 95%;
  display: flex;
  height: 2.5rem;
  overflow: hidden;
  margin-left: auto;
  margin-right: auto;
  border-radius: 2rem;
  align-items: center;
  justify-content: space-between;
  background: rgb(185 26 88 / 20%);
}

.searchBox input {
  border: 0;
  width: 90%;
  height: 100%;
  font-size: 1rem;
  padding-left: 1rem;
  color: rgb(185 26 88);
  background: transparent;
}

.searchBox input:focus {
  border: 0;
  outline: none;
}

.searchBox input::placeholder {
  color: rgb(185 26 88);
}

.searchBox i {
  font-size: 1.1rem;
  color: rgb(185 26 88);
  margin-right: 0.8rem;
}

.countriesBox {
  width: 100%;
  height: 17rem;
  display: grid;
  margin-top: 2rem;
  overflow-y: scroll;
  place-items: center;
  grid-template-columns: repeat(3, 1fr);
  grid-template-rows: repeat(3, 1fr);
}

.countriesBox .countryTag {
  display: flex;
  margin: 0.5rem;
  display: flex;
  height: 2.8rem;
  padding: 0 1rem;
  cursor: pointer;
  font-weight: 600;
  user-select: none;
  width: fit-content;
  border-radius: 5rem;
  align-items: center;
  color: rgb(185 26 88 / 70%);
  justify-content: space-between;
  transition: all 0.1s ease-in-out;
  border: 2px solid rgb(185 26 88 / 70%);
}

.countriesBox .countryTag:hover,
.countriesBox .countryTag.selected {
  background: rgb(185 26 88 / 25%);
  border: 2px solid rgb(255 197 203);
}

.countriesBox .countryTag.selected {
  outline-offset: 2px;
  outline: 2px solid rgb(185 26 88 / 70%);
}

.countriesBox .countryTag img {
  height: 1.8rem;
  margin-right: 0.5rem;
}

.notFound {
  width: 100%;
  height: 100%;
  display: none;
  margin-top: 2rem;
  text-align: center;
}

.notFound i {
  font-size: 5rem;
  margin-bottom: 0.5rem;
  color: rgb(185 26 88 / 70%);
}

.notFound h2 {
  font-size: clamp(1.5rem, 8vw, 2.5rem);
  color: rgb(185 26 88);
}

.notFound p {
  font-size: 0.9rem;
}

.convertedCurrencyBox .notFound i{
  font-size: 4rem;
}

.convertedCurrencyBox .notFound h2 {
  font-size: 1.9rem;
  color: rgb(185 26 88);
}

.convertedCurrencyBox .notFound p {
  font-size: 0.8rem;
  padding-left: 1rem;
  padding-right: 1rem;
}

.closeModal{
  border: 0;
  top: 1.5rem;
  right: 1.5rem;
  width: 2.5rem;
  height: 2.5rem;
  cursor: pointer;
  position: fixed;
  color: #ffffff;
  font-size: 1.2rem;
  border-radius: .5rem;
  background: rgb(185 26 88 / 55%);
}

.convertedCurrencyBox{
  left: 0;
  top: 10rem;
  width: 100%;
  height: 100%;
  overflow-y: scroll;
  position: absolute;
  background: #ffffff;
  border-top-left-radius: 1.5rem;
  transition: top .3s ease-in-out;
  border-top-right-radius: 1.5rem;
}

.convertedCurrencyBox.active{
  top: 0;
}

.convertedCurrencyBox > span{
  width: 4rem;
  cursor: grab;
  height: .4rem;
  display: block;
  margin-left: auto;
  margin-top: 0.8rem;
  margin-right: auto;
  border-radius: .5rem;
  background: #00000038;
}

.convertedListTag{
  margin-top: .5rem;
}

.convertedTag{
  display: flex;
  cursor: pointer;
  padding-top: .5rem;
  position: relative;
  align-items: center;
  padding-right: 1rem;
  padding-bottom: .8rem;
  justify-content: space-between;
  border-bottom: 1px solid #0000001f;
}

.convertedTag:first-child{
  margin-top: 1rem;
}

.convertedTag:last-child{
  margin-bottom: 10rem;
}

.convertedCurrencyBox.active .convertedTag:last-child{
  margin-bottom: 0rem;
}

.convertedTag #flag{
  cursor: auto;
  margin-left: 1rem;
}

.convertedTag #flag img{
  height: 2rem;
}

.convertedTag .convertedAmount{
  font-weight: 500;
  font-size: 1.5rem;
}

.convertedTag #flag p{
  color: #000;
  margin-left: .2rem;
}

.convertedTag .conversionData{
  bottom: 0;
  right: 1rem;
  font-size: .7rem;
  position: absolute;
}

.loadingScreen{
  width: 100%;
  height: 11rem;
  display: none;
  margin-top: 1rem;
  align-items: center;
  flex-direction: column;
  justify-content: center;
}

.loadingScreen i{
  font-size: 3rem;
  margin-bottom: .5rem;
}

#loading{
  top: 0;
  left: 0;
  z-index: 2;
  width: 100vw;
  display: flex;
  height: 100vh;
  position: fixed;
  align-items: center;
  justify-content: center;
  background: rgb(255 151 163);
}
Enter fullscreen mode Exit fullscreen mode

JavaScript CODE (countriesData.js)

// currency: Country
let countryList = {
    "AED" : "AE",
    "AFN" : "AF",
    "XCD" : "AG",
    "ALL" : "AL",
    "AMD" : "AM",
    "AOA" : "AO",
    "AQD" : "AQ",
    "ARS" : "AR",
    "AUD" : "AU",
    "AZN" : "AZ",
    "BAM" : "BA",
    "BBD" : "BB",
    "BDT" : "BD",
    "XOF" : "BE",
    "BGN" : "BG",
    "BHD" : "BH",
    "BIF" : "BI",
    "BMD" : "BM",
    "BND" : "BN",
    "BOB" : "BO",
    "BRL" : "BR",
    "BSD" : "BS",
    "BWP" : "BW",
    "BZD" : "BZ",
    "CAD" : "CA",
    "CDF" : "CD",
    "XAF" : "CF",
    "CHF" : "CH",
    "CLP" : "CL",
    "CNY" : "CN",
    "COP" : "CO",
    "CRC" : "CR",
    "CUP" : "CU",
    "CVE" : "CV",
    "CYP" : "CY",
    "CZK" : "CZ",
    "DJF" : "DJ",
    "DKK" : "DK",
    "DOP" : "DO",
    "DZD" : "DZ",
    "ECS" : "EC",
    "EEK" : "EE",
    "EGP" : "EG",
    "ETB" : "ET",
    "EUR" : "FR",
    "FJD" : "FJ",
    "FKP" : "FK",
    "GBP" : "GB",
    "GEL" : "GE",
    "GGP" : "GG",
    "GHS" : "GH",
    "GIP" : "GI",
    "GMD" : "GM",
    "GNF" : "GN",
    "GTQ" : "GT",
    "GYD" : "GY",
    "HKD" : "HK",
    "HNL" : "HN",
    "HRK" : "HR",
    "HTG" : "HT",
    "HUF" : "HU",
    "IDR" : "ID",
    "ILS" : "IL",
    "INR" : "IN",
    "IQD" : "IQ",
    "IRR" : "IR",
    "ISK" : "IS",
    "JMD" : "JM",
    "JOD" : "JO",
    "JPY" : "JP",
    "KES" : "KE",
    "KGS" : "KG",
    "KHR" : "KH",
    "KMF" : "KM",
    "KPW" : "KP",
    "KRW" : "KR",
    "KWD" : "KW",
    "KYD" : "KY",
    "KZT" : "KZ",
    "LAK" : "LA",
    "LBP" : "LB",
    "LKR" : "LK",
    "LRD" : "LR",
    "LSL" : "LS",
    "LTL" : "LT",
    "LVL" : "LV",
    "LYD" : "LY",
    "MAD" : "MA",
    "MDL" : "MD",
    "MGA" : "MG",
    "MKD" : "MK",
    "MMK" : "MM",
    "MNT" : "MN",
    "MOP" : "MO",
    "MUR" : "MU",
    "MVR" : "MV",
    "MWK" : "MW",
    "MXN" : "MX",
    "MYR" : "MY",
    "MZN" : "MZ",
    "NAD" : "NA",
    "XPF" : "NC",
    "NGN" : "NG",
    "NIO" : "NI",
    "NPR" : "NP",
    "NZD" : "NZ",
    "OMR" : "OM",
    "PAB" : "PA",
    "PEN" : "PE",
    "PGK" : "PG",
    "PHP" : "PH",
    "PKR" : "PK",
    "PLN" : "PL",
    "PYG" : "PY",
    "QAR" : "QA",
    "RON" : "RO",
    "RSD" : "RS",
    "RUB" : "RU",
    "RWF" : "RW",
    "SAR" : "SA",
    "SBD" : "SB",
    "SCR" : "SC",
    "SDG" : "SD",
    "SEK" : "SE",
    "SGD" : "SG",
    "SKK" : "SK",
    "SLL" : "SL",
    "SOS" : "SO",
    "SRD" : "SR",
    "STD" : "ST",
    "SVC" : "SV",
    "SYP" : "SY",
    "SZL" : "SZ",
    "THB" : "TH",
    "TJS" : "TJ",
    "TMT" : "TM",
    "TND" : "TN",
    "TOP" : "TO",
    "TRY" : "TR",
    "TTD" : "TT",
    "TWD" : "TW",
    "TZS" : "TZ",
    "UAH" : "UA",
    "UGX" : "UG",
    "USD" : "US",
    "UYU" : "UY",
    "UZS" : "UZ",
    "VEF" : "VE",
    "VND" : "VN",
    "VUV" : "VU",
    "YER" : "YE",
    "ZAR" : "ZA",
    "ZMK" : "ZM",
    "ZWD" : "ZW"
}
Enter fullscreen mode Exit fullscreen mode

JavaScript CODE (script.js)

const searchBox = document.querySelector('.searchBox')
convertedFilter = document.querySelector('#convertedFilter')
countriesBox = document.querySelector('.countriesBox')
notFound = document.querySelector('.notFound')
clearSearch = document.querySelector('.clearSearch')
closeModal = document.querySelector('.closeModal')
countryFlag = document.querySelector('#flag')
countriesModal = document.querySelector('.countriesModal')
selectedCurrency = document.querySelector('#selectedCurrency')
convertedListTag = document.querySelector('.convertedListTag')
conversionAmount = document.querySelector('#conversionAmount')
loadingScreen = document.querySelector('.loadingScreen')
convertedCurrencyBox = document.querySelector('.convertedCurrencyBox')
searchInp = document.querySelector('#searchInp')
APIKey = '60ec6c2270a80ca6c3a356d4'
fetchedData = []

const getDataFromAPI = () => {
    let URL = `https://v6.exchangerate-api.com/v6/${APIKey}/latest/${selectedCurrency.value}`
    fetch(URL).then(response => response.json()).then((result) => {
        fetchedData.push(result)
    })
}

const getConversion = () => {
    convertedListTag.innerHTML = ""
    convertedListTag.style.display = 'none'
    loadingScreen.style.display = 'flex'
    let conversionVal = conversionAmount.value
    setTimeout(() => {
        let rateData = fetchedData
        for (currencyCode in countryList) {
            convertedListTag.innerHTML += `<div class="convertedTag">
            <span id="flag">
            <img src="https://flagcdn.com/40x30/${countryList[currencyCode].toLocaleLowerCase()}.png" alt="">
            <p data-code="${currencyCode}">${currencyCode}</p>
            </span>
            <div class="convertedAmount">
            ${((rateData[0].conversion_rates[selectedCurrency.value] / rateData[0].conversion_rates[currencyCode]) * conversionVal).toFixed(2)}
            </div>
            <div class="conversionData">1 ${selectedCurrency.value} = ${rateData[0].conversion_rates[currencyCode]} ${currencyCode}</div>
            </div>`
        }
        loadingScreen.style.display = 'none'
        convertedListTag.style.display = 'block'
    }, 2000)
}
getDataFromAPI()
getConversion()

new Promise((resolve, reject) => {
    for (currencyCode in countryList) {
        countriesBox.innerHTML += `<div class="countryTag">
        <img src="https://flagcdn.com/40x30/${countryList[currencyCode].toLocaleLowerCase()}.png" alt="">
        <p data-code="${currencyCode}">${currencyCode}</p>
        </div>`
    }
    resolve(true)
}).then(() => {
    console.log("done")
})

conversionAmount.addEventListener("keyup", () => {
    let conversionVal = conversionAmount.value
    if (conversionVal == "" || conversionVal == "0") {
        conversionAmount.value = '1'
    }
})


const filterData = () => {
    notFound.style.display = 'none'
    countriesBox.style.display = 'grid'
    let searchedVal = searchInp.value.toLowerCase()
    searchedCurrency = []

    searchedCurrency = Object.keys(countryList).filter(data => {
        return data.toLocaleLowerCase().startsWith(searchedVal)
    }).map(data => {
        countriesBox.innerHTML = ''
        return `<div class="countryTag">
        <img src="https://flagcdn.com/40x30/${countryList[data].toLocaleLowerCase()}.png" alt="">
        <p data-code="${data}">${data}</p>
        </div>`
    }).join('')

    if (searchedCurrency != "") {
        countriesBox.innerHTML += searchedCurrency
    } else {
        countriesBox.innerHTML = ''
        countriesBox.style.display = 'none'
        notFound.style.display = 'block'
    }
}
filterData()

const filterConvertedData = () => {
    notFound.style.display = 'none'
    convertedListTag.style.display = 'block'
    let searchedVal = convertedFilter.value.toLowerCase()
    searchedCurrency = []
    searchedCurrency = Object.keys(countryList).filter(data => {
        return data.toLocaleLowerCase().startsWith(searchedVal);
    }).map(data => {
        convertedListTag.innerHTML = ''
        let conversionVal = conversionAmount.value
        let convertedRates = fetchedData[0].conversion_rates
        return `<div class="convertedTag">
                <span id="flag">
                <img src="https://flagcdn.com/40x30/${countryList[data].toLocaleLowerCase()}.png" alt="">
                <p data-code="${data}">${data}</p>
                </span>
                <div class="convertedAmount">
                ${(convertedRates[data] * conversionVal).toFixed(2)}
                </div>
                <div class="conversionData">1 ${selectedCurrency.value} = ${convertedRates[data]} ${data}</div>
                </div>`;
    }).join('')
    if (searchedCurrency != "") {
        convertedListTag.innerHTML += searchedCurrency
    } else {
        convertedListTag.innerHTML = ''
        convertedListTag.style.display = 'none'
        notFound.style.display = 'block'
    }
}

window.addEventListener("click", (e) => {
    if (e.target.classList.contains("countryTag")) {
        let countryTag = document.querySelectorAll('.countryTag')
        countryTag.forEach(tag => {
            tag.classList.remove("selected")
        })
        e.target.classList.add('selected')
        selectedCurrency.value = e.target.querySelector('p').innerText
        countriesModal.classList.remove("active")
        let currentTag = document.querySelector(`[data-code="${selectedCurrency.value}"]`)
        code = currentTag.getAttribute('data-code')
        countryFlag.querySelector('img').setAttribute('src', `https://flagcdn.com/40x30/${countryList[code].toLocaleLowerCase()}.png`)
        countryFlag.querySelector('p').innerText = selectedCurrency.value
        getConversion()

    } else if (e.target.parentElement.classList.contains("countryTag")) {
        let countryTag = document.querySelectorAll('.countryTag')
        countryTag.forEach(tag => {
            tag.classList.remove("selected")
        })
        e.target.parentElement.classList.add('selected')
        selectedCurrency.value = e.target.parentElement.querySelector('p').innerText
        countriesModal.classList.add("active")
        countriesModal.classList.remove("active")
        let currentTag = document.querySelector(`[data-code="${selectedCurrency.value}"]`)
        code = currentTag.getAttribute('data-code')
        countryFlag.querySelector('img').setAttribute('src', `https://flagcdn.com/40x30/${countryList[code].toLocaleLowerCase()}.png`)
        countryFlag.querySelector('p').innerText = selectedCurrency.value
        getConversion()
    }
})

countryFlag.addEventListener("click", () => {
    countriesModal.classList.add("active")
    let countryTag = document.querySelectorAll(`[data-code="${selectedCurrency.value}"]`)
    countryTag[0].parentElement.classList.add('selected')
})

closeModal.addEventListener("click", () => {
    countriesModal.classList.remove("active")
})

convertedFilter.addEventListener("keyup", () => {
    convertedListTag.innerHTML = ""
    convertedCurrencyBox.classList.add('active')
    if (convertedFilter.value == "") {
        convertedCurrencyBox.classList.remove('active')
    }
    filterConvertedData()
})

window.onload = () => {
    document.getElementById("loading").style.display = "none"
}
Enter fullscreen mode Exit fullscreen mode

Top comments (0)