Software & Praxis

Modernes JavaScript ohne unnötige Bibliotheken

Von einer belastbaren HTML-Ausgangsseite zur getesteten Erweiterung: Wie Sie Browserfunktionen gezielt einsetzen und Abhängigkeiten begründen.

KI-generiertes Symbolmotiv: Ein rotes JS-Modul aktiviert einen Kursfilter auf einer Glas-Webseite mit drei Kursangeboten in der AZ-Herobildwelt.

Für einen kleinen Filter, einen aufklappbaren Hinweis oder eine Berechnung muss nicht zwangsläufig ein umfangreiches Framework geladen werden. Browser bieten bereits viele nützliche Funktionen. Gleichzeitig bedeutet „ohne Bibliothek“ nicht automatisch schnell, sicher oder leicht wartbar. Auch selbst geschriebener Code kann unübersichtlich werden und Fehler enthalten.

Dieser Beitrag entwickelt anhand eines fiktiven Kursverzeichnisses eine kleine Filterfunktion. Die Daten sind Beispiele. Es gibt keine behaupteten Kundenergebnisse oder erfundenen Geschwindigkeitsmessungen. Im Mittelpunkt stehen eine belastbare Ausgangsseite, eine klare Zustandsänderung und eine nachvollziehbare Prüfung. Das Vorgehen ist besonders für begrenzte Ergänzungen einer Unternehmenswebseite geeignet.

1. Den tatsächlichen Funktionsumfang festlegen

Bevor eine technische Entscheidung fällt, wird die Aufgabe präzisiert. Im Kursverzeichnis sollen Besucher zwischen allen Kursen, Präsenzterminen und Onlinekursen wechseln können. Die Kursinformationen stehen bereits im HTML. Nach der Auswahl soll die passende Menge sichtbar bleiben und eine verständliche Anzahl angezeigt werden.

Eine Anmeldung, Zahlung oder serverseitige Suche gehört nicht zu dieser Aufgabe. Diese Grenzen sind wichtig. Wenn später mehrere tausend Einträge, Zugriffsrechte und eine zeitabhängige Verfügbarkeit hinzukommen, kann eine andere Architektur nötig werden. Die kleine Demonstration ist kein fertiges Buchungssystem.

Formulieren Sie die Anforderungen als beobachtbare Ergebnisse. „Nach Auswahl von Online werden nur Onlinekurse angezeigt“ ist überprüfbar. „Modernes JavaScript verwenden“ beschreibt dagegen hauptsächlich eine Umsetzungsvorstellung. Ebenso wichtig sind Randfälle: Was geschieht ohne JavaScript? Wie funktioniert die Auswahl mit Tastatur? Gibt es einen verständlichen Zustand ohne Treffer?

Für dieses Beispiel lautet die Rückfallebene: Ohne Skript bleiben alle Kurse sichtbar. Der Filter wird erst nach erfolgreicher Initialisierung eingeblendet. Dadurch verspricht die Oberfläche keine Funktion, die bei einem Ladefehler fehlt. Diese Entscheidung eignet sich für ein kleines öffentliches Verzeichnis, aber nicht automatisch für Anwendungen mit verpflichtenden Berechtigungsprüfungen.

2. Browserfunktionen nach Aufgabe auswählen

Die grundlegenden Werkzeuge sind einfach: Ein Element wird gefunden, ein Ereignis löst die Aktualisierung aus und ein vorhersehbarer Zustand bestimmt die Darstellung. Die MDN-Referenz zu querySelector erläutert die Auswahl des ersten passenden Elements. Sie liefert keine Garantie, dass dieses Element auf jeder Seite tatsächlich vorhanden ist. Diesen Fall muss die Initialisierung berücksichtigen.

AufgabePassender AusgangspunktWichtige Prüfung
Komponente findenquerySelector innerhalb eines eindeutigen BereichsFehlendes Element behandeln
Auswahl auswertenchange-Ereignis eines AuswahlfeldsTastatur und Touch prüfen
Einträge ausblendenhidden-EigenschaftRelevante Inhalte bleiben verfügbar
Anzahl schreibentextContentKein Inhalt als HTML interpretieren
Code abgrenzenES-Modul oder InitialisierungsfunktionKeine unnötigen globalen Variablen

