Jetzt Kontakt aufnehmenENDE
Produktfilter mit JavaScript und PHP für Ihren E-Commerce-Shop

Produktfilter mit JavaScript und PHP für Ihren E-Commerce-Shop

Produktfilter sind im E-Commerce entscheidend für die Benutzererfahrung. In diesem Artikel zeigen wir Ihnen, wie Sie ein schnelles, dynamisches Filtersystem mit JavaScript und PHP umsetzen – inklusive AJAX für reibungslose Updates und erweiterte Funktionen für bessere Performance.

Nehmen Sie Kontakt auf
Nehmen Sie Kontakt auf

Wir sind für Sie erreichbar.

Jetzt Gespräch suchen
Von
  • Webentwicklung
  • E-Commerce

HTML-Grundstruktur für Filter und Produktliste

<form id="filter-form">
<fieldset>
	<legend>Produktfilter</legend>
	<label>
		Kategorie:
		<select name="category">
			<option value="">Alle Kategorien</option>
			<option value="shirts">Shirts</option>
			<option value="hosen">Hosen</option>
			<option value="schuhe">Schuhe</option>
			<option value="accessoires">Accessoires</option>
		</select>
	</label>
	<label>
		Preis:
		<select name="price">
			<option value="">Alle Preise</option>
			<option value="low">Unter 50€</option>
			<option value="medium">50–100€</option>
			<option value="high">Über 100€</option>
		</select>
	</label>
	<label>
		Sortierung:
		<select name="sort">
			<option value="">Standard</option>
			<option value="price_asc">Preis aufsteigend</option>
			<option value="price_desc">Preis absteigend</option>
			<option value="name_asc">Name A-Z</option>
		</select>
	</label>
</fieldset>
</form>

<div id="loading-indicator" style="display: none;">
	<p>Produkte werden geladen...</p>
</div>

<div id="product-list"></div>

Wir beginnen mit einer strukturierten HTML-Vorlage, die aus einem Formular zur Filterung und einem Container für die Produktliste besteht. Die Verwendung von Fieldset und Legend verbessert die Barrierefreiheit, während der Loading-Indikator dem Benutzer visuelles Feedback gibt.

AJAX-Request bei Filteränderung mit verbessertem Error-Handling

Das Formular löst bei jeder Änderung einen AJAX-Request aus, um die Produktliste zu aktualisieren. Inklusive Ladeindikator und Fehlerbehandlung.

document.getElementById("filter-form").addEventListener("change", function () {
	const formData = new FormData(this);
	const loadingIndicator = document.getElementById("loading-indicator");
	const productList = document.getElementById("product-list");
	
	// Ladeindikator anzeigen
	loadingIndicator.style.display = "block";
	productList.style.opacity = "0.5";
	
	fetch("get_products.php", {
		method: "POST",
		body: formData,
		headers: {
			'X-Requested-With': 'XMLHttpRequest'
		}
	})
	.then(response => {
		if (!response.ok) {
			throw new Error(`HTTP error! status: ${response.status}`);
		}
		return response.text();
	})
	.then(html => {
		productList.innerHTML = html;
		productList.style.opacity = "1";
		
		// Anzahl der Ergebnisse anzeigen
		const productCount = productList.querySelectorAll('.product').length;
		updateResultCount(productCount);
	})
	.catch(err => {
		console.error("Fehler beim Laden der Produkte:", err);
		productList.innerHTML = '<p class="error">Fehler beim Laden der Produkte. Bitte versuchen Sie es später erneut.</p>';
		productList.style.opacity = "1";
	})
	.finally(() => {
		loadingIndicator.style.display = "none";
	});
});

function updateResultCount(count) {
	let countElement = document.getElementById("result-count");
	if (!countElement) {
		countElement = document.createElement("p");
		countElement.id = "result-count";
		document.getElementById("product-list").insertAdjacentElement("beforebegin", countElement);
	}
	countElement.textContent = `${count} Produkt${count !== 1 ? 'e' : ''} gefunden`;
}

Diese erweiterte JavaScript-Funktion holt bei jeder Filteränderung neue Produktdaten vom Server. Sie bietet verbessertes Error-Handling, einen Ladeindikator und zeigt die Anzahl der gefundenen Produkte an.

Serverseitige Verarbeitung in PHP mit Datenbankanbindung

Der Server filtert Produkte je nach Anfrageparameter, implementiert Sortierung und gibt strukturiertes HTML zurück.

