<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>Shopping list</title>
<link rel="stylesheet" href="/style.css">
<script src="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.2.0/js/all.min.js"
integrity="sha512-naukR7I+Nk6gp7p5TMA4ycgfxaZBJ7MO5iC3Fp6ySQyKFHOGfpkSZkYVWV5R7u7cfAicxanwYQ5D1e17EfJcMA=="
crossorigin="anonymous" referrerpolicy="no-referrer"></script>
</head>
<body>
<div class="container">
<h2>My Shopping list 📃 </h2>
<input name="items" type="text" id="search" placeholder="Enter the items">
<div class="results"></div>
<div class="my-items"></div>
</div>
</body>
</html>
body {
font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto,
Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
}
.container {
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
margin: 1rem 0;
}
input[type='text'] {
padding: 0.4rem;
width: 15%;
outline: none;
}
.results {
border: 1px solid black;
width: 16%;
display: none;
position: relative;
background-color: #f0f0f0;
z-index: 1;
}
.show-results {
display: block;
}
.results p {
margin: 0.5rem;
padding: 0.2rem;
cursor: pointer;
}
.flex {
display: flex;
gap: 0.5rem;
align-items: center;
}
.outer-flex {
display: flex;
gap: 4rem;
align-items: center;
justify-content: space-between;
}
.my-items {
position: absolute;
top: 8rem;
}
.fa-check {
border: 1px solid black;
border-radius: 100px;
}
button {
cursor: pointer;
background: transparent;
border: 0;
font-size: 1rem;
}
.strike {
text-decoration: line-through;
color: gray;
}
.check-gray {
border: 1px solid gray;
color: gray;
}
.close-gray {
color: gray;
}
.no-strike {
text-decoration: none;
color: black;
}
/*
* https://frontendeval.com/questions/shopping-list
*
* Create a shopping list app with autocomplete item entry
*/
const search = document.querySelector('#search');
const results = document.querySelector('.results');
const itemsUI = document.querySelector('.my-items');
let myItems = [];
function debounce(callbackFn, time) {
let timeoutId;
return function (...args) {
if (timeoutId) clearInterval(timeoutId);
timeoutId = setTimeout(() => {
callbackFn(args);
}, time);
};
}
const fetchItemsOnType = debounce(fetchItems, 500);
async function fetchItems(item) {
const res = await fetch(`https://api.frontendeval.com/fake/food/${item}`);
const data = await res.json();
showSearchResults(data);
}
function showSearchResults(data) {
data?.length >= 1
? results.classList.add('show-results')
: results.classList.remove('show-results');
const output = data?.map((item) => {
return `<p onclick="handleItems(this)">${item}</p>`;
});
results.innerHTML = output.join('');
}
function handleItems(e) {
myItems.push({ item: e.textContent, id: Math.random() });
results.classList.remove('show-results');
search.value = '';
showItems(myItems);
}
function showItems(items) {
if (items.length === 0) {
itemsUI.innerHTML = '<p>No items in the list 📪 </p>';
} else {
const output = items?.map((item, i) => {
return `
<div class="outer-flex">
<div class="flex">
<button id=${item.id} onclick="toggleCheck(this)">
<i class="${
item?.isDone ? 'check-gray fa fa-check ' : 'fa fa-check '
}" aria-hidden="true"></i>
</button>
<p class="${item?.isDone ? 'strike' : 'no-strike'}">${
item.item
}</p>
</div>
<button onclick="removeItem(${i})">
<i class="${
item?.isDone ? 'close-gray fa fa-close' : 'fa fa-close'
}" aria-hidden="true"></i>
</button>
</div>
`;
});
itemsUI.innerHTML = output.join('');
}
}
function removeItem(id) {
myItems?.splice(id, 1);
showItems(myItems);
}
function toggleCheck(e) {
const newItem = myItems?.map((item) => {
if (item.id === Number(e.id)) {
item.isDone ? (item.isDone = false) : (item.isDone = true);
}
return item;
});
console.log(newItem);
showItems(newItem);
}
search.addEventListener('input', () => {
fetchItemsOnType(search.value);
});
View Compiled
This Pen doesn't use any external CSS resources.
This Pen doesn't use any external JavaScript resources.