Ein natives Auswahlfeld bringt bereits grundlegende Bedienung mit. Seine sichtbare Beschriftung und ein gut gestalteter Fokus bleiben trotzdem notwendig. Ein selbst gebautes Auswahlmenü würde zusätzliche Zustände und Prüfaufgaben erzeugen. Für die fachliche Aufgabe des Beispiels wäre das ohne weiteren Bedarf unnötiger Aufwand.

Die Wahl einer Browserfunktion reduziert nicht jede Verantwortung. Das Ausblenden eines Eintrags verändert beispielsweise seine Erreichbarkeit. Wenn sich der Fokus innerhalb eines gerade ausgeblendeten Bereichs befindet, ist ein bewusstes Verhalten nötig. Unsere Auswahl bleibt außerhalb der Kurskarten, sodass die Filteränderung keinen fokussierten Kursbutton verschwinden lässt.

3. Mit funktionierendem HTML beginnen

Das folgende HTML enthält die vollständigen Ausgangsinformationen. Im Beispiel werden nur drei Kurse verwendet. Der Komponentenbereich ist über ein Datenattribut erkennbar. Die Auswahl ist anfangs verborgen, die Inhalte selbst sind sichtbar. Der Text der Ergebnisanzeige gehört zum Dokument und wird später aktualisiert.

<section data-course-filter aria-labelledby="kurse">
  <h2 id="kurse">Kurse im Überblick</h2>
  <div data-filter-controls hidden>
    <label for="kursart">Kursart</label>
    <select id="kursart" data-filter-select>
      <option value="all">Alle Kurse</option>
      <option value="online">Online</option>
      <option value="onsite">Präsenz</option>
    </select>
  </div>
  <p data-filter-count role="status">3 Kurse verfügbar</p>
  <ul>
    <li data-course-kind="online">Einführung in die Bildbearbeitung</li>
    <li data-course-kind="onsite">Fotografie vor Ort</li>
    <li data-course-kind="online">Grundlagen der Textarbeit</li>
  </ul>
</section>

Die fachlichen Werte online und onsite sind bewusst unabhängig von den sichtbaren Beschriftungen. Eine spätere Übersetzung kann die Labels ändern, ohne die Vergleichslogik umzubauen. Die Anzahl im Ausgangstext muss mit den tatsächlich ausgelieferten Einträgen übereinstimmen. In einer dynamischen Anwendung wird sie aus derselben Datenquelle erzeugt.

Die ID des Auswahlfelds muss im Dokument eindeutig bleiben. Wird die Komponente mehrfach auf derselben Seite eingebunden, braucht jede Instanz eine eigene ID und passende Beschriftung. Datenattribute erlauben die lokale Zuordnung des Skripts, ersetzen aber keine korrekte Formularbeschriftung.

Zwei tatsächliche Browseransichten mit identischem Maßstab: drei lesbare Kursangebote vor der Initialisierung und zwei Online-Kurse nach der Filterauswahl.
Der unveränderte Beispielcode wird im Browser ausgeführt. Die zwei Ansichten verwenden dieselben drei Kursangebote. Das Ausblenden eines Angebots im Browser stellt keine serverseitige Zugriffsprüfung dar.

4. Eine kleine, abgegrenzte Initialisierung schreiben

Die Funktion erhält den Komponentenbereich als Argument. Sie sucht ihre Elemente nur darin. Fehlt ein notwendiger Bestandteil, wird die Erweiterung nicht aktiviert. Die Kurse bleiben sichtbar. Die Anzeige verwendet Textausgabe und interpretiert ihre Zeichenfolge nicht als HTML.

export function initCourseFilter(root) {
  const controls = root.querySelector('[data-filter-controls]');
  const select = root.querySelector('[data-filter-select]');
  const count = root.querySelector('[data-filter-count]');
  const items = [...root.querySelectorAll('[data-course-kind]')];

  if (!controls || !select || !count || items.length === 0) return;
  if (root.dataset.filterReady === 'true') return;

  function update() {
    const wanted = select.value;
    let visible = 0;
    for (const item of items) {
      const matches = wanted === 'all' || item.dataset.courseKind === wanted;
      item.hidden = !matches;
      if (matches) visible += 1;
    }
    count.textContent = visible === 1
      ? '1 Kurs verfügbar'
      : `${visible} Kurse verfügbar`;
  }

  select.addEventListener('change', update);
  update();
  root.dataset.filterReady = 'true';
  controls.hidden = false;
}

