From d2b21326056d3da5eeac846afc5bd71da16538a2 Mon Sep 17 00:00:00 2001 From: David Kertzscher Date: Wed, 20 May 2026 13:03:38 +0000 Subject: [PATCH] =?UTF-8?q?Bonus:=20fetch=20&=20API=20=E2=80=93=20JSONPlac?= =?UTF-8?q?eholder=20Users,=20DOM-Rendering,=20Fehlerbehandlung,=20Filter?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- api.html | 143 +++++++++++++++++++++++++ css/style.css | 261 ++++++++++++++++++++++++++++++++++++++++++++++ eis_projekt.html | 4 +- galerie.html | 4 +- impressum.html | 4 +- index.html | 4 +- js/api-demo.js | 169 ++++++++++++++++++++++++++++++ kontakt.html | 3 +- notenrechner.html | 1 + team.html | 1 + textanalyse.html | 1 + ueber_uns.html | 4 +- 12 files changed, 593 insertions(+), 6 deletions(-) create mode 100644 api.html create mode 100644 js/api-demo.js diff --git a/api.html b/api.html new file mode 100644 index 0000000..499f0b9 --- /dev/null +++ b/api.html @@ -0,0 +1,143 @@ + + + + + + + API Demo – David & Karo + + + + +
+
+ + + + + + +
+
+ +
+
+

🌐 fetch & API Demo

+

Live-Daten von jsonplaceholder.typicode.com/users – geladen per JavaScript fetch(). ✨

+
+ +
+

πŸ’‘ Wie funktioniert's?

+
+
+

1️⃣ fetch(URL)

+

JavaScript sendet einen HTTP-GET Request an die API-URL und erhΓ€lt eine Promise zurΓΌck.

+
+
+

2️⃣ .then(r => r.json())

+

Die Antwort wird als JSON geparst – aus Text werden echte JavaScript-Objekte.

+
+
+

3️⃣ DOM rendern

+

Die Daten werden mit createElement in den DOM eingefΓΌgt – kein Reload nΓΆtig!

+
+
+

4️⃣ .catch(err)

+

Fehlerbehandlung: Falls die API nicht erreichbar ist, wird eine Fehlermeldung angezeigt.

+
+
+
+ +
+

πŸ‘₯ Benutzer von der API

+

Die Liste wird live beim Laden der Seite von jsonplaceholder.typicode.com abgerufen:

+ +
+ + +
+ + +
+
+

Daten werden geladen…