<?php
	header('Content-Type: text/html; charset=utf-8');
	
	// Eingabedaten validieren und bereinigen
	$category = filter_input(INPUT_POST, 'category', FILTER_SANITIZE_STRING) ?? '';
	$price = filter_input(INPUT_POST, 'price', FILTER_SANITIZE_STRING) ?? '';
	$sort = filter_input(INPUT_POST, 'sort', FILTER_SANITIZE_STRING) ?? '';

	// Beispiel-Produktdaten (in der Praxis: Datenbankabfrage)
	$products = [
		["id" => 1, "name" => "Blaues Shirt", "category" => "shirts", "price" => 29.99, "image" => "shirt1.jpg"],
		["id" => 2, "name" => "Schwarze Hose", "category" => "hosen", "price" => 89.50, "image" => "hose1.jpg"],
		["id" => 3, "name" => "Designer-Shirt", "category" => "shirts", "price" => 120.00, "image" => "shirt2.jpg"],
		["id" => 4, "name" => "Sportschuhe", "category" => "schuhe", "price" => 75.00, "image" => "schuh1.jpg"],
		["id" => 5, "name" => "Elegante Hose", "category" => "hosen", "price" => 45.00, "image" => "hose2.jpg"],
	];

	// Produkte nach Kriterien filtern
	$filtered = array_filter($products, function ($product) use ($category, $price) {
		// Kategorie-Filter
		if ($category && $product['category'] !== $category) {
			return false;
		}
		
		// Preis-Filter
		if ($price === 'low' && $product['price'] >= 50) return false;
		if ($price === 'medium' && ($product['price'] < 50 || $product['price'] > 100)) return false;
		if ($price === 'high' && $product['price'] <= 100) return false;
		
		return true;
	});

	// Sortierung anwenden
	switch ($sort) {
		case 'price_asc':
			usort($filtered, fn($a, $b) => $a['price'] <=> $b['price']);
			break;
		case 'price_desc':
			usort($filtered, fn($a, $b) => $b['price'] <=> $a['price']);
			break;
		case 'name_asc':
			usort($filtered, fn($a, $b) => strcasecmp($a['name'], $b['name']));
			break;
	}

	// HTML-Ausgabe generieren
	if (empty($filtered)) {
		echo '<p class="no-results">Keine Produkte gefunden, die Ihren Kriterien entsprechen.</p>';
	} else {
		foreach ($filtered as $item) {
			$formattedPrice = number_format($item['price'], 2, ',', '.');
			echo "<div class='product' data-id='{$item['id']}'>
				<img src='images/{$item['image']}' alt='{$item['name']}' loading='lazy'>
				<h3>{$item['name']}</h3>
				<p class='price'>{$formattedPrice}€</p>
				<button class='add-to-cart' data-product-id='{$item['id']}'>In den Warenkorb</button>
			</div>";
		}
	}

	// Beispiel für Datenbankabfrage (PDO)
	/*
	try {
		$pdo = new PDO("mysql:host=localhost;dbname=shop", $username, $password);
		$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
		
		$sql = "SELECT * FROM products WHERE 1=1";
		$params = [];
		
		if ($category) {
			$sql .= " AND category = :category";
			$params[':category'] = $category;
		}
		
		if ($price) {
			switch ($price) {
				case 'low':
					$sql .= " AND price < 50";
					break;
				case 'medium':
					$sql .= " AND price BETWEEN 50 AND 100";
					break;
				case 'high':
					$sql .= " AND price > 100";
					break;
			}
		}
		
		if ($sort) {
			switch ($sort) {
				case 'price_asc':
					$sql .= " ORDER BY price ASC";
					break;
				case 'price_desc':
					$sql .= " ORDER BY price DESC";
					break;
				case 'name_asc':
					$sql .= " ORDER BY name ASC";
					break;
			}
		}
		
		$stmt = $pdo->prepare($sql);
		$stmt->execute($params);
		$products = $stmt->fetchAll(PDO::FETCH_ASSOC);
		
	} catch (PDOException $e) {
		error_log("Datenbankfehler: " . $e->getMessage());
		echo '<p class="error">Fehler beim Laden der Produkte.</p>';
	}
	*/
?>

Die Datei get_products.php wertet die POST-Daten aus, filtert und sortiert die Produktliste und gibt sie als strukturiertes HTML zurück. Die Kommentare zeigen, wie Sie eine echte Datenbankanbindung implementieren können.

Verbesserung der Performance durch Debouncing und Caching

let debounceTimer;
const cache = new Map();

