MockShop Project
→ Kurulum
ilk olarak vite ile typescript projesi olustururuz ve gereksiz dosyalari temizleriz:
👉Ilgili github kod linki: https://github.com/snahmd/mockshop/tree/3d2bf168ec89b5985cc3895f11f04b4551094120
npm create vite@latest
npm install
npm run dev
bu komutlar ile projeyi olusturuyoruz.
→API bilgisi
ürünlerinlerin listelendigi bir api’dir. Projemizde bu api ile html dom elementlerini kullanacagiz.
Bu api bize json data’da ne dönüyor, projemizi ona göre düzenleyecegiz.
Json datayi buraya kopyalayacagiz, bize gerekli olan tpye, interface, enum vs hangi türde verilere ihtiyacimiz varsa onlari veriyor.
→Interfaces Dosyasi
gerekli olan type bilgilerini ayni isimlerle interface olarak olustururuz.
type Rating = {
rate: number;
count: number;
}
interface IProduct {
id: number;
title: string;
price: number;
category: string;
image: string;
rating: Rating;
}
export default IProduct;
burada Product’larimizin icerisinde Rating obje olarak gelir, bundan dolayi ona da ayri bir type verip IProduct icerisinde tanimlariz.
👉Ilgili github kod linki: https://github.com/snahmd/mockshop/tree/a701656654974b6a6ae5311bb871872cafb263fc
→ Index.html dosyasini yazmak
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/src/img/mockshop.png" />
<script type="module" src="src/main.ts" defer></script>
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>MockShop</title>
</head>
<body>
<h1>MockShop</h1>
<section>
<input id="itemTitle" type="text" />
<select name="" id="sortBy">
<option value="0">Sort By</option>
<option value="1">⬆️ Lowest Price</option>
<option value="2">⬇️ Highest Price</option>
<option value="3">⭐️ Best Rating</option>
</select>
<div class="filterCategories">
<p>Filter by Categories:</p>
<button id="electronics">Electronics</button>
<button id="jewelery">Jewelery</button>
<button id="mens">Men's clothing</button>
<button id="womens">Women's clothing</button>
</div>
</section>
<section id="itemsSection">
<div id="itemContainer">
<img id="img" src="" alt="" />
<h2 id="productName"></h2>
<div class="priceCard">
<h3 id="price"></h3>
<button id="addToCart">Add to cart</button>
</div>
</div>
</section>
</body>
</html>
Bu HTML kodu, MockShop adlı bir e-ticaret web sitesinin ana sayfasını oluşturur. Sayfa şunları içerir:
- Başlık ve favicon
- Ürün arama, sıralama ve kategori filtreleme seçenekleri
- Ürün listesi için bir bölüm (resim, isim, fiyat ve sepete ekle butonu içeren)
👉İlgili github kod linki: https://github.com/snahmd/mockshop/tree/a1e2115acffde5b2f190d10b12171988dc77adab
→ API’dan gelen ürünlerin Html’de gösterilmesi:
ilk olarak interface’mizi cagiralim ve index.html’de bulunan elementleri secelim.
import IProduct from "./interfaces/IProduct";
const titleInput = document.getElementById("itemTitle") as HTMLInputElement;
const sortByButton = document.getElementById("sortBy") as HTMLButtonElement;
const filterElectronicsButton = document.getElementById("electronics") as HTMLButtonElement;
const filterJewelerysButton = document.getElementById("jewelery") as HTMLButtonElement;
const filterMensButton = document.getElementById("mens") as HTMLButtonElement;
const filterWomensButton = document.getElementById("womens") as HTMLButtonElement;
const productsContainer = document.getElementById("itemsSection") as HTMLDivElement;
fetchAndDisplay();
const BASE_URL = "<https://fakestoreapi.com>";
const PRODUCT_URL = `${BASE_URL}/products`;
function fetchAndDisplay(){
fetch(PRODUCT_URL)
.then((response: Response) => {
if(!response.ok){
throw new Error("Failed to fetch Data");
}
return response.json();
})
.then((data: IProduct[]) => {
data.forEach((product: IProduct) => {
console.log(product.image)
const cardContainer = document.querySelector("#itemContainer")!.cloneNode(true) as HTMLElement;
(cardContainer.querySelector("#img") as HTMLImageElement).src = product.image;
(cardContainer.querySelector("#productName") as HTMLElement).textContent = product.title;
(cardContainer.querySelector("#price") as HTMLElement).textContent = product.price.toString();
productsContainer.appendChild(cardContainer);
})
})
}
- Öncelikle "https://fakestoreapi.com/products"; adresinden ürün verilerini çeker.
- Eğer veri çekme işlemi başarısız olursa bir hata fırlatır.
- Başarılı olursa, gelen veriyi JSON formatında işler.
- Her bir ürün için şu işlemleri yapar:
- HTML'deki ürün şablonunu klonlar.
- Ürün resmini, adını ve fiyatını ilgili HTML elementlerine yerleştirir.
- Oluşturulan ürün kartını ana ürün konteynırına ekler.
Bu fonksiyon, web sayfasına dinamik olarak ürün kartları ekleyerek kullanıcıya ürünleri gösterir.
.cloneNode() JavaScript'te bir DOM (Document Object Model) metodudur. Bu metod, bir DOM düğümünün (node) kopyasını oluşturmak için kullanılır. Yukarıdaki kodda, bu metod şu amaçla kullanılmıştır:
Mevcut bir HTML elementinin (muhtemelen bir ürün kartı şablonu) kopyasını oluşturmak
Bu kopyayı, her bir ürün için özelleştirmek ve sayfaya eklemek
Bu yaklaşım, tekrar eden yapıları (örneğin, birden çok ürün kartı) dinamik olarak oluşturmak için etkili bir yöntemdir. Her ürün için yeni bir kart oluşturmak yerine, bir şablonu kopyalayıp içeriğini değiştirmek daha verimli olabilir.
2.Yöntem; cloneNode() alternatifi ise:
function fetchAndDisplay() {
fetch(PRODUCT_URL)
.then((response) => {
if (!response.ok) {
throw new Error("Failed to fetch Data");
}
return response.json();
})
.then((data: IProduct[]) => {
data.forEach((product: IProduct) => {
const cardContainer = document.createElement('div');
cardContainer.id = 'itemContainer';
const img = document.createElement('img');
img.id = 'img';
img.src = product.image;
cardContainer.appendChild(img);
const productName = document.createElement('h2');
productName.id = 'productName';
productName.textContent = product.title;
cardContainer.appendChild(productName);
const priceCard = document.createElement('div');
priceCard.className = 'priceCard';
const price = document.createElement('h3');
price.id = 'price';
price.textContent = product.price.toString();
priceCard.appendChild(price);
const addToCartButton = document.createElement('button');
addToCartButton.id = 'addToCart';
addToCartButton.textContent = 'Add to cart';
priceCard.appendChild(addToCartButton);
cardContainer.appendChild(priceCard);
productsContainer.appendChild(cardContainer);
});
});
}
Bu yaklaşımda, her bir ürün için yeni DOM elementleri oluşturuyoruz ve bunları birbirine bağlayarak ürün kartını oluşturuyoruz. Bu yöntem, cloneNode() kullanmadan benzer bir sonuç elde etmemizi sağlar.
Bu yöntemin avantajı, her bir element için daha fazla kontrol sağlamasıdır. Dezavantajı ise, özellikle çok sayıda ürün için, cloneNode() yöntemine göre biraz daha yavaş olabilmesidir.
→Suan ürünler html sayfasina basiliyor. Biz simdi cesitli siralama ve filtreleme islemleri yapacagiz, bunun icin html’de basilan degerleri silcegimiz bir clear fonsiyonu yazmamiz gerekiyor, bunu setAtribute ile fetchAndDisplay fonksiyonuna göndermemiz gerekir.
#...
function fetchAndDisplay(){
fetch(PRODUCT_URL)
.then((response: Response) => {
#...
return response.json();
})
.then((data: IProduct[]) => {
data.forEach((product: IProduct) => {
#...
cardContainer.setAttribute("id", "delete");
productsContainer.appendChild(cardContainer);
#...
})
})
}
function clearItemCards() {
const itemCard = document.querySelectorAll("#delete");
itemCard.forEach((itemCard) => itemCard.remove());
}
Bu son kod parçacığı iki önemli işlevi yerine getirir:
- fetchAndDisplay() fonksiyonunda, her ürün kartına "delete" ID'si atanır. Bu, daha sonra bu kartları kolayca seçip silmek için kullanılacaktır.
- clearItemCards() fonksiyonu, sayfadaki tüm ürün kartlarını temizler. Bu fonksiyon:
- "delete" ID'sine sahip tüm elementleri seçer
- Bu elementleri döngüyle dolaşır ve her birini sayfadan kaldırır
Bu kod, ürünleri yeniden sıralamak veya filtrelemek istediğinizde, mevcut ürün kartlarını temizleyip yeni sıralanmış veya filtrelenmiş ürünleri göstermek için kullanışlıdır.
👉İlgili github kod linki: https://github.com/snahmd/mockshop/tree/fb0ae87e68e3641ffbdee583a445c28154c839fa
Product’lari fonksiyonda cagirmak:
Öncesinde bütün productlari ekranda basiyorduk, her clear yaptigimizda productlari tekrar olusturmamiz lazim. DRY prensibince product’lar icin bir tane printProduct fonksiyonu olusturacagiz,tekrar ürün listeleme yapmak istedigimizde onu cagiracagiz.
function fetchAndDisplay() {
fetch("<https://fakestoreapi.com/products>")
.then((response: Response) => {
if (!response.ok) {
throw new Error("Failed to fetch Data");
}
return response.json();
})
.then((data: IProduct[]) => {
data.forEach((product: IProduct) => {
printProduct(product);
});
});
}
function printProduct(product: IProduct) {
console.log(product.image);
const cardContainer = document
.querySelector("#itemContainer")!
.cloneNode(true) as HTMLElement;
(cardContainer.querySelector("#img") as HTMLImageElement).src = product.image;
(cardContainer.querySelector("#productName") as HTMLElement).textContent =
product.title;
(cardContainer.querySelector("#price") as HTMLElement).textContent =
product.price.toString();
cardContainer.setAttribute("id", "delete");
productsContainer.appendChild(cardContainer);
}
forEach döngüsünü yeniden düzenledik fonksiyonu cagirdik.
👉İlgili github kod linki: https://github.com/snahmd/mockshop/tree/a6fe477cda86ec65da590ccbc8af999e83e4d2e7
Sort Mantik Kismi
sortByButton.addEventListener("change", () => {
clearItemCards();
fetch("<https://fakestoreapi.com/products>")
.then((response: Response) => {
if (!response.ok) {
throw new Error("Failed to fetch Data");
}
return response.json();
})
.then((data: IProduct[]) => {
if (Number(sortByButton.value) === 1) {
const sortedByLowestPriceItems: IProduct[] = data.sort(
(a, b) => a.price - b.price
);
sortedByLowestPriceItems.forEach((product: IProduct) => {
printProduct(product);
});
} else if (Number(sortByButton.value) === 2) {
const sortedByHighestPriceItems: IProduct[] = data.sort(
(a, b) => b.price - a.price
);
sortedByHighestPriceItems.forEach((product: IProduct) => {
printProduct(product);
});
} else if (Number(sortByButton.value) === 3) {
const sortedByBestRatingItems: IProduct[] = data.sort(
(a, b) => a.rating.rate - b.rating.rate
);
sortedByBestRatingItems.forEach((product: IProduct) => {
printProduct(product);
});
}
});
});
1. Event Listener Tanımı
Kod, 'sortBySelected' adlı bir HTML elementine (muhtemelen bir dropdown menü) bir 'change' event listener'ı ekler. Bu, kullanıcı sıralama seçeneğini her değiştirdiğinde tetiklenir.
2. Mevcut Ürünlerin Temizlenmesi
'clearItemCards()' fonksiyonu çağrılır. Bu, mevcut ürün kartlarını sayfadan kaldırır, böylece yeni sıralanmış ürünler için yer açılır.
3. API'den Veri Çekme
Fetch API kullanılarak "https://fakestoreapi.com/products"; adresinden ürün verileri çekilir. Bu, her sıralama işleminde en güncel verilerin kullanılmasını sağlar.
4. Hata Kontrolü
API yanıtının başarılı olup olmadığı kontrol edilir. Eğer yanıt başarısızsa, bir hata fırlatılır.
5. Veri İşleme ve Sıralama
API'den gelen veriler JSON formatında işlenir ve 'data' değişkenine atanır. Ardından, kullanıcının seçtiği sıralama kriterine göre işlem yapılır:
- Eğer seçilen değer 1 ise:
Ürünler fiyata göre artan sırada sıralanır (en düşük fiyattan en yükseğe).
const sortedByLowestPriceItems: IProduct[] = data.sort(
(a, b) => a.price - b.price
);
- Eğer seçilen değer 2 ise:
Ürünler fiyata göre azalan sırada sıralanır (en yüksek fiyattan en düşüğe).
const sortedByHighestPriceItems: IProduct[] = data.sort(
(a, b) => b.price - a.price
);
- Eğer seçilen değer 3 ise:
Ürünler derecelendirmeye göre sıralanır (en düşük derecelendirmeden en yükseğe).
const sortedByBestRatingItems: IProduct[] = data.sort(
(a, b) => a.rating.rate - b.rating.rate
);
6. Sıralanmış Ürünlerin Gösterilmesi
Son olarak, sıralanmış ürün dizisi üzerinde bir döngü çalıştırılır ve her ürün için 'printProduct' fonksiyonu çağrılır. Bu fonksiyon muhtemelen her bir ürünü HTML sayfasında görüntüler.
Bu kod, kullanıcıya dinamik olarak ürünleri sıralama imkanı sağlar, böylece kullanıcı deneyimini iyileştirir ve kişiselleştirilmiş bir alışveriş deneyimi sunar.
👉İlgili github kod linki: https://github.com/snahmd/mockshop/tree/2998be6faa3120f7774569b722e5763f5cea9deb
→ Kategorileri Filtreleme:
Kategorilerin butonuna basildiginda ilgili ürünler gelecek sadece
filterElectronicsButton.addEventListener("click", () => {
clearItemCards();
fetch("<https://fakestoreapi.com/products>")
.then((response: Response) => {
if (!response.ok) {
throw new Error("Failed to fetch Data");
}
return response.json();
})
.then((data: IProduct[]) => {
const filteredByCategories: IProduct[] = data.filter(
(product: IProduct) => product.category === "electronics"
);
filteredByCategories.forEach((product: IProduct) => {
printProduct(product);
});
});
});
Bu kod bloğu, elektronik kategorisindeki ürünleri filtrelemek için bir olay dinleyicisi (event listener) ekler ve şu işlemleri gerçekleştirir:
- 'filterElectronicsButton' adlı bir butona tıklama olayı dinleyicisi eklenir.
- Butona tıklandığında, önce mevcut ürün kartları temizlenir ('clearItemCards()' fonksiyonu ile).
- Ardından, API'den tüm ürünler çekilir ('https://fakestoreapi.com/products'; adresinden).
- API yanıtı kontrol edilir ve başarısız olursa hata fırlatılır.
- Başarılı yanıt alındığında, veriler JSON formatında işlenir.
- Ürünler, 'category' özelliği 'electronics' olanları filtreleyerek yeni bir dizi oluşturulur.
- Son olarak, filtrelenmiş ürünler döngüyle işlenir ve her biri için 'printProduct' fonksiyonu çağrılarak ekranda gösterilir.
Bu kod, kullanıcıya sadece elektronik kategorisindeki ürünleri gösterme imkanı sağlar.
Ayni bunun gibi diger kategoriler icinde yapalim:
filterJewelerysButton.addEventListener("click", () => {
clearItemCards();
fetch("<https://fakestoreapi.com/products>")
.then((response: Response) => {
if (!response.ok) {
throw new Error("Failed to fetch Data");
}
return response.json();
})
.then((data: IProduct[]) => {
const filteredByCategories: IProduct[] = data.filter(
(product: IProduct) => product.category === "jewelery"
);
filteredByCategories.forEach((product: IProduct) => {
printProduct(product);
});
});
});
filterMensButton.addEventListener("click", () => {
clearItemCards();
fetch("<https://fakestoreapi.com/products>")
.then((response: Response) => {
if (!response.ok) {
throw new Error("Failed to fetch Data");
}
return response.json();
})
.then((data: IProduct[]) => {
const filteredByCategories: IProduct[] = data.filter(
(product: IProduct) => product.category === "men's clothing"
);
filteredByCategories.forEach((product: IProduct) => {
printProduct(product);
});
});
});
filterWomensButton.addEventListener("click", () => {
clearItemCards();
fetch("<https://fakestoreapi.com/products>")
.then((response: Response) => {
if (!response.ok) {
throw new Error("Failed to fetch Data");
}
return response.json();
})
.then((data: IProduct[]) => {
const filteredByCategories: IProduct[] = data.filter(
(product: IProduct) => product.category === "women's clothing"
);
filteredByCategories.forEach((product: IProduct) => {
printProduct(product);
});
});
});
👉İlgili github kod linki: https://github.com/snahmd/mockshop/tree/bc89dade6bd06b0c97546f32c10a0673a6299d0d
Arama Sonuclarinin Döndürülmesi
titleInput.addEventListener("change", () => {
clearItemCards();
fetch("<https://fakestoreapi.com/products>")
.then((response: Response) => {
if (!response.ok) {
throw new Error("Failed to fetch Data");
}
return response.json();
})
.then((data: IProduct[]) => {
data.forEach((product: IProduct) => {
if (product.title.includes(titleInput.value)) {
printProduct(product);
}
});
});
});
Bu kod bloğu, bir arama işlevselliği sağlar ve şu şekilde çalışır:
- Event Listener: 'titleInput' adlı bir input elementine 'change' event listener'ı eklenir. Bu, kullanıcı arama kutusuna bir şey yazdığında ve inputtan çıktığında tetiklenir.
- Temizleme: 'clearItemCards()' fonksiyonu çağrılarak mevcut ürün kartları temizlenir.
- API Çağrısı: Tüm ürünleri almak için API'ye bir istek yapılır.
- Hata Kontrolü: API yanıtının başarılı olup olmadığı kontrol edilir.
- Veri İşleme: API'den gelen veriler JSON formatında işlenir.
- Filtreleme: Her ürün için, ürünün başlığının kullanıcının girdiği metni içerip içermediği kontrol edilir.
- Görüntüleme: Eğer ürün başlığı arama metnini içeriyorsa, 'printProduct' fonksiyonu çağrılarak ürün görüntülenir.
Bu kod, kullanıcıya ürünleri başlıklarına göre arama yapma imkanı sağlar.
👉İlgili github kod linki: https://github.com/snahmd/mockshop/tree/efce1038b9cac63d33d31002b28b88bb1efeee10
→Proje fonksiyonel bir sekilde calisiyor, bundan sonra style kismi yazacagiz. Tailwind CSS kullanacagiz.
Style Verme
Tailwind CSS kullanacagiz, cdn ile dosyayi cekiyoruz.
...
<script src="<https://cdn.tailwindcss.com>"></script>
...
Sonra tailwind calisma ortaminda bizim yazdigimiz özellikleri tanimasi icin tanimlamamiz lasim, yani config dosyasini ana dizinde olusturmamiz lazim.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/src/img/mockshop-logo.png" />
<link rel="stylesheet" href="src/styles.css" />
<script src="<https://cdn.tailwindcss.com>"></script>
<script type="module" src="src/main.ts" defer></script>
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>MockShop</title>
</head>
<body class="flex flex-col justify-center items-center bg-sky-100">
<img
class="w-[30%] md:w-[25%] xl:w-[15%]"
src="./src/img/mockshop-logo.png"
alt="logo"
/>
<section class="bg-sky-200 w-[100vw] p-4 flex flex-col gap-2">
<div class="flex gap-2 mx-auto">
<input class="p-2 bg-sky-100" id="itemTitle" type="text" />
<select class="p-2 bg-sky-100" name="" id="sortBy">
<option value="0">Sort By</option>
<option value="1">⬆️ Lowest Price</option>
<option value="2">⬇️ Highest Price</option>
<option value="3">⭐️ Best Rating</option>
</select>
</div>
<div class="filterCategories flex flex-col">
<p class="text-center text-2xl">Filter by Categories:</p>
<div
class="text-sm md:text-xl gap-1 md:gap-4 mx-auto flex items-center flex-wrap text-nowrap"
>
<button
class="border border-slate-600 p-1 rounded-sm"
id="electronics"
>
Electronics
</button>
<button class="border border-slate-600 p-1 rounded-sm" id="jewelery">
Jewelery
</button>
<button class="border border-slate-600 p-1 rounded-sm" id="mens">
Men's clothing
</button>
<button class="border border-slate-600 p-1 rounded-sm" id="womens">
Women's clothing
</button>
</div>
</div>
</section>
<section
class="flex flex-wrap justify-center p-4 gap-4 pb-20"
id="itemsSection"
>
<div
class="flex flex-col justify-center items-center border border-slate-600 w-[300px] h-[400px] sm:w-[450px] sm:h-[600px] first:hidden"
id="itemContainer"
>
<img
class="w-[200px] h-[270px] sm:w-[320px] sm:h-[400px] object-cover"
id="img"
src=""
alt=""
/>
<h2 class="text-center" id="productName"></h2>
<div class="priceCard flex items-center gap-4">
<h3 class="text-green-500 text-xl" id="price"></h3>
<button
class="bg-sky-200 p-2 rounded-md text-slate-600"
id="addToCart"
>
Add to cart
</button>
</div>
</div>
</section>
<footer>
<p class="text-center text-slate-600 pb-10">
Made with by
<a
class="text-slate-800"
href="<https://github.com/snahmd/>"
target="_blank"
rel="noopener noreferrer"
>snahmd</a
>
</p>
</footer>
</body>
</html>
Bu kod parçası, bir HTML sayfasının yapısını göstermektedir. Aşağıda ana bölümleri açıklıyorum:
- Sayfa, standart HTML5 yapısıyla başlıyor.
- Head bölümünde, meta etiketleri, favicon, stil dosyası bağlantısı, Tailwind CSS CDN bağlantısı ve ana TypeScript dosyası yer alıyor.
- Body etiketi, Tailwind CSS sınıfları kullanılarak stillendirilmiş ve flex yapısıyla düzenlenmiş.
- Sayfada bir logo, arama ve filtreleme bölümü, ürünlerin gösterileceği bir alan ve bir footer bulunuyor.
- Ürün filtreleme bölümünde, kategori seçimi için butonlar ve sıralama için bir dropdown menü var.
- Ürünlerin gösterileceği bölüm, esnek bir yapıda tasarlanmış ve responsive tasarım prensiplerine uygun olarak ayarlanmış.
- Footer'da, projenin yapımcısına ait bir link bulunuyor.
Sitenin son hali asagidaki gibi olacaktir:
Kapanis:
Bu projede api’dan bilgi cekip onu html icerisinde gösterdik. Ve bu API’den gelen degerlere göre filtreleme, siralama ve arama islemleri gerceklestirdik. Bir sonraki projede yahut yazida görüsürüz.
Projenin Linkleri Asagidadir:
https://github.com/snahmd/mockshop/
Projenin Canli Hali:
Yorum yap