document.querySelectorAll('[data-course-filter]').forEach(initCourseFilter);

Die einfache Markierung verhindert eine doppelte Initialisierung desselben vorhandenen Elements. Sie ist für diese statische Demonstration ausreichend. Ein komplexer Lebenszyklus mit ausgetauschten DOM-Bereichen benötigt zusätzlich eine klare Entscheidung über Abbau und erneute Initialisierung. Ein bereits gesetztes Datenattribut sollte dort nicht unüberlegt als vollständiges Komponentenmodell betrachtet werden.

Die Datei wird als Modul geladen, beispielsweise mit <script type="module" src="/assets/course-filter.js"></script>. Die MDN-Anleitung zu JavaScript-Modulen beschreibt die entsprechenden Grundlagen. Für einen realen Test wird die Seite über einen HTTP-Server geöffnet, damit der Modulaufruf unter den vorgesehenen Auslieferungsbedingungen geprüft wird.

5. Datenverarbeitung und Darstellung auseinanderhalten

Im kleinen Beispiel ist die Schleife übersichtlich. Wenn die fachlichen Regeln wachsen, lohnt sich eine Trennung. Eine reine Funktion kann entscheiden, welche Daten passen. Eine andere Funktion setzt die sichtbare Ausgabe um. Dadurch lässt sich eine Regel mit einfachen Eingaben prüfen, ohne jedes Mal eine Browseroberfläche aufzubauen.

export function matchesKind(course, wanted) {
  return wanted === 'all' || course.kind === wanted;
}

const courses = [
  { id: 'intro', kind: 'online' },
  { id: 'photo', kind: 'onsite' }
];
const onlineCourses = courses.filter(course => matchesKind(course, 'online'));

Die Array-Referenz zu filter erläutert das Erstellen einer gefilterten Ergebnisliste. Im Projekt bleibt zu entscheiden, welche Eingabeformen gültig sind und wie unbekannte Werte behandelt werden. Eine öffentliche Schnittstelle benötigt andere Validierung als ein fester, vollständig kontrollierter Datensatz.

Eine solche Trennung ist kein Anlass, jede einzelne Zeile in ein eigenes Modul zu verschieben. Die Grenzen sollten fachliche Zusammenhänge ausdrücken. Eine Datei für Filterregeln und eine für die konkrete Komponente können sinnvoll sein. Zehn winzige Dateien ohne eigenständige Verantwortung erhöhen dagegen den Suchaufwand.

6. Text sicher ausgeben und Eingaben prüfen

Die Ergebnisanzahl wird über textContent geschrieben. Die MDN-Dokumentation zu textContent unterscheidet Textausgabe von HTML-Verarbeitung. Für Zeichenfolgen aus Eingaben oder Schnittstellen ist diese Unterscheidung besonders wichtig. Ein Kursname soll als Text erscheinen und nicht als ausführbare Auszeichnung behandelt werden.

Das macht die gesamte Anwendung noch nicht sicher. Berechtigungen, Datenbankzugriffe und serverseitige Verarbeitung bleiben eigenständige Aufgaben. Ein im Browser versteckter Kurs ist weiterhin im ausgelieferten HTML enthalten. Der Filter ist eine Orientierungshilfe und keine Zugriffskontrolle.

Ebenso kann eine Person das Auswahlfeld oder das Skript verändern. Ein Server darf daraus keine ungeprüfte Berechtigung oder Preisentscheidung ableiten. Werden Daten über eine Schnittstelle gesendet, muss die empfangende Anwendung Werte und Rechte erneut prüfen. Die Oberfläche erleichtert Eingaben, sie bildet keine vertrauenswürdige Sicherheitsgrenze.

Für Entwickler ist eine kleine Übersicht hilfreich: Welche Werte entstehen im eigenen Quellcode? Welche stammen von Besuchern? Welche liefert ein externer Dienst? Jede Übergabe erhält eine passende Prüfung. Das verhindert, dass eine zunächst harmlose Filterkomponente später unbemerkt Aufgaben übernimmt, für die ihre Annahmen nicht mehr gelten.

7. Die Entscheidung für eine Abhängigkeit begründen