+
+ + + + + +
    + + +

    +
    + +
    +

    πŸ“„ Quellcode-ErklΓ€rung

    +
    // 1. fetch aufrufen
    +fetch('https://jsonplaceholder.typicode.com/users')
    +
    +  // 2. Antwort als JSON parsen
    +  .then(response => {
    +    if (!response.ok) {
    +      throw new Error('HTTP ' + response.status);
    +    }
    +    return response.json();
    +  })
    +
    +  // 3. Daten in console.log prΓΌfen & im DOM rendern
    +  .then(users => {
    +    console.log('API-Daten:', users);
    +    renderUsers(users);
    +  })
    +
    +  // 4. Fehlerbehandlung
    +  .catch(error => {
    +    console.error('Fetch-Fehler:', error);
    +    showApiError(error.message);
    +  });
    +
    +
    + + + + + + + diff --git a/css/style.css b/css/style.css index 95d4304..c09542e 100644 --- a/css/style.css +++ b/css/style.css @@ -2926,3 +2926,264 @@ body.dark .nav-toggle span { text-align: center; } } + +/* ═══════════════════════════════════════════════════════════════════════════ + API Demo – api.html + ═══════════════════════════════════════════════════════════════════════════ */ + +/* Info-Cards (ErklΓ€rung wie fetch funktioniert) */ +.info-cards { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); + gap: 1.25rem; + margin: 1.5rem 0; +} + +.info-card { + background: linear-gradient(135deg, #fff0f5, #fce7f3); + border: 1px solid #f9a8d4; + border-radius: 12px; + padding: 1.25rem; + text-align: center; +} + +.info-card h4 { + color: #be185d; + margin-bottom: 0.5rem; + font-size: 1rem; +} + +.info-card p { + font-size: 0.9rem; + color: #555; + margin: 0; +} + +.info-card code { + background: #fce7f3; + padding: 0.1em 0.35em; + border-radius: 4px; + font-size: 0.85em; + color: #be185d; +} + +/* API Controls (Button + Suchfeld) */ +.api-controls { + display: flex; + gap: 1rem; + align-items: center; + flex-wrap: wrap; + margin: 1.25rem 0; +} + +#api-search { + flex: 1 1 220px; + padding: 0.6rem 1rem; + border: 2px solid #f9a8d4; + border-radius: 25px; + font-size: 0.95rem; + outline: none; + transition: border-color 0.2s; +} + +#api-search:focus { + border-color: #ec4899; +} + +/* Lade-Indikator */ +.api-loading { + display: flex; + align-items: center; + gap: 1rem; + padding: 1.5rem; + color: #be185d; +} + +.api-loading[hidden] { display: none; } + +.api-spinner { + width: 32px; + height: 32px; + border: 4px solid #fce7f3; + border-top-color: #ec4899; + border-radius: 50%; + animation: spin 0.8s linear infinite; + flex-shrink: 0; +} + +@keyframes spin { + to { transform: rotate(360deg); } +} + +/* Fehlermeldung */ +.api-error { + display: flex; + align-items: flex-start; + gap: 1rem; + background: #fff0f0; + border: 1px solid #fca5a5; + border-radius: 10px; + padding: 1rem 1.25rem; + color: #b91c1c; + margin: 1rem 0; +} + +.api-error[hidden] { display: none; } + +.api-error span:first-child { + font-size: 1.5rem; + flex-shrink: 0; +} + +/* Benutzer-Liste */ +.user-list { + list-style: none; + padding: 0; + margin: 0; + display: grid; + grid-template-columns: repeat(auto-fill, minmax(280px, 1fr)); + gap: 1.25rem; +} + +/* User-Karte */ +.api-user-card { + background: #fff; + border: 1px solid #f9a8d4; + border-radius: 14px; + padding: 1.25rem; + display: flex; + gap: 1rem; + align-items: flex-start; + box-shadow: 0 2px 8px rgba(236,72,153,0.08); + transition: transform 0.2s, box-shadow 0.2s; +} + +.api-user-card:hover { + transform: translateY(-3px); + box-shadow: 0 6px 18px rgba(236,72,153,0.15); +} + +.api-user-avatar { + width: 48px; + height: 48px; + background: linear-gradient(135deg, #ec4899, #be185d); + color: #fff; + border-radius: 50%; + display: flex; + align-items: center; + justify-content: center; + font-size: 1.3rem; + font-weight: 700; + flex-shrink: 0; +} + +.api-user-name { + font-size: 1rem; + font-weight: 700; + color: #be185d; + margin: 0 0 0.15rem; +} + +.api-user-username { + font-size: 0.85rem; + color: #888; + margin: 0 0 0.5rem; +} + +.api-user-details { + list-style: none; + padding: 0; + margin: 0; + font-size: 0.85rem; + color: #444; + display: flex; + flex-direction: column; + gap: 0.2rem; +} + +.api-user-details a { + color: #ec4899; + text-decoration: none; +} + +.api-user-details a:hover { + text-decoration: underline; +} + +/* Ergebnis-ZΓ€hler */ +.api-count { + text-align: right; + font-size: 0.9rem; + color: #888; + margin-top: 0.75rem; +} + +/* Keine Ergebnisse */ +.api-no-results { + grid-column: 1 / -1; + text-align: center; + padding: 2rem; + color: #aaa; + font-size: 1rem; +} + +/* Code-Block */ +.code-block { + background: #1e1e2e; + color: #cdd6f4; + border-radius: 12px; + padding: 1.5rem; + overflow-x: auto; + font-size: 0.88rem; + line-height: 1.65; + border: 1px solid #313244; +} + +.code-block code { + font-family: 'Courier New', Courier, monospace; +} + +/* ── Dark Mode ── */ +body.dark .info-card { + background: linear-gradient(135deg, #1e1e2e, #2d2d3f); + border-color: #7c3aed44; +} + +body.dark .info-card h4 { color: #f9a8d4; } +body.dark .info-card p { color: #aaa; } +body.dark .info-card code { + background: #2d2d3f; + color: #f9a8d4; +} + +body.dark #api-search { + background: #1e1e2e; + color: #f0f0f0; + border-color: #7c3aed55; +} + +body.dark .api-loading { color: #f9a8d4; } +body.dark .api-spinner { border-color: #333; border-top-color: #f9a8d4; } + +body.dark .api-error { + background: #2d1515; + border-color: #7f1d1d; + color: #fca5a5; +} + +body.dark .api-user-card { + background: #1e1e2e; + border-color: #7c3aed44; +} + +body.dark .api-user-name { color: #f9a8d4; } +body.dark .api-user-username { color: #777; } +body.dark .api-user-details { color: #ccc; } +body.dark .api-user-details a { color: #f9a8d4; } + +/* ── Responsive ── */ +@media (max-width: 600px) { + .api-controls { flex-direction: column; align-items: stretch; } + #api-search { flex: unset; width: 100%; } + .user-list { grid-template-columns: 1fr; } +} diff --git a/eis_projekt.html b/eis_projekt.html index 02247a3..7a0c2ad 100644 --- a/eis_projekt.html +++ b/eis_projekt.html @@ -34,6 +34,7 @@ Galerie Notenrechner Textanalyse + API Demo Kontakt Impressum @@ -175,7 +176,8 @@

    © 2026 – Made with πŸ’• & ✨ – David & Karo

    diff --git a/galerie.html b/galerie.html index fc605c9..49014fe 100644 --- a/galerie.html +++ b/galerie.html @@ -34,6 +34,7 @@ Galerie Notenrechner Textanalyse + API Demo Kontakt Impressum @@ -87,7 +88,8 @@

    © 2026 – Made with πŸ’• & ✨ – David & Karo

    diff --git a/impressum.html b/impressum.html index 434543b..b757496 100644 --- a/impressum.html +++ b/impressum.html @@ -34,6 +34,7 @@ Galerie Notenrechner Textanalyse + API Demo Kontakt Impressum @@ -117,7 +118,8 @@

    © 2026 – Made with πŸ’• & ✨ – David & Karo

    diff --git a/index.html b/index.html index 498803f..106353a 100644 --- a/index.html +++ b/index.html @@ -34,6 +34,7 @@ Galerie Notenrechner Textanalyse + API Demo Kontakt Impressum @@ -87,7 +88,8 @@

    © 2026 – Made with πŸ’• & ✨ – David & Karo

    diff --git a/js/api-demo.js b/js/api-demo.js new file mode 100644 index 0000000..861bdd2 --- /dev/null +++ b/js/api-demo.js @@ -0,0 +1,169 @@ +/** + * api-demo.js + * Fetch & API Demo – jsonplaceholder.typicode.com/users + * LΓ€dt Benutzer per fetch(), rendert sie im DOM, mit Fehlerbehandlung & Filterung. + */ + +document.addEventListener('DOMContentLoaded', function () { + // ── DOM-Referenzen ──────────────────────────────────────────────────────── + const userList = document.getElementById('user-list'); + const loadingEl = document.getElementById('api-loading'); + const errorEl = document.getElementById('api-error'); + const errorMsgEl = document.getElementById('api-error-msg'); + const countEl = document.getElementById('user-count'); + const searchInput = document.getElementById('api-search'); + const btnReload = document.getElementById('btn-reload'); + const btnRetry = document.getElementById('btn-retry'); + + // Nur auf der API-Demo-Seite ausfΓΌhren + if (!userList) return; + + // Alle geladenen User merken (fΓΌr Client-seitigen Filter) + let allUsers = []; + + // ── Hilfsfunktionen ─────────────────────────────────────────────────────── + + /** Zeigt den Lade-Spinner */ + function showLoading () { + loadingEl.removeAttribute('hidden'); + errorEl.setAttribute('hidden', ''); + userList.innerHTML = ''; + countEl.textContent = ''; + if (btnReload) btnReload.disabled = true; + } + + /** Versteckt den Lade-Spinner */ + function hideLoading () { + loadingEl.setAttribute('hidden', ''); + if (btnReload) btnReload.disabled = false; + } + + /** Zeigt eine Fehlermeldung an */ + function showApiError (message) { + hideLoading(); + errorMsgEl.textContent = message; + errorEl.removeAttribute('hidden'); + countEl.textContent = ''; + } + + /** + * Erstellt eine User-Karte als
  • -Element + * @param {Object} user – JSON-Objekt aus der API + * @returns {HTMLLIElement} + */ + function createUserCard (user) { + const li = document.createElement('li'); + li.className = 'api-user-card'; + li.innerHTML = ` + + + `; + return li; + } + + /** + * Rendert eine Liste von Usern in den DOM + * @param {Array} users + */ + function renderUsers (users) { + userList.innerHTML = ''; + + if (users.length === 0) { + userList.innerHTML = '
  • Keine Benutzer gefunden. πŸ”
  • '; + countEl.textContent = ''; + return; + } + + // DocumentFragment fΓΌr performantes EinfΓΌgen (ein Reflow statt n) + const fragment = document.createDocumentFragment(); + users.forEach(function (user) { + fragment.appendChild(createUserCard(user)); + }); + userList.appendChild(fragment); + + countEl.textContent = users.length + ' Benutzer geladen βœ…'; + } + + // ── Haupt-Fetch-Funktion ────────────────────────────────────────────────── + + function loadUsers () { + showLoading(); + + // β‘  fetch() aufrufen + fetch('https://jsonplaceholder.typicode.com/users') + + // β‘‘ Antwort prΓΌfen & als JSON parsen + .then(function (response) { + if (!response.ok) { + throw new Error('HTTP-Fehler: ' + response.status + ' ' + response.statusText); + } + return response.json(); + }) + + // β‘’ Daten verarbeiten & im DOM rendern + .then(function (users) { + console.log('βœ… API-Antwort erhalten:', users); + console.table(users.map(function (u) { + return { Name: u.name, 'E-Mail': u.email, Stadt: u.address.city, Firma: u.company.name }; + })); + + allUsers = users; + hideLoading(); + renderUsers(allUsers); + + // Suchfeld zurΓΌcksetzen + if (searchInput) searchInput.value = ''; + }) + + // β‘£ Fehlerbehandlung + .catch(function (err) { + console.error('❌ Fetch-Fehler:', err); + showApiError(err.message || 'Unbekannter Fehler. Bitte Internetverbindung prΓΌfen.'); + }); + } + + // ── Client-seitiger Filter ──────────────────────────────────────────────── + + function filterUsers (query) { + if (!query.trim()) { + renderUsers(allUsers); + return; + } + const q = query.toLowerCase(); + const filtered = allUsers.filter(function (user) { + return ( + user.name.toLowerCase().includes(q) || + user.username.toLowerCase().includes(q) || + user.address.city.toLowerCase().includes(q) || + user.company.name.toLowerCase().includes(q) || + user.email.toLowerCase().includes(q) + ); + }); + renderUsers(filtered); + } + + // ── Event Listener ──────────────────────────────────────────────────────── + + if (btnReload) btnReload.addEventListener('click', loadUsers); + if (btnRetry) btnRetry.addEventListener('click', loadUsers); + + if (searchInput) { + searchInput.addEventListener('input', function () { + filterUsers(this.value); + }); + } + + // ── Beim Seitenaufruf automatisch laden ─────────────────────────────────── + loadUsers(); +}); diff --git a/kontakt.html b/kontakt.html index a2b736f..9e8afea 100644 --- a/kontakt.html +++ b/kontakt.html @@ -127,7 +127,8 @@

    © 2026 – Made with πŸ’• & ✨ – David & Karo

    diff --git a/notenrechner.html b/notenrechner.html index ad094da..75c16ee 100644 --- a/notenrechner.html +++ b/notenrechner.html @@ -34,6 +34,7 @@ Galerie Notenrechner Textanalyse + API Demo Kontakt Impressum diff --git a/team.html b/team.html index 2fb444d..b2cf778 100644 --- a/team.html +++ b/team.html @@ -34,6 +34,7 @@ Galerie Notenrechner Textanalyse + API Demo Kontakt Impressum diff --git a/textanalyse.html b/textanalyse.html index ac2f784..69f14ff 100644 --- a/textanalyse.html +++ b/textanalyse.html @@ -34,6 +34,7 @@ Galerie Notenrechner Textanalyse + API Demo Kontakt Impressum diff --git a/ueber_uns.html b/ueber_uns.html index cd64910..bce70bc 100644 --- a/ueber_uns.html +++ b/ueber_uns.html @@ -34,6 +34,7 @@ Galerie Notenrechner Textanalyse + API Demo Kontakt Impressum @@ -108,7 +109,8 @@

    © 2026 – Made with πŸ’• & ✨ – David & Karo