document.getElementById("filter-form").addEventListener("change", function () {
	clearTimeout(debounceTimer);
	
	debounceTimer = setTimeout(() => {
		const formData = new FormData(this);
		const cacheKey = new URLSearchParams(formData).toString();
		
		// Prüfen, ob Ergebnis bereits im Cache vorhanden ist
		if (cache.has(cacheKey)) {
			document.getElementById("product-list").innerHTML = cache.get(cacheKey);
			return;
		}
		
		const loadingIndicator = document.getElementById("loading-indicator");
		const productList = document.getElementById("product-list");
		
		loadingIndicator.style.display = "block";
		productList.style.opacity = "0.5";
		
		fetch("get_products.php", {
			method: "POST",
			body: formData,
			headers: {
				'X-Requested-With': 'XMLHttpRequest'
			}
		})
		.then(response => {
			if (!response.ok) {
				throw new Error(`HTTP error! status: ${response.status}`);
			}
			return response.text();
		})
		.then(html => {
			// Ergebnis im Cache speichern
			cache.set(cacheKey, html);
			
			productList.innerHTML = html;
			productList.style.opacity = "1";
			
			const productCount = productList.querySelectorAll('.product').length;
			updateResultCount(productCount);
		})
		.catch(err => {
			console.error("Fehler beim Laden der Produkte:", err);
			productList.innerHTML = '<p class="error">Fehler beim Laden der Produkte. Bitte versuchen Sie es später erneut.</p>';
			productList.style.opacity = "1";
		})
		.finally(() => {
			loadingIndicator.style.display = "none";
		});
	}, 300);
});

// Cache nach 5 Minuten leeren
setTimeout(() => {
	cache.clear();
}, 5 * 60 * 1000);

Mit Debouncing vermeiden wir zu viele schnelle Requests bei mehreren schnellen Änderungen. Das integrierte Caching reduziert Server-Anfragen für identische Filter-Kombinationen zusätzlich.

CSS-Styling für eine professionelle Darstellung

#filter-form {
	background: #f8f9fa;
	padding: 20px;
	border-radius: 8px;
	margin-bottom: 20px;
	box-shadow: 0 2px 4px rgba(0,0,0,0.1);
}

#filter-form fieldset {
	border: none;
	padding: 0;
	margin: 0;
}

#filter-form legend {
	font-size: 1.2em;
	font-weight: bold;
	margin-bottom: 15px;
	color: #333;
}

#filter-form label {
	display: inline-block;
	margin-right: 20px;
	margin-bottom: 10px;
	font-weight: 500;
}

#filter-form select {
	display: block;
	width: 100%;
	max-width: 200px;
	padding: 8px 12px;
	border: 1px solid #ddd;
	border-radius: 4px;
	font-size: 14px;
	margin-top: 5px;
}

#loading-indicator {
	text-align: center;
	padding: 20px;
	font-style: italic;
	color: #666;
}

#product-list {
	display: grid;
	grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
	gap: 20px;
	transition: opacity 0.3s ease;
}

.product {
	border: 1px solid #eee;
	border-radius: 8px;
	padding: 15px;
	background: white;
	text-align: center;
	box-shadow: 0 2px 4px rgba(0,0,0,0.1);
	transition: transform 0.2s ease;
}

.product:hover {
	transform: translateY(-2px);
	box-shadow: 0 4px 8px rgba(0,0,0,0.15);
}

.product img {
	width: 100%;
	height: 200px;
	object-fit: cover;
	border-radius: 4px;
	margin-bottom: 10px;
}

.product h3 {
	margin: 10px 0;
	font-size: 1.1em;
	color: #333;
}

.product .price {
	font-size: 1.2em;
	font-weight: bold;
	color: #e74c3c;
	margin: 10px 0;
}

.add-to-cart {
	background: #3498db;
	color: white;
	border: none;
	padding: 10px 20px;
	border-radius: 4px;
	cursor: pointer;
	font-size: 14px;
	transition: background 0.2s ease;
}

.add-to-cart:hover {
	background: #2980b9;
}

.no-results, .error {
	text-align: center;
	padding: 40px;
	color: #666;
	font-style: italic;
}

.error {
	color: #e74c3c;
	background: #fdf2f2;
	border: 1px solid #f5c6cb;
	border-radius: 4px;
}

#result-count {
	margin-bottom: 20px;
	font-weight: 500;
	color: #666;
}

Diese CSS-Styles sorgen für eine professionelle, responsive Darstellung des Filtersystems. Das Grid-Layout passt sich automatisch an verschiedene Bildschirmgrößen an.

Fazit: Dynamische Produktfilter verbessern UX und Conversion

Ein dynamischer Produktfilter sorgt für bessere Benutzerführung und höhere Conversion-Raten. Mit JavaScript und PHP lässt sich ein performantes und leicht anpassbares Filtersystem für jeden E-Commerce-Shop realisieren. Die vorgestellte Lösung bietet erweiterte Funktionen wie Caching, Debouncing und professionelle Fehlerbehandlung, die für produktive Umgebungen unerlässlich sind. Durch die modulare Struktur können Sie das System einfach an Ihre spezifischen Anforderungen anpassen und mit weiteren Features wie Suchfunktionen oder erweiterten Filtern ergänzen.

Nehmen Sie Kontakt auf
Nehmen Sie Kontakt auf

Wir sind für Sie erreichbar.

Jetzt Gespräch suchen

Kann ich dieses Filtersystem in WooCommerce nutzen?

