Creating a country search feature using HTML and JavaScript involves creating a simple user interface where users can input a search term to find a country. Here’s a step-by-step guide to building this feature:
HTML
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Country Search & Filter App</title>
<link
rel="stylesheet"
href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:opsz,wght,FILL,GRAD@48,400,0,0"
/>
<link rel="stylesheet" href="style.css" />
<script src="script.js" defer></script>
</head>
<body>
<div class="container">
<!-- search -->
<div class="search">
<input type="search" id="search" placeholder="Search country" />
</div>
<!-- filter -->
<div class="filter">
<button>All</button>
<div class="options"></div>
</div>
<!-- grid -->
<div class="grid-container"></div>
<!-- result -->
<div class="no-result">No result found.</div>
<!-- modal -->
<div class="modal">
<div class="modal-content">
<button data-dismiss>
<span class="material-symbols-outlined">close</span>
</button>
<figure>
<img
src="https://placehold.co/600x400/purple/white?text=FLAG"
alt=""
/>
<figcaption>Country Name</figcaption>
</figure>
</div>
</div>
</div>
</body>
</html>
CSS (styles.css)
@import url("https://fonts.googleapis.com/css2?family=Poppins:wght@400;500;600&display=swap");
:root {
--color0: goldenrod;
--color1: #082032;
--color2: #2c394b;
--color3: #9db2bf;
--color4: #dde6ed;
}
* {
font-family: "Poppins", sans-serif;
padding: 0;
margin: 0;
box-sizing: border-box;
}
body {
background: var(--color1);
color: var(--color3);
}
img {
width: 100%;
height: auto;
display: block;
}
/* layout */
.container {
width: 90%;
max-width: 1200px;
margin: 2rem auto 1rem;
display: flex;
flex-wrap: wrap;
justify-content: space-between;
gap: 2rem;
}
.search,
.filter {
flex-basis: calc(50% - 1rem);
}
.grid-container {
flex-basis: 100%;
}
/* search */
.search input {
width: 100%;
max-width: 400px;
padding: 10px;
padding-left: 20px;
border: none;
border-radius: 5px;
background: var(--color2);
color: var(--color4);
outline-color: var(--color0);
box-shadow: 0 5px 10px rgba(0, 0, 0, 0.25);
}
.search input::placeholder {
color: var(--color3);
}
.search input::-webkit-search-cancel-button {
-webkit-appearance: none;
display: inline-block;
width: 12px;
height: 12px;
margin-left: 10px;
background: linear-gradient(
45deg,
rgba(0, 0, 0, 0) 0%,
rgba(0, 0, 0, 0) 43%,
var(--color0) 45%,
var(--color0) 55%,
rgba(0, 0, 0, 0) 57%,
rgba(0, 0, 0, 0) 100%
),
linear-gradient(
135deg,
transparent 0%,
transparent 43%,
var(--color0) 45%,
var(--color0) 55%,
transparent 57%,
transparent 100%
);
}
/* filter */
.filter {
width: 100%;
max-width: 250px;
border-radius: 5px;
box-shadow: 0 5px 10px rgba(0, 0, 0, 0.25);
position: relative;
}
.filter button {
width: 100%;
height: 100%;
cursor: pointer;
border: none;
border-radius: inherit;
background: var(--color2);
color: var(--color3);
}
.filter button:hover {
color: var(--color4);
}
.filter button::after {
font-family: "Material Symbols Outlined";
content: "expand_more";
width: 24px;
height: 24px;
font-size: 24px;
line-height: 1;
position: absolute;
top: 50%;
right: 10px;
transform: translateY(-50%);
}
.filter.open button::after {
content: "expand_less";
}
.filter .options {
position: absolute;
top: calc(100% + 2px);
z-index: 99;
background: var(--color2);
width: 100%;
border-radius: inherit;
box-shadow: inherit;
overflow: hidden;
opacity: 0;
transform: scaleY(0);
transform-origin: top;
transition: 0.2s;
}
.filter.open .options {
opacity: 1;
transform: scaleY(1);
}
.filter .options div {
padding: 0.5rem 1rem;
cursor: pointer;
}
.filter .options div:hover {
background: var(--color1);
color: var(--color4);
}
/* grid */
.grid-container {
display: grid;
grid-template-columns: auto;
gap: 2rem;
}
.grid-container + .no-result {
display: none;
width: 100%;
text-align: center;
}
.grid-container:empty + .no-result {
display: block;
}
.grid-item {
background: var(--color2);
border-radius: 10px;
box-shadow: 0 5px 10px rgba(0, 0, 0, 0.25);
overflow: hidden;
}
.grid-item a {
display: block;
}
.grid-item img {
width: 100%;
height: 200px;
object-fit: cover;
background: #ddd;
}
.grid-item div {
padding: 1.5rem;
}
.grid-item h3 {
font-size: 1rem;
font-weight: 600;
text-transform: uppercase;
color: #fff;
text-shadow: 0 1px 3px rgba(0, 0, 0, 0.25);
margin-bottom: 0.5rem;
}
.grid-item p {
margin-bottom: 0.5rem;
}
.grid-item label {
display: block;
color: var(--color0);
font-size: 0.85rem;
font-weight: 600;
text-transform: uppercase;
text-shadow: 0 1px 3px rgba(0, 0, 0, 0.25);
}
@media (min-width: 768px) {
.grid-container {
grid-template-columns: repeat(2, 1fr);
}
}
@media (min-width: 1024px) {
.grid-container {
grid-template-columns: repeat(3, 1fr);
}
}
/* modal */
.modal {
position: fixed;
top: 0;
left: 0;
z-index: 1024;
width: 100%;
height: 100%;
background: var(--color1);
display: none;
animation-name: fadeIn;
animation-duration: 0.3s;
}
.modal.show {
display: grid;
place-items: center;
}
.modal img {
max-width: 80%;
max-height: 80vh;
margin: auto;
filter: drop-shadow(0 5px 10px rgba(0, 0, 0, 0.25));
}
.modal figcaption {
margin-top: 1.5rem;
text-align: center;
font-size: 0.9rem;
font-weight: 500;
text-transform: uppercase;
color: var(--color0);
}
.modal button[data-dismiss] {
position: absolute;
top: 1.5rem;
right: 1.5rem;
width: 50px;
height: 50px;
border: none;
border-radius: 50%;
cursor: pointer;
background: transparent;
color: var(--color3);
}
.modal button[data-dismiss]:hover {
color: var(--color4);
}
.modal button[data-dismiss] span {
font-size: 42px;
}
@keyframes fadeIn {
from {
opacity: 0;
}
to {
opacity: 1;
}
}
JavaScript (script.js)
const API_URL = "https://restcountries.com/v3.1/all";
const FIELDS =
"name,cca3,currencies,capital,region,population,continents,flags";
const filter = document.querySelector(".filter");
const gridContainer = document.querySelector(".grid-container");
const searchInput = document.getElementById("search");
const modal = document.querySelector(".modal");
//* variables
let countries = [];
let filteredCountries = [];
let search = null;
let continent = "All";
//* api functions
const fetchData = async (apiURL) => {
try {
const response = await fetch(apiURL);
return await response.json();
} catch (error) {
console.error("Error fetching data", error);
throw error;
}
};
const getCountries = async () => {
try {
const apiURL = `${API_URL}?fields=${FIELDS}`;
const data = await fetchData(apiURL);
return data.sort((a, b) => {
const nameA = a.name.common.toLowerCase();
const nameB = b.name.common.toLowerCase();
if (nameA < nameB) return -1;
if (nameA > nameB) return 1;
return 0;
});
} catch (error) {
return [];
}
};
//* render functions
const renderCountryList = () => {
// use fragment to temporary build the list
const fragment = document.createDocumentFragment();
filteredCountries.forEach((country) => {
// destructure
const {
name,
flags,
cca3,
capital,
population,
currencies,
region,
continents,
} = country;
// change currencies from object to array
const currencyList = Object.keys(currencies).map((code) => {
const { name } = currencies[code];
return `${name} (${code})`;
});
const gridItem = document.createElement("div");
gridItem.classList.add("grid-item");
gridItem.addEventListener("click", onGridItemClick);
gridItem.innerHTML = `
<a href="${flags.svg}">
<img
src="${flags.svg}"
alt="${name.common} (${cca3})"
/>
</a>
<div>
<h3>${name.common}</h3>
<p>
<label>Capital</label>
${capital.join(", ")}
</p>
<p>
<label>Population</label>
${population.toLocaleString("en")}
</p>
<p>
<label>Currencies</label>
${currencyList.join(", ")}
</p>
<p>
<label>Region</label>
${region}
</p>
<p>
<label>Continent</label>
${continents.join(", ")}
</p>
</div>`;
fragment.appendChild(gridItem);
});
// add the virtual list to DOM
gridContainer.replaceChildren(fragment);
};
const renderFilterList = () => {
const optionList = filter.querySelector(".options");
// get the continents from countries and sort alphabetically
let continents = countries.flatMap((c) => c.continents).sort();
// prepend 'All' and remove duplicates
continents = ["All", ...new Set(continents)];
continents.forEach((continent) => {
const option = document.createElement("div");
option.textContent = continent;
option.addEventListener("click", onFilterOptionClick);
optionList.appendChild(option);
});
};
//* event handlers
const loadCountries = async () => {
countries = await getCountries();
filteredCountries = countries;
renderCountryList();
renderFilterList();
};
const handleEscapeKey = (e) => {
if (e.key === "Escape") {
modal.classList.remove("show");
}
};
const applyFilters = () => {
filteredCountries = countries;
if (search !== null) {
filteredCountries = filteredCountries.filter((country) => {
// name, capital, code
const name = country.name.common.toLowerCase();
const capital = country.capital.join(", ").toLowerCase();
const code = country.cca3.toLowerCase();
return (
name.includes(search) ||
capital.includes(search) ||
code.includes(search)
);
});
}
if (continent !== "All") {
filteredCountries = filteredCountries.filter((country) => {
return country.continents.includes(continent);
});
}
};
const onGridItemClick = (e) => {
e.preventDefault();
if (e.target.tagName === "IMG") {
const { src, alt } = e.target;
const img = modal.querySelector("img");
const dismiss = modal.querySelector("[data-dismiss]");
const caption = modal.querySelector("figcaption");
img.src = src;
img.alt = alt;
caption.textContent = alt;
dismiss.addEventListener("click", onDismissClick);
modal.classList.add("show");
}
};
const onDismissClick = () => {
modal.classList.remove("show");
};
const onFilterOptionClick = (e) => {
continent = e.target.textContent;
// Change the selected filter
const button = filter.querySelector("button");
button.textContent = continent;
// apply the search filter to filtered countries
applyFilters();
// re-render country list
renderCountryList();
};
const onFilterButtonClick = () => {
filter.classList.toggle("open");
};
const onSearchInput = () => {
search = searchInput.value.trim().toLowerCase();
// apply the search keyword to filtered countries
applyFilters();
// re-render country list
renderCountryList();
};
//* event listeners
document.addEventListener("DOMContentLoaded", loadCountries);
document.addEventListener("keydown", handleEscapeKey);
filter.addEventListener("click", onFilterButtonClick);
searchInput.addEventListener("input", onSearchInput);
Explanation
- HTML Structure:
- A simple HTML structure includes a container with an input field for the search term and an unordered list to display the filtered country names.
- CSS Styling:
- The CSS styles the container and input elements for a clean, centered look.
- The unordered list (
ul) and list items (li) are styled to remove default styling and add custom borders and padding.
- JavaScript Functionality:
- A list of country names is stored in an array.
- An event listener is added to the search input to filter the country names based on the user input.
- The
inputevent triggers a function that filters the country array and updates the unordered list (ul) with the filtered results.
How to Run
- Create three files:
index.html,styles.css, andscript.js. - Copy the respective code blocks into these files.
- Open the
index.htmlfile in a web browser to see the country search feature in action.
This simple implementation allows users to type in a search term and see a list of matching country names in real-time.


+91 7905834592
Enquiry Now
piyushmnm@gmail.com
piyush.gupta384
Reviews
There are no reviews yet. Be the first one to write one.