Es gibt Aufgaben, bei denen eine gepflegte Bibliothek sehr sinnvoll ist. Dazu können komplexe Editoren, umfangreiche internationale Datumsverarbeitung oder anspruchsvolle Diagramme gehören. Selbstentwicklung bedeutet dort nicht automatisch geringeren Aufwand. Entscheidend ist, ob das Paket die konkrete Aufgabe gut und nachvollziehbar abdeckt.

KriteriumFragePrüfbeleg
FunktionWelche Anforderung wird gelöst?Konkreter Funktionsvergleich
AuslieferungWas wird tatsächlich geladen?Netzwerkansicht der realen Seite
PflegeWer verfolgt Updates und Änderungen?Benannte Zuständigkeit
LizenzPasst die Nutzung zum Projekt?Geprüfte Lizenzunterlagen
AusstiegWie lässt sich die Funktion ersetzen?Dokumentierte Schnittstelle und Datenformat

Eine vorhandene Abhängigkeit sollte nicht allein wegen einer allgemeinen Vorliebe entfernt werden. Prüfen Sie ihren tatsächlichen Einsatz. Ein Austausch kann neue Fehler und Schulungsaufwand erzeugen. Umgekehrt sollte ein bereits installiertes Paket nicht automatisch jede neue Kleinigkeit übernehmen, wenn dadurch eine unnötig große Menge Code in zusätzliche Seiten gelangt.

8. Mit realistischen Zuständen testen

Für den Kursfilter reichen wenige gezielte Fälle als Anfang. Alle Einträge müssen sichtbar sein, wenn „Alle Kurse“ gewählt ist. Online zeigt zwei und Präsenz einen Eintrag. Eine Variante ohne passenden Eintrag zeigt null. Ein fehlender Komponentenbestandteil verhindert die Aktivierung, ohne den Rest der Seite zu beschädigen.

Hinzu kommen Tastatur und wiederholte Nutzung. Wechseln Sie mehrfach zwischen den Auswahlwerten. Prüfen Sie, ob die angezeigte Zahl zur sichtbaren Menge passt. Testen Sie eine zweite unabhängige Komponente auf derselben Seite. Deren Zustände dürfen sich nicht gegenseitig verändern.

Ein aussagekräftiger Test beschreibt den beobachtbaren Unterschied. Er prüft beispielsweise die sichtbare Menge nach der Auswahl. Ein Test, der nur kontrolliert, ob eine interne Hilfsfunktion aufgerufen wurde, kann trotz falscher Darstellung erfolgreich sein. Die fachliche Aufgabe bleibt das entscheidende Prüfkriterium.

Prüfen Sie außerdem eine Seite ohne Skriptausführung. Die Kursinformationen müssen weiterhin lesbar bleiben. Ein fehlender Filter ist dann eine bewusst gewählte Einschränkung. Falls die Anwendung diese Rückfallebene nicht zulässt, muss das Architekturkonzept entsprechend angepasst werden. Eine rein dekorative Fehlermeldung löst das nicht.

9. Leistung am wirklichen Engpass untersuchen

Eine kleine Quelldatei ist noch kein Leistungsnachweis. Langsame Bilder, wiederholte DOM-Arbeit oder große Datenmengen können die Wirkung eines schlanken Skripts überlagern. Untersuchen Sie zuerst die konkrete Seite. Welche Interaktion dauert zu lange? Welche Arbeit wird dabei tatsächlich ausgelöst?

Bei drei Kursen ist eine einfache Schleife angemessen. Bei zehntausenden Einträgen stellt sich eine andere Aufgabe. Dort können serverseitige Suche, begrenzte Ergebnismengen oder andere Darstellungsverfahren nötig sein. Eine komplizierte Mikrooptimierung der kleinen Demonstration wäre dafür keine ausreichend begründete Lösung.

Messungen werden mit Seite, Datum, Gerät und Bedingungen dokumentiert. Eine Aussage wie „ohne Bibliothek ist die Seite fünfmal schneller“ wäre ohne passenden Vergleich nicht belastbar. Besser ist eine konkrete Beobachtung: Eine bestimmte Datei wird nicht mehr geladen oder eine nachgewiesene wiederholte Berechnung wurde entfernt.

10. Änderungen am Datenbestand bewusst behandeln