Nicht direkt, aber die Logik lässt sich mit eigenen Endpoints und Templates in WooCommerce adaptieren. Sie können WooCommerce-Hooks verwenden, um das System zu integrieren.

Wie lade ich Produktdaten aus einer Datenbank statt aus einem Array?

Ersetzen Sie das $products-Array durch eine SQL-Abfrage und nutzen Sie z. B. PDO, um die Daten dynamisch zu laden. Ein Beispiel finden Sie im kommentierten Code-Bereich.

Kann ich weitere Filter wie Farben oder Größen ergänzen?

Ja, fügen Sie einfach weitere Felder im HTML-Formular hinzu und erweitern Sie die entsprechenden Bedingungen im PHP-Code. Das System ist modular aufgebaut und leicht erweiterbar.

Was ist Debouncing und warum ist es sinnvoll?

Debouncing sorgt dafür, dass Funktionen nicht zu oft hintereinander aufgerufen werden – ideal bei schnellen UI-Interaktionen. Es reduziert die Server-Last und verbessert die Performance.

Wie kann ich das Ganze mit Vue oder React umsetzen?

Das Prinzip bleibt gleich – State Management und API-Aufrufe übernehmen dabei die JavaScript-Frameworks. Sie können die PHP-API unverändert verwenden.

Wie implementiere ich eine Volltext-Suche?

Ergänzen Sie ein Suchfeld im HTML-Formular und erweitern Sie die PHP-Logik um eine LIKE-Abfrage oder nutzen Sie Elasticsearch für erweiterte Suchfunktionen.

Ist das System für mobile Geräte optimiert?

Ja, durch die responsive CSS-Grid-Layout-Implementierung passt sich das System automatisch an verschiedene Bildschirmgrößen an.

Wie kann ich die Performance bei großen Produktkatalogen verbessern?

Implementieren Sie Pagination, Lazy Loading und Datenbank-Indizierung. Zusätzlich können Sie Server-seitiges Caching mit Redis oder Memcached einsetzen.

Wie können wir Ihnen helfen?
Wie können wir Ihnen helfen?

Unsere Dienstleistungen decken alle Bereiche der digitalen Kommunikation.

Schreiben Sie uns
weedesign Blog

Shopware 6 gegen Shopify: Ein klarer Vergleich

Die Wahl des richtigen E-Commerce-Systems ist für den Erfolg Ihres Online-Geschäfts von entscheidender Bedeutung. Zwei der beliebtesten Plattformen sind Shopware 6 und Shopify. Wir vergleichen die beiden Systeme in Bezug auf Leistung, Benutzerfreundlichkeit, Funktionen und Kosten, um Ihnen zu helfen, die beste Lösung für Ihre Online-Präsenz zu finden.

Zum Blog-Beitrag

Wie Chatbots Ihre Kundenkommunikation verbessern können

In einer Welt, in der Kunden immer mehr von 24/7-Verfügbarkeit und sofortiger Antwortzeit erwarten, können Chatbots eine ideale Lösung für Unternehmen sein, die ihre Kundenkommunikation verbessern möchten. In diesem Artikel werden wir die Vorteile von Chatbots in der Kundenkommunikation diskutieren und erklären, wie sie Ihr Unternehmen unterstützen können, Kundenanfragen schneller zu beantworten und eine höhere Kundenzufriedenheit zu erreichen.

Zum Blog-Beitrag

Ladezeiten-Optimierung für E-Commerce: Mit Core Web Vitals zu besseren Rankings

Schnelle Ladezeiten sind entscheidend für den E-Commerce-Erfolg. In diesem Artikel zeigen wir, wie Sie die Core Web Vitals optimieren, um bessere Google-Rankings und höhere Conversion-Raten zu erreichen.

Zum Blog-Beitrag
Auf der Suche nach Shopware-Erweiterungen?
Hier sind unsere Bestseller!
Erweiterter Editor | WYSIWYG

Erweiterter Editor | WYSIWYG

Nutze den erweiterten WYSIWYG-Editor in Shopware 6. Dieser Editor ermöglicht die einfache Einbettung von Medien in die Beschreibung und viele weitere Features.

ab 7,99 €* / Monat

PageSpeed optimieren

PageSpeed optimieren

Optimieren Sie Ihren Shop und schaffen Sie damit ein besseres Erlebnis für Ihre Kunden. Dieses Plugin minimiert die Ladezeit Ihres Shops und bietet zahlreiche Konfigurationen.

ab 27,49 €* / Monat

Twig Manager

Twig Manager

Erstellen und bearbeiten Sie Ihre eigenen Template-Erweiterungen schnell und einfach in der Administration. Anzeige vorhandener Storefront-Vorlagenpfade und -Inhalte.

ab 3,99 €* / Monat

Hinweis: * Alle Preise verstehen sich zzgl. Mehrwertsteuer

x