→ Installation
Zunächst erstellen wir ein TypeScript-Projekt mit Vite und bereinigen die überflüssigen Dateien:
👉 Relevanter GitHub-Code-Link: https://github.com/snahmd/mockshop/tree/3d2bf168ec89b5985cc3895f11f04b4551094120
npm create vite@latest
npm install
npm run dev
Mit diesen Befehlen erstellen wir das Projekt.
→ API-Information
Dies ist eine API, die Produkte auflistet. In unserem Projekt werden wir diese API mit HTML-DOM-Elementen verwenden.
Wir werden sehen, welche JSON-Daten diese API zurückgibt und unser Projekt entsprechend anpassen.
Wir können dieses Tool verwenden, um die JSON-Daten in TypeScript-Schnittstellen umzuwandeln.
Interfaces File
Wir erstellen die erforderlichen Typinformationen als Schnittstelle mit denselben Namen.
type Rating = {
rate: number;
count: number;
}
interface IProduct {
id: number;
title: string;
price: number;
category: string;
image: string;
rating: Rating;
}
export default IProduct;
Hier kommen unsere Produkte als Rating-Objekt an, deshalb definieren wir dafür einen separaten Typ und integrieren ihn in IProduct.
👉Entsprechender GitHub-Code-Link: https://github.com/snahmd/mockshop/tree/a701656654974b6a6ae5311bb871872cafb263fc
→ Schreiben der Index.html-Datei
<!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>
Dieser HTML-Code erstellt die Hauptseite einer E-Commerce-Website namens MockShop. Die Seite enthält Folgendes:
- Titel und Favicon
- Optionen zur Produktsuche, Sortierung und Kategoriefilterung
- Einen Bereich für die Produktliste (mit Bild, Name, Preis und Schaltfläche zum Hinzufügen zum Warenkorb)
👉Relevanter GitHub-Code-Link: https://github.com/snahmd/mockshop/tree/a1e2115acffde5b2f190d10b12171988dc77adab
→ Anzeigen der Produkte aus der API im HTML:
Zuerst rufen wir unser Interface auf und wählen die Elemente aus der index.html aus.
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);
})
})
}
Zunächst werden die Produktdaten von "https://fakestoreapi.com/products"; abgerufen.
Falls der Datenabruf fehlschlägt, wird ein Fehler geworfen.
Bei erfolgreichem Abruf werden die empfangenen Daten im JSON-Format verarbeitet.
Für jedes Produkt werden folgende Schritte durchgeführt:
- Die Produktvorlage im HTML wird geklont.
- Produktbild, Name und Preis werden in die entsprechenden HTML-Elemente eingefügt.
- Die erstellte Produktkarte wird dem Hauptproduktcontainer hinzugefügt.
Diese Funktion fügt der Webseite dynamisch Produktkarten hinzu und zeigt dem Benutzer so die Produkte an.
.cloneNode() ist eine DOM-Methode (Document Object Model) in JavaScript. Diese Methode wird verwendet, um eine Kopie eines DOM-Knotens (node) zu erstellen. Im obigen Code wird diese Methode für folgende Zwecke eingesetzt:
- Eine Kopie eines bestehenden HTML-Elements (vermutlich einer Produktkartenvorlage) zu erstellen
- Diese Kopie für jedes Produkt anzupassen und der Seite hinzuzufügen
Dieser Ansatz ist eine effektive Methode, um sich wiederholende Strukturen (wie z.B. mehrere Produktkarten) dynamisch zu erstellen. Es ist effizienter, eine Vorlage zu kopieren und deren Inhalt zu ändern, als für jedes Produkt eine neue Karte zu erstellen.
- Methode; Alternative zu cloneNode():
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);
});
});
}
Bei diesem Ansatz erstellen wir für jedes Produkt neue DOM-Elemente und verbinden diese miteinander, um die Produktkarte zu erstellen. Diese Methode ermöglicht es uns, ein ähnliches Ergebnis wie mit cloneNode() zu erzielen, ohne diese Funktion zu verwenden.
Der Vorteil dieser Methode ist, dass sie mehr Kontrolle über jedes einzelne Element bietet. Der Nachteil ist, dass sie, insbesondere bei einer großen Anzahl von Produkten, etwas langsamer sein kann als die cloneNode()-Methode.
→Aktuell werden die Produkte auf der HTML-Seite angezeigt. Als Nächstes werden wir verschiedene Sortier- und Filteroperationen durchführen. Dafür müssen wir eine Clear-Funktion schreiben, die die auf der HTML-Seite angezeigten Werte löscht. Diese müssen wir mit setAttribute an die fetchAndDisplay-Funktion übergeben.
#...
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());
}
Dieser letzte Codeabschnitt erfüllt zwei wichtige Funktionen:
- fetchAndDisplay() Funktion: Jeder Produktkarte wird eine "delete" ID zugewiesen. Dies wird später verwendet, um diese Karten einfach auszuwählen und zu löschen.
- clearItemCards() Funktion: Entfernt alle Produktkarten von der Seite. Diese Funktion:
- Wählt alle Elemente mit der ID "delete" aus
- Durchläuft diese Elemente in einer Schleife und entfernt jedes einzelne von der Seite
Dieser Code ist nützlich, wenn Sie Produkte neu sortieren oder filtern möchten, da er die vorhandenen Produktkarten löscht und es ermöglicht, neu sortierte oder gefilterte Produkte anzuzeigen.
👉Zugehöriger GitHub-Code-Link: https://github.com/snahmd/mockshop/tree/fb0ae87e68e3641ffbdee583a445c28154c839fa
Produkte in einer Funktion aufrufen:
Zuvor haben wir alle Produkte auf dem Bildschirm angezeigt. Bei jedem Löschen müssen wir die Produkte neu erstellen. Nach dem DRY-Prinzip werden wir eine printProduct-Funktion für die Produkte erstellen, die wir aufrufen werden, wenn wir die Produkte erneut auflisten möchten.
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);
}
Wir haben die forEach-Schleife neu strukturiert und die Funktion aufgerufen.
👉Entsprechender GitHub-Code-Link: https://github.com/snahmd/mockshop/tree/a6fe477cda86ec65da590ccbc8af999e83e4d2e7
Logischer Teil der Sortierung
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. Definition des Event Listeners
Der Code fügt einem HTML-Element namens 'sortBySelected' (vermutlich ein Dropdown-Menü) einen 'change' Event Listener hinzu. Dieser wird jedes Mal ausgelöst, wenn der Benutzer die Sortieroption ändert.
2. Entfernen vorhandener Produkte
Die Funktion 'clearItemCards()' wird aufgerufen. Diese entfernt die bestehenden Produktkarten von der Seite, um Platz für die neu sortierten Produkte zu schaffen.
3. Daten von der API abrufen
Mithilfe der Fetch API werden Produktdaten von der Adresse "https://fakestoreapi.com/products"; abgerufen. Dies stellt sicher, dass bei jedem Sortiervorgang die aktuellsten Daten verwendet werden.
4. Fehlerüberprüfung
Es wird überprüft, ob die API-Antwort erfolgreich war. Falls die Antwort nicht erfolgreich ist, wird ein Fehler ausgelöst.
5. Datenverarbeitung und Sortierung
Die von der API empfangenen Daten werden im JSON-Format verarbeitet und der Variable 'data' zugewiesen. Anschließend wird basierend auf dem vom Benutzer gewählten Sortierkriterium eine Aktion durchgeführt:
- Wenn der ausgewählte Wert 1 ist: Die Produkte werden nach Preis in aufsteigender Reihenfolge sortiert (vom niedrigsten zum höchsten Preis).
const sortedByLowestPriceItems: IProduct[] = data.sort(
(a, b) => a.price - b.price
);
Wenn der ausgewählte Wert 2 ist:
Die Produkte werden in absteigender Reihenfolge nach Preis sortiert (vom höchsten zum niedrigsten Preis).
const sortedByHighestPriceItems: IProduct[] = data.sort(
(a, b) => b.price - a.price
);
- Wenn der ausgewählte Wert 3 ist: Die Produkte werden nach Bewertung sortiert (von der niedrigsten zur höchsten Bewertung).
const sortedByBestRatingItems: IProduct[] = data.sort(
(a, b) => a.rating.rate - b.rating.rate
);
6. Anzeigen der sortierten Produkte
Schließlich wird über das sortierte Produktarray iteriert und für jedes Produkt die Funktion 'printProduct' aufgerufen. Diese Funktion zeigt vermutlich jedes Produkt auf der HTML-Seite an.
Dieser Code ermöglicht es dem Benutzer, die Produkte dynamisch zu sortieren, wodurch das Benutzererlebnis verbessert und ein personalisiertes Einkaufserlebnis geboten wird.
👉Entsprechender GitHub-Code-Link: https://github.com/snahmd/mockshop/tree/2998be6faa3120f7774569b722e5763f5cea9deb
→ Kategorien filtern:
Wenn auf den Button einer Kategorie geklickt wird, werden nur die entsprechenden Produkte angezeigt
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);
});
});
});
Dieser Codeblock fügt einen Event-Listener hinzu, um Produkte der Elektronik-Kategorie zu filtern und führt folgende Aktionen aus:
- Ein Klick-Event-Listener wird dem 'filterElectronicsButton' hinzugefügt.
- Beim Klicken auf den Button werden zunächst alle vorhandenen Produktkarten gelöscht (mit der Funktion 'clearItemCards()').
- Anschließend werden alle Produkte von der API abgerufen (von der Adresse 'https://fakestoreapi.com/products').
- Die API-Antwort wird überprüft und bei Misserfolg wird ein Fehler geworfen.
- Bei erfolgreicher Antwort werden die Daten im JSON-Format verarbeitet.
- Die Produkte werden gefiltert, um ein neues Array zu erstellen, das nur Produkte mit der 'category'-Eigenschaft 'electronics' enthält.
- Schließlich werden die gefilterten Produkte in einer Schleife verarbeitet und für jedes Produkt wird die Funktion 'printProduct' aufgerufen, um es auf dem Bildschirm anzuzeigen.
Dieser Code ermöglicht es dem Benutzer, nur Produkte der Elektronik-Kategorie anzuzeigen.
Lasst uns das Gleiche für die anderen Kategorien machen:
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);
});
});
});
👉Zugehöriger GitHub-Code-Link: https://github.com/snahmd/mockshop/tree/bc89dade6bd06b0c97546f32c10a0673a6299d0d
Rückgabe der Suchergebnisse
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);
}
});
});
});
Dieser Codeblock bietet eine Suchfunktionalität und funktioniert wie folgt:
- Event Listener: Ein 'change' Event Listener wird dem Input-Element namens 'titleInput' hinzugefügt. Dieser wird ausgelöst, wenn der Benutzer etwas in das Suchfeld eingibt und das Input-Feld verlässt.
- Bereinigung: Die Funktion 'clearItemCards()' wird aufgerufen, um die vorhandenen Produktkarten zu löschen.
- API-Aufruf: Eine Anfrage wird an die API gesendet, um alle Produkte abzurufen.
- Fehlerprüfung: Es wird überprüft, ob die API-Antwort erfolgreich war.
- Datenverarbeitung: Die von der API empfangenen Daten werden im JSON-Format verarbeitet.
- Filterung: Für jedes Produkt wird geprüft, ob der Produkttitel den vom Benutzer eingegebenen Text enthält.
- Anzeige: Wenn der Produkttitel den Suchtext enthält, wird die Funktion 'printProduct' aufgerufen, um das Produkt anzuzeigen.
Dieser Code ermöglicht es dem Benutzer, Produkte nach ihren Titeln zu durchsuchen.
👉Relevanter GitHub-Code-Link: https://github.com/snahmd/mockshop/tree/efce1038b9cac63d33d31002b28b88bb1efeee10
→Das Projekt funktioniert nun, als Nächstes werden wir den Styling-Teil schreiben. Wir werden Tailwind CSS verwenden.
Styling
Wir werden Tailwind CSS verwenden und es über CDN einbinden.
...
<script src="<https://cdn.tailwindcss.com>"></script>
...
Dann müssen wir eine Konfigurationsdatei im Hauptverzeichnis erstellen, damit Tailwind unsere selbst definierten Eigenschaften erkennt.
<!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>
Dieser Codeabschnitt zeigt die Struktur einer HTML-Seite. Im Folgenden erkläre ich die Hauptbereiche:
- Die Seite beginnt mit der Standard-HTML5-Struktur.
- Der Head-Bereich enthält Meta-Tags, Favicon, Stylesheet-Verlinkung, Tailwind CSS CDN-Verlinkung und die Haupt-TypeScript-Datei.
- Das Body-Tag ist mit Tailwind CSS-Klassen gestylt und mit einer Flex-Struktur organisiert.
- Die Seite enthält ein Logo, einen Such- und Filterbereich, einen Bereich zur Produktdarstellung und einen Footer.
- Im Produktfilterbereich gibt es Buttons zur Kategorieauswahl und ein Dropdown-Menü zum Sortieren.
- Der Bereich zur Produktdarstellung ist flexibel gestaltet und entsprechend responsiven Designprinzipien angepasst.
- Im Footer befindet sich ein Link zum Projektersteller.
Das finale Erscheinungsbild der Website wird wie folgt aussehen:
Abschluss:
In diesem Projekt haben wir Informationen von einer API abgerufen und sie in HTML dargestellt. Basierend auf den Werten, die wir von dieser API erhalten haben, haben wir Filter-, Sortier- und Suchvorgänge durchgeführt. Wir sehen uns im nächsten Projekt oder Artikel wieder.
Projekt Link:
https://github.com/snahmd/mockshop/
Live Projekt Link:
Kommentar hinterlassen