Die Demonstration liest die Kursliste genau einmal während der Initialisierung. Das ist eine wichtige Annahme. Wird später ein weiterer Eintrag per Skript eingefügt, gehört er nicht automatisch zur gespeicherten Liste. Das sichtbare HTML und die interne Sammlung würden auseinanderlaufen. Die Lösung besteht nicht darin, diese Änderung zufällig durch weitere Ereignisse zu überdecken, sondern den Datenfluss ausdrücklich festzulegen.

Für einen statischen Auftritt kann die Seite nach einer Inhaltsänderung einfach neu ausgeliefert werden. Für eine Anwendung mit dynamischen Ergebnissen ist dagegen eine eigene Aktualisierungsfunktion sinnvoll. Sie erhält den neuen Datenbestand und baut Darstellung sowie Zählung in einem geordneten Schritt auf. Dabei wird entschieden, ob die bisherige Auswahl erhalten bleibt und wie ein nicht mehr verfügbarer Wert behandelt wird.

Auch die Reihenfolge ist eine fachliche Entscheidung. Ein Filter soll normalerweise die vorhandene Sortierung beibehalten. Wenn stattdessen Relevanz, Termin oder Verfügbarkeit die Reihenfolge ändern, muss diese Regel nachvollziehbar sein. Die sichtbare Zahl kann sonst korrekt sein, während Besucher eine unerwartete Liste erhalten. Ein gutes Review betrachtet deshalb nicht nur die Menge der Ergebnisse.

Eine eigene Zustandsstruktur lohnt sich, sobald mehrere Einstellungen zusammenwirken. Beispielsweise könnten Kursart und Suchbegriff gemeinsam gefiltert werden. Speichern Sie dann beide Werte an einer eindeutigen Stelle. Wenn jeder Ereignishandler heimlich Teile der Oberfläche als Datenquelle verwendet, entstehen schwer nachvollziehbare Abhängigkeiten. Der Zustand sollte die Darstellung bestimmen und nicht aus zufälligen Darstellungsdetails erraten werden.

11. Fehler sichtbar eingrenzen, ohne den Ablauf zu verschleiern

Ein früher Abbruch bei fehlenden Elementen ist für unsere Erweiterung ein angemessener Rückfall. Während der Entwicklung muss die Ursache trotzdem auffindbar bleiben. Prüfen Sie in den Browserwerkzeugen, ob die Datei geladen wird, ob ihre Antwort den richtigen Inhalt besitzt und ob eine Fehlermeldung vor der Initialisierung auftritt. Das bloße Fehlen eines roten Fehlersymbols beweist keine funktionierende Komponente.

Unterscheiden Sie zwischen erwarteten und unerwarteten Zuständen. Eine Seite ohne Kursverzeichnis muss das Filtermodul nicht initialisieren. Eine Seite mit einem als vollständig deklarierten Komponentenbereich, in dem die Auswahl versehentlich fehlt, enthält dagegen einen Integrationsfehler. In einer größeren Anwendung kann diese Unterscheidung in Entwicklungsprotokollen sichtbar gemacht werden. Öffentlich sichtbare interne Diagnosemeldungen sind dafür nicht nötig.

Verändern Sie bei der Fehlersuche jeweils eine konkrete Annahme. Ist der Selektor falsch oder wurde das Element später eingefügt? Kommt das Ereignis an oder scheitert erst die Aktualisierung? Eine kleine überprüfbare Eingabe liefert meist mehr Erkenntnis als eine umfassende Neufassung. Bewahren Sie ein reproduzierbares Beispiel auf, damit die Korrektur später erneut geprüft werden kann.

Nach der Korrektur gehören die betroffenen Randfälle in die Prüfung. Ein fehlender Eintrag, eine zweite Instanz oder ein unerwarteter Wert muss dann bewusst behandelt werden. Der Aufwand sollte zur Bedeutung der Funktion passen. Für eine optionale Ansichtserleichterung ist ein sinnvoller Rückfall ausreichend. Für eine verbindliche Geschäftsaktion braucht es dagegen eine entsprechend strengere Absicherung des gesamten Ablaufs.

Damit wird die Frage nach Bibliotheken konkreter: Sie wählen ein Werkzeug für eine klar definierte Verantwortung. Ob der Quellcode aus eigener Entwicklung oder einem gepflegten Paket stammt, bleibt eine nachgeordnete Entscheidung. Die Verantwortung für das angebotene Verhalten, seine Grenzen und seine Prüfung bleibt im Projekt.

12. Den kleinen Vertrag der Komponente dokumentieren

Ein wartbarer Baustein braucht auch dann einen verständlichen Vertrag, wenn sein Quellcode nur wenige Zeilen umfasst. Für den Kursfilter gehören dazu der erwartete Komponentenbereich, die drei erforderlichen Bedienelemente und die fachlichen Werte der Kategorien. Dokumentieren Sie außerdem, dass die Einträge während der Lebensdauer dieser einfachen Variante nicht nachträglich ausgetauscht werden.

Ein späterer Entwickler muss dadurch nicht aus dem gesamten Seitenquelltext rekonstruieren, warum ein bestimmtes Datenattribut vorhanden ist. Die Dokumentation kann direkt neben dem Modul stehen und ein kleines gültiges HTML-Beispiel enthalten. Ebenso hilfreich ist eine kurze Liste der bewusst nicht übernommenen Aufgaben: keine Anmeldung, keine Zugriffskontrolle und keine serverseitige Datenabfrage.

Die Grenze erleichtert spätere Erweiterungen. Soll eine zusätzliche Suchfunktion entstehen, kann das Team entscheiden, ob sie in denselben Zustand gehört oder eine neue fachliche Komponente benötigt. Diese Entscheidung wird sichtbar getroffen. Andernfalls sammeln sich häufig kleine Sonderregeln an, bis ein ursprünglich einfacher Filter nur noch zufällig nachvollziehbar ist.

Bei der Übergabe gehören die geprüften Beispiele ebenfalls dazu. Halten Sie fest, wie die erfolgreiche Auswahl, ein Nulltreffer und fehlende Elemente untersucht wurden. So bleibt der Nutzen einer schlanken Lösung erhalten: Der nächste Bearbeiter kann Verhalten und Grenzen schnell verstehen und eine Änderung gezielt absichern.

13. Checkliste vor der Veröffentlichung

  • Die Funktion und ihre fachlichen Grenzen sind klar beschrieben.
  • Die Ausgangsseite enthält die wichtigen Informationen.
  • Bedienelemente werden erst bei funktionsfähiger Erweiterung angeboten.
  • Jede Komponente arbeitet in ihrem eigenen Bereich.
  • Doppelte Initialisierung und dynamische Lebenszyklen sind bedacht.
  • Texte werden passend ausgegeben und Eingaben an den nötigen Grenzen geprüft.
  • Clientseitige Darstellung wird nicht mit Zugriffsschutz verwechselt.
  • Tastatur, Touch, Nulltreffer und wiederholte Nutzung sind geprüft.
  • Abhängigkeiten haben eine konkrete Begründung und verantwortliche Pflege.
  • Leistungsbehauptungen beruhen auf dokumentierten Messungen.

Häufige Fragen

Soll jede Bibliothek entfernt werden?

Nein. Eine geeignete Bibliothek kann Entwicklung und Pflege erleichtern. Die Entscheidung folgt der konkreten Aufgabe und den tatsächlichen Folgekosten.

Ist Vanilla JavaScript eine eigene Sprache?

Nein. Der Ausdruck bezeichnet gewöhnlich JavaScript ohne zusätzliches Framework. Browserfunktionen und Sprachstandard bleiben dieselben Grundlagen.

Ist TypeScript für kleine Komponenten erforderlich?

Das hängt vom Projekt ab. Bestehende Werkzeuge und Teamregeln können dafür sprechen. Für das nachvollziehbare Lernbeispiel ist keine zusätzliche Übersetzungsschicht nötig.

Quellen und passende Vertiefung

Grundlagen sind die verlinkten MDN-Referenzen zu Modulen, Elementauswahl, Filterung und Textausgabe. Quellenprüfung am 9. November 2026. Kursdaten, Code und Entscheidungsmatrix wurden als eigene Lernbeispiele entwickelt.

Die passende HTML-Grundlage erläutert Semantisches HTML anhand einer vollständigen Beispielseite. Den größeren Planungsrahmen beschreibt Was eine professionelle Unternehmenswebseite leisten muss.

Gemeinsam weiterdenken

Was möchten Sie bewegen?

Eine Idee, eine konkrete Aufgabe oder der nächste Schritt mit KI. Wir hören zu.

Sprechen